Skip to content

Commit 2ceb596

Browse files
authored
Return exit code 2 on invalid or not found subject (#106)
1 parent 232f827 commit 2ceb596

File tree

2 files changed

+97
-11
lines changed

2 files changed

+97
-11
lines changed

evidence/create/create_custom.go

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import (
1111
"github.com/jfrog/jfrog-cli-artifactory/evidence"
1212
"github.com/jfrog/jfrog-cli-artifactory/evidence/sigstore"
1313
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
14+
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
1415
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1516
)
1617

1718
type createEvidenceCustom struct {
1819
createEvidenceBase
19-
subjectRepoPath string
20-
subjectSha256 string
21-
sigstoreBundlePath string
20+
subjectRepoPath string
21+
subjectSha256 string
22+
sigstoreBundlePath string
23+
autoSubjectResolution bool
2224
}
2325

2426
func NewCreateEvidenceCustom(serverDetails *config.ServerDetails, predicateFilePath, predicateType, markdownFilePath, key, keyId, subjectRepoPath,
@@ -63,13 +65,13 @@ func (c *createEvidenceCustom) Run() error {
6365
return err
6466
}
6567

66-
err = validateSubject(c.subjectRepoPath)
68+
err = c.validateSubject()
6769
if err != nil {
6870
return err
6971
}
7072
err = c.uploadEvidence(evidencePayload, c.subjectRepoPath)
7173
if err != nil {
72-
err = handleSubjectNotFound(err, c.subjectRepoPath)
74+
err = c.handleSubjectNotFound(err)
7375
return err
7476
}
7577

@@ -83,6 +85,7 @@ func (c *createEvidenceCustom) processSigstoreBundle() ([]byte, error) {
8385
}
8486

8587
if c.subjectRepoPath == "" {
88+
c.autoSubjectResolution = true
8689
extractedSubject, err := c.extractSubjectFromBundle(sigstoreBundle)
8790
if err != nil {
8891
return nil, err
@@ -100,7 +103,7 @@ func (c *createEvidenceCustom) extractSubjectFromBundle(bundle *bundle.Bundle) (
100103
}
101104

102105
if subject == "" {
103-
return "", errorutils.CheckErrorf("Subject is not found in the sigstore bundle. Please ensure the bundle contains a valid subject.")
106+
return "", c.newSubjectError("Subject is not found in the sigstore bundle. Please ensure the bundle contains a valid subject.")
104107
} else {
105108
clientLog.Info("Subject " + subject + " is resolved from sigstore bundle.")
106109
}
@@ -117,19 +120,32 @@ func (c *createEvidenceCustom) createDSSEEnvelope() ([]byte, error) {
117120
return envelope, nil
118121
}
119122

120-
func validateSubject(subject string) error {
123+
func (c *createEvidenceCustom) validateSubject() error {
121124
// Pattern: must have at least one slash with non-empty sections
122-
if matched, _ := regexp.MatchString(`^[^/]+(/[^/]+)+$`, subject); !matched {
123-
return errorutils.CheckErrorf("Subject '%s' is invalid. Subject must be in format: <repo>/<path>/<name> or <repo>/<name>", subject)
125+
if matched, _ := regexp.MatchString(`^[^/]+(/[^/]+)+$`, c.subjectRepoPath); !matched {
126+
return c.newSubjectError("Subject '" + c.subjectRepoPath + "' is invalid. Subject must be in format: <repo>/<path>/<name> or <repo>/<name>")
124127
}
125128
return nil
126129
}
127130

128-
func handleSubjectNotFound(err error, subject string) error {
131+
func (c *createEvidenceCustom) handleSubjectNotFound(err error) error {
129132
errStr := err.Error()
130133
if strings.Contains(errStr, "404 Not Found") {
131134
clientLog.Debug("Server response error:", err.Error())
132-
return errorutils.CheckErrorf("Subject '%s' is not found. Please ensure the subject exists.", subject)
135+
return c.newSubjectError("Subject '" + c.subjectRepoPath + "' is not found. Please ensure the subject exists.")
133136
}
134137
return err
135138
}
139+
140+
// newSubjectError creates an error with ExitCodeFailNoOp (2) for subject-related failures
141+
// When auto subject resolution is enabled, this allows pipeline calls with gh attestation
142+
// sigstore bundle generation to skip command execution without breaking a pipeline
143+
func (c *createEvidenceCustom) newSubjectError(message string) error {
144+
if c.autoSubjectResolution {
145+
return coreutils.CliError{
146+
ExitCode: coreutils.ExitCodeFailNoOp,
147+
ErrorMsg: message,
148+
}
149+
}
150+
return errorutils.CheckErrorf(message)
151+
}

evidence/create/create_custom_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
11+
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
1112
"github.com/stretchr/testify/assert"
1213
)
1314

@@ -212,3 +213,72 @@ func TestCreateEvidenceCustom_SigstoreBundleWithSubjectPath(t *testing.T) {
212213
assert.Equal(t, bundlePath, custom.sigstoreBundlePath)
213214
assert.Equal(t, "provided-repo/provided-artifact", custom.subjectRepoPath)
214215
}
216+
217+
func TestCreateEvidenceCustom_NewSubjectError_AutoSubjectResolution(t *testing.T) {
218+
serverDetails := &config.ServerDetails{
219+
Url: "https://test.jfrog.io",
220+
User: "test-user",
221+
AccessToken: "test-token",
222+
}
223+
224+
cmd := NewCreateEvidenceCustom(
225+
serverDetails,
226+
"predicate.json",
227+
"https://example.com/predicate/v1",
228+
"markdown.md",
229+
"key.pem",
230+
"key-alias",
231+
"",
232+
"abcd1234",
233+
"/path/to/sigstore-bundle.json",
234+
"test-provider",
235+
)
236+
237+
custom, ok := cmd.(*createEvidenceCustom)
238+
assert.True(t, ok, "cmd should be of type *createEvidenceCustom")
239+
240+
custom.autoSubjectResolution = true
241+
242+
testMessage := "Test error message"
243+
err := custom.newSubjectError(testMessage)
244+
245+
assert.Error(t, err)
246+
cliErr, ok := err.(coreutils.CliError)
247+
assert.True(t, ok, "error should be of type CliError when autoSubjectResolution is enabled")
248+
assert.Equal(t, coreutils.ExitCodeFailNoOp, cliErr.ExitCode, "should return exit code 2 (ExitCodeFailNoOp)")
249+
assert.Equal(t, testMessage, cliErr.ErrorMsg, "error message should match")
250+
}
251+
252+
func TestCreateEvidenceCustom_NewSubjectError_RegularExecution(t *testing.T) {
253+
serverDetails := &config.ServerDetails{
254+
Url: "https://test.jfrog.io",
255+
User: "test-user",
256+
AccessToken: "test-token",
257+
}
258+
259+
cmd := NewCreateEvidenceCustom(
260+
serverDetails,
261+
"predicate.json",
262+
"https://example.com/predicate/v1",
263+
"markdown.md",
264+
"key.pem",
265+
"key-alias",
266+
"test-repo/test-artifact",
267+
"abcd1234",
268+
"",
269+
"test-provider",
270+
)
271+
272+
custom, ok := cmd.(*createEvidenceCustom)
273+
assert.True(t, ok, "cmd should be of type *createEvidenceCustom")
274+
275+
custom.autoSubjectResolution = false
276+
277+
testMessage := "Test error message"
278+
err := custom.newSubjectError(testMessage)
279+
280+
assert.Error(t, err)
281+
_, ok = err.(coreutils.CliError)
282+
assert.False(t, ok, "error should not be of type CliError when autoSubjectResolution is disabled")
283+
assert.Contains(t, err.Error(), testMessage, "error message should contain the test message")
284+
}

0 commit comments

Comments
 (0)