diff --git a/README.md b/README.md index 570d92ef5..8d80c77ed 100644 --- a/README.md +++ b/README.md @@ -156,13 +156,13 @@ Below is a list of options currently supported for binary blobs and container im ```bash $ git clone git@github.com:slsa-framework/slsa-verifier.git $ go run ./cli/slsa-verifier/ verify-artifact --help -Verifies SLSA provenance on an artifact blob +Verifies SLSA provenance on artifact blobs given as arguments (assuming same provenance) Usage: - slsa-verifier verify-artifact [flags] + slsa-verifier verify-artifact [flags] artifact [artifact..] Flags: - --build-workflow-input map[] [optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events). (default map[]) + --build-workflow-input map[] [optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events on GitHub Actions). (default map[]) --builder-id string [optional] the unique builder ID who created the provenance -h, --help help for verify-artifact --print-provenance [optional] print the verified provenance to stdout @@ -173,6 +173,8 @@ Flags: --source-versioned-tag string [optional] expected version the binary was compiled from. Uses semantic version to match the tag ``` +Multiple artifacts can be passed to `verify-artifact`. As long as they are all covered by the same provenance file, the verification will succeed. + ### Option details The following options are available: @@ -205,6 +207,27 @@ The verified in-toto statement may be written to stdout with the `--print-proven Only GitHub URIs are supported with the `--source-uri` flag. A tag should not be specified, even if the provenance was built at some tag. If you intend to do source versioning validation, use `--print-provenance` and inspect the commit SHA of the config source or materials. +Multiple artifacts built from the same GitHub builder can be verified in the same command, by passing them in the same command line as arguments: + +```bash +$ slsa-verifier verify-artifact \ + --provenance-path /tmp/demo/multiple.intoto.jsonl \ + --source-uri github.com/mihaimaruseac/example \ + /tmp/demo/fib /tmp/demo/hello + +Verified signature against tlog entry index 9712459 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77a1544828b67bb5a2335f7e0d01c504a32ceb6f3a8814ed12c8f1b222d308bd9e8 +Verified build using builder https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.4.0 at commit 11fab87c5ee6f46c6f5e68f6c5378c62ce1ca77c +Verifying artifact /tmp/demo/fib: PASSED + +Verified signature against tlog entry index 9712459 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77a1544828b67bb5a2335f7e0d01c504a32ceb6f3a8814ed12c8f1b222d308bd9e8 +Verified build using builder https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.4.0 at commit 11fab87c5ee6f46c6f5e68f6c5378c62ce1ca77c +Verifying artifact /tmp/demo/hello: PASSED + +PASSED: Verified SLSA provenance +``` + +The only requirement is that the provenance file covers all artifacts passed as arguments in the command line (that is, they are a subset of `subject` field in the provenance file). + ### Containers This is WIP and currently not supported. diff --git a/cli/slsa-verifier/main_regression_test.go b/cli/slsa-verifier/main_regression_test.go index f9733af4e..de016ed99 100644 --- a/cli/slsa-verifier/main_regression_test.go +++ b/cli/slsa-verifier/main_regression_test.go @@ -78,7 +78,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { tests := []struct { name string - artifact string + artifacts []string source string pbranch *string ptag *string @@ -96,147 +96,151 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { minversion string // specifying builders will restrict builders to only the specified ones. builders []string + // specify provenance path if not the same as artifacts[0] + // useful for testing provenance with multiple artifacts, + // without needing to duplicate provenance + provenancePath string }{ { - name: "valid main branch default", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/slsa-framework/example-package", + name: "valid main branch default", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/slsa-framework/example-package", }, { name: "valid main branch default - invalid builderID", - artifact: "binary-linux-amd64-workflow_dispatch", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, source: "github.com/slsa-framework/example-package", pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/not-trusted.yml"), err: serrors.ErrorUntrustedReusableWorkflow, }, { - name: "valid main branch set", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/slsa-framework/example-package", - pbranch: pString("main"), + name: "valid main branch set", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/slsa-framework/example-package", + pbranch: pString("main"), }, { - name: "wrong branch master", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/slsa-framework/example-package", - pbranch: pString("master"), - err: serrors.ErrorMismatchBranch, + name: "wrong branch master", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/slsa-framework/example-package", + pbranch: pString("master"), + err: serrors.ErrorMismatchBranch, }, { - name: "branch master not verified", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/slsa-framework/example-package", + name: "branch master not verified", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/slsa-framework/example-package", }, { - name: "wrong source append A", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/laurentsimon/slsa-verifier-test-genA", - err: serrors.ErrorMismatchSource, + name: "wrong source append A", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/laurentsimon/slsa-verifier-test-genA", + err: serrors.ErrorMismatchSource, }, { - name: "wrong source prepend A", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/laurentsimon/slsa-verifier-test-gen", - err: serrors.ErrorMismatchSource, + name: "wrong source prepend A", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/laurentsimon/slsa-verifier-test-gen", + err: serrors.ErrorMismatchSource, }, { - name: "wrong source middle A", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/Alaurentsimon/slsa-verifier-test-gen", - err: serrors.ErrorMismatchSource, + name: "wrong source middle A", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/Alaurentsimon/slsa-verifier-test-gen", + err: serrors.ErrorMismatchSource, }, { - name: "tag no match empty tag workflow_dispatch", - artifact: "binary-linux-amd64-workflow_dispatch", - source: "github.com/slsa-framework/example-package", - ptag: pString("v1.2.3"), - err: serrors.ErrorMismatchTag, + name: "tag no match empty tag workflow_dispatch", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, + source: "github.com/slsa-framework/example-package", + ptag: pString("v1.2.3"), + err: serrors.ErrorMismatchTag, }, { name: "versioned tag no match empty tag workflow_dispatch", - artifact: "binary-linux-amd64-workflow_dispatch", + artifacts: []string{"binary-linux-amd64-workflow_dispatch"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v1"), err: serrors.ErrorInvalidSemver, }, // Provenance contains tag = v13.0.30. { - name: "tag v13.0.29 no match v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", - source: "github.com/slsa-framework/example-package", - ptag: pString("v13.0.29"), - err: serrors.ErrorMismatchTag, + name: "tag v13.0.29 no match v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, + source: "github.com/slsa-framework/example-package", + ptag: pString("v13.0.29"), + err: serrors.ErrorMismatchTag, }, { - name: "tag v13.0 no match v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", - source: "github.com/slsa-framework/example-package", - ptag: pString("v13.0"), - err: serrors.ErrorMismatchTag, + name: "tag v13.0 no match v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, + source: "github.com/slsa-framework/example-package", + ptag: pString("v13.0"), + err: serrors.ErrorMismatchTag, }, { - name: "tag v13 no match v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", - source: "github.com/slsa-framework/example-package", - ptag: pString("v13"), - err: serrors.ErrorMismatchTag, + name: "tag v13 no match v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, + source: "github.com/slsa-framework/example-package", + ptag: pString("v13"), + err: serrors.ErrorMismatchTag, }, { name: "versioned v13.0.30 match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.0.30"), }, { name: "versioned v13.0 match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.0"), }, { name: "versioned v13 match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13"), }, { name: "versioned v2 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v2"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v0 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v0"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13.1 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v12.9 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v12.9"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13.0.29 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.0.29"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13.0.31 no match push-v13.0.30", - artifact: "binary-linux-amd64-push-v13.0.30", + artifacts: []string{"binary-linux-amd64-push-v13.0.30"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.0.31"), err: serrors.ErrorMismatchVersionedTag, @@ -244,54 +248,54 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Provenance contains tag = v14. { name: "versioned v14 match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14"), }, { name: "versioned v14.0 match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.0"), }, { name: "versioned v14.1 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v15 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v15"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13.2 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13.2"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v15 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v15"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v0 no match push-v14", - artifact: "binary-linux-amd64-push-v14", + artifacts: []string{"binary-linux-amd64-push-v14"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v0"), err: serrors.ErrorMismatchVersionedTag, @@ -299,68 +303,68 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Provenance contains tag = v14.2 { name: "versioned v14.2 match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.2"), }, { name: "versioned v14.2.1 match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.2.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v14.2.3 match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.2.3"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v14 match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14"), }, { name: "versioned v14.1 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v14.1.1 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.1.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v14.3.1 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v14.3.1"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v13 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v13"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v15 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v15"), err: serrors.ErrorMismatchVersionedTag, }, { name: "versioned v15.1 no match push-v14.2", - artifact: "binary-linux-amd64-push-v14.2", + artifacts: []string{"binary-linux-amd64-push-v14.2"}, source: "github.com/slsa-framework/example-package", pversiontag: pString("v15.1"), err: serrors.ErrorMismatchVersionedTag, @@ -368,21 +372,57 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Multiple subjects in version v1.2.0+ { name: "multiple subject first match", - artifact: "binary-linux-amd64-multi-subject-first", + artifacts: []string{"binary-linux-amd64-multi-subject-first"}, source: "github.com/slsa-framework/example-package", noversion: true, builders: []string{"gha_generic"}, }, { name: "multiple subject second match", - artifact: "binary-linux-amd64-multi-subject-second", + artifacts: []string{"binary-linux-amd64-multi-subject-second"}, source: "github.com/slsa-framework/example-package", noversion: true, builders: []string{"gha_generic"}, }, + { + name: "multiple subject first and second match", + artifacts: []string{"binary-linux-amd64-multi-subject-first", "binary-linux-amd64-multi-subject-second"}, + source: "github.com/slsa-framework/example-package", + noversion: true, + builders: []string{"gha_generic"}, + }, + { + name: "multiple subject second and first match", + artifacts: []string{"binary-linux-amd64-multi-subject-second", "binary-linux-amd64-multi-subject-first"}, + source: "github.com/slsa-framework/example-package", + noversion: true, + builders: []string{"gha_generic"}, + }, + { + name: "multiple subject repeated match", + artifacts: []string{"binary-linux-amd64-multi-subject-first", "binary-linux-amd64-multi-subject-first"}, + source: "github.com/slsa-framework/example-package", + noversion: true, + builders: []string{"gha_generic"}, + }, + { + name: "multiple subject one mismatch", + artifacts: []string{"binary-linux-amd64-multi-subject-first", "binary-linux-amd64-sharded"}, + source: "github.com/slsa-framework/example-package", + noversion: true, + err: serrors.ErrorMismatchHash, + }, + { + name: "multiple subject no match", + artifacts: []string{"binary-linux-amd64-sharded"}, + source: "github.com/slsa-framework/example-package", + noversion: true, + err: serrors.ErrorMismatchHash, + provenancePath: "binary-linux-amd64-multi-subject-first.intoto.jsonl", + }, { name: "multiple subject second match - builderID", - artifact: "binary-linux-amd64-multi-subject-second", + artifacts: []string{"binary-linux-amd64-multi-subject-second"}, source: "github.com/slsa-framework/example-package", noversion: true, builders: []string{"gha_generic"}, @@ -392,7 +432,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Special case of the e2e test repository building builder from head. { name: "e2e test repository verified with builder at head", - artifact: "binary-linux-amd64-e2e-builder-repo", + artifacts: []string{"binary-linux-amd64-e2e-builder-repo"}, source: "github.com/slsa-framework/example-package", pbranch: pString("main"), noversion: true, @@ -402,21 +442,21 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Malicious builders and workflows. { name: "rekor upload bypassed", - artifact: "binary-linux-amd64-no-tlog-upload", + artifacts: []string{"binary-linux-amd64-no-tlog-upload"}, source: "github.com/slsa-framework/example-package", err: serrors.ErrorRekorSearch, noversion: true, }, { name: "malicious: untrusted builder", - artifact: "binary-linux-amd64-untrusted-builder", + artifacts: []string{"binary-linux-amd64-untrusted-builder"}, source: "github.com/slsa-framework/example-package", err: serrors.ErrorUntrustedReusableWorkflow, noversion: true, }, { name: "malicious: invalid signature expired certificate", - artifact: "binary-linux-amd64-expired-cert", + artifacts: []string{"binary-linux-amd64-expired-cert"}, source: "github.com/slsa-framework/example-package", err: serrors.ErrorRekorSearch, noversion: true, @@ -424,14 +464,14 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Annotated tags. { name: "annotated tag", - artifact: "annotated-tag", + artifacts: []string{"annotated-tag"}, source: "github.com/laurentsimon/slsa-on-github-test", pversiontag: pString("v5.0.1"), noversion: true, }, { name: "no branch", - artifact: "annotated-tag", + artifacts: []string{"annotated-tag"}, source: "github.com/laurentsimon/slsa-on-github-test", pversiontag: pString("v5.0.1"), pbranch: pString("main"), @@ -440,9 +480,9 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { }, // Workflow inputs. { - name: "workflow inputs match", - artifact: "workflow-inputs", - source: "github.com/laurentsimon/slsa-on-github-test", + name: "workflow inputs match", + artifacts: []string{"workflow-inputs"}, + source: "github.com/laurentsimon/slsa-on-github-test", inputs: map[string]string{ "release_version": "v1.2.3", "some_bool": "true", @@ -451,9 +491,9 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { noversion: true, }, { - name: "workflow inputs missing field", - artifact: "workflow-inputs", - source: "github.com/laurentsimon/slsa-on-github-test", + name: "workflow inputs missing field", + artifacts: []string{"workflow-inputs"}, + source: "github.com/laurentsimon/slsa-on-github-test", inputs: map[string]string{ "release_version": "v1.2.3", "some_bool": "true", @@ -463,9 +503,9 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { noversion: true, }, { - name: "workflow inputs mismatch", - artifact: "workflow-inputs", - source: "github.com/laurentsimon/slsa-on-github-test", + name: "workflow inputs mismatch", + artifacts: []string{"workflow-inputs"}, + source: "github.com/laurentsimon/slsa-on-github-test", inputs: map[string]string{ "release_version": "v1.2.3", "some_bool": "true", @@ -477,7 +517,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Regression test of sharded UUID. { name: "regression: sharded uuids", - artifact: "binary-linux-amd64-sharded", + artifacts: []string{"binary-linux-amd64-sharded"}, source: "github.com/slsa-framework/slsa-verifier", pbranch: pString("release/v1.0"), pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml"), @@ -496,8 +536,18 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { } for _, v := range checkVersions { - artifactPath := filepath.Clean(filepath.Join(TEST_DIR, v, tt.artifact)) - provenancePath := fmt.Sprintf("%s.intoto.jsonl", artifactPath) + var provenancePath string + if tt.provenancePath == "" { + testPath := filepath.Clean(filepath.Join(TEST_DIR, v, tt.artifacts[0])) + provenancePath = fmt.Sprintf("%s.intoto.jsonl", testPath) + } else { + provenancePath = filepath.Clean(filepath.Join(TEST_DIR, v, tt.provenancePath)) + } + + artifacts := make([]string, len(tt.artifacts)) + for i, artifact := range tt.artifacts { + artifacts[i] = filepath.Clean(filepath.Join(TEST_DIR, v, artifact)) + } // TODO(#258): invalid builder ref. sv := path.Base(v) @@ -555,7 +605,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { BuildWorkflowInputs: tt.inputs, } - outBuilderID, err := cmd.Exec(context.Background(), []string{artifactPath}) + outBuilderID, err := cmd.Exec(context.Background(), artifacts) if !errCmp(err, tt.err) { t.Errorf("%v: %v", v, cmp.Diff(err, tt.err, cmpopts.EquateErrors())) } @@ -583,10 +633,10 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { // Smoke test against the CLI command cliCmd := verifyArtifactCmd() args := []string{ - artifactPath, "--source-uri", tt.source, "--provenance-path", provenancePath, } + args = append(args, artifacts...) if bid != nil { args = append(args, "--builder-id", *bid) } diff --git a/cli/slsa-verifier/verify.go b/cli/slsa-verifier/verify.go index 7d38cad5e..82eb51ea6 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -32,14 +32,8 @@ func verifyArtifactCmd() *cobra.Command { o := &verify.VerifyOptions{} cmd := &cobra.Command{ - Use: "verify-artifact [flags] artifact", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return errors.New("expects a single path to an artifact") - } - return nil - }, - Short: "Verifies SLSA provenance on an artifact blob", + Use: "verify-artifact [flags] artifact [artifact..]", + Short: "Verifies SLSA provenance on artifact blobs given as arguments (assuming same provenance)", RunE: func(cmd *cobra.Command, args []string) error { v := verify.VerifyArtifactCommand{ ProvenancePath: o.ProvenancePath, diff --git a/cli/slsa-verifier/verify/verify_artifact.go b/cli/slsa-verifier/verify/verify_artifact.go index cf7b62340..2c7dbda45 100644 --- a/cli/slsa-verifier/verify/verify_artifact.go +++ b/cli/slsa-verifier/verify/verify_artifact.go @@ -40,39 +40,55 @@ type VerifyArtifactCommand struct { } func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (*utils.TrustedBuilderID, error) { - artifactHash, err := getArtifactHash(artifacts[0]) - if err != nil { - return nil, err - } + var builderId *utils.TrustedBuilderID - provenanceOpts := &options.ProvenanceOpts{ - ExpectedSourceURI: c.SourceURI, - ExpectedBranch: c.SourceBranch, - ExpectedDigest: artifactHash, - ExpectedVersionedTag: c.SourceVersionTag, - ExpectedTag: c.SourceTag, - ExpectedWorkflowInputs: c.BuildWorkflowInputs, - } + for _, artifact := range artifacts { + artifactHash, err := getArtifactHash(artifact) + if err != nil { + fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) + return nil, err + } - builderOpts := &options.BuilderOpts{ - ExpectedID: c.BuilderID, - } + provenanceOpts := &options.ProvenanceOpts{ + ExpectedSourceURI: c.SourceURI, + ExpectedBranch: c.SourceBranch, + ExpectedDigest: artifactHash, + ExpectedVersionedTag: c.SourceVersionTag, + ExpectedTag: c.SourceTag, + ExpectedWorkflowInputs: c.BuildWorkflowInputs, + } - provenance, err := os.ReadFile(c.ProvenancePath) - if err != nil { - return nil, err - } + builderOpts := &options.BuilderOpts{ + ExpectedID: c.BuilderID, + } - verifiedProvenance, outBuilderID, err := verifiers.VerifyArtifact(ctx, provenance, artifactHash, provenanceOpts, builderOpts) - if err != nil { - return nil, err - } + provenance, err := os.ReadFile(c.ProvenancePath) + if err != nil { + fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) + return nil, err + } + + verifiedProvenance, outBuilderID, err := verifiers.VerifyArtifact(ctx, provenance, artifactHash, provenanceOpts, builderOpts) + if err != nil { + fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) + return nil, err + } + + if c.PrintProvenance { + fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance)) + } - if c.PrintProvenance { - fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance)) + if builderId == nil { + builderId = outBuilderID + } else if *builderId != *outBuilderID { + err := fmt.Errorf("Encountered different builderIDs %v %v\n", builderId, outBuilderID) + fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) + return nil, err + } + fmt.Fprintf(os.Stderr, "Verifying artifact %s: PASSED\n\n", artifact) } - return outBuilderID, nil + return builderId, nil } func getArtifactHash(artifactPath string) (string, error) {