From 9168a5d7d895b9e31a24aa338df01bd8b6ff5272 Mon Sep 17 00:00:00 2001 From: Fredrik Skogman Date: Mon, 18 Nov 2024 14:12:44 +0100 Subject: [PATCH 01/20] Added a section on manual verification of the relases. Signed-off-by: Fredrik Skogman --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 064008e3b..23b33881d 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ For more information, see [Linux & BSD installation](./docs/install_linux.md). | ------------------- | --------------------| | `winget install --id GitHub.cli` | `winget upgrade --id GitHub.cli` | -> **Note** +> **Note** > The Windows installer modifies your PATH. When using Windows Terminal, you will need to **open a new window** for the changes to take effect. (Simply opening a new tab will _not_ be sufficient.) #### scoop @@ -125,6 +125,36 @@ GitHub CLI comes pre-installed in all [GitHub-Hosted Runners](https://docs.githu Download packaged binaries from the [releases page][]. +#### Verification of binaries + +Since version 2.50.0 `gh` has been producing [Build Provenance Attestation](https://github.blog/changelog/2024-06-25-artifact-attestations-is-generally-available/) enabling a cryptographically verifiable paper-trail back to the origin GitHub repository, git revision and build instructions used. The build provenance attestations are signed and relies on Public Good [Sigstore](https://www.sigstore.dev/) for PKI. + +There are two common ways to verify a downloaded release, depending if `gh` is aready installed or not. If `gh` is installed, it's trivial to verify a new release: + +```shell +$ % gh at verify -R cli/cli gh_2.62.0_macOS_arm64.zip +Loaded digest sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc for file://gh_2.62.0_macOS_arm64.zip +Loaded 1 attestation from GitHub API +✓ Verification succeeded! + +sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc was attested by: +REPO PREDICATE_TYPE WORKFLOW +cli/cli https://slsa.dev/provenance/v1 .github/workflows/deployment.yml@refs/heads/trunk +``` + +If `gh` is not installed, you can use the `cosign` [binary](https://github.com/sigstore/cosign) from Sigstore to verify a release. To perform this, you must first download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release, then you can invoke cosign to verify the authenticity of the downloaded file: + +```shell +$ cosign verify-blob-attestation --bundle cli-cli-attestation-3120304.sigstore.json \ + --new-bundle-format \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + --certificate-identity-regexp="^https://github.com/cli/cli/.github/workflows/deployment.yml@refs/heads/trunk$" \ + gh_2.62.0_macOS_arm64.zip +Verified OK +``` + +This will verify that the downloaded zip file is authentic, and it can be extracted to get the `gh` binary. + ### Build from source See here on how to [build GitHub CLI from source][build from source]. From 5098ea407aa88f56fe62d4c0203db6d467c95e5d Mon Sep 17 00:00:00 2001 From: Fredrik Skogman Date: Mon, 18 Nov 2024 14:48:34 +0100 Subject: [PATCH 02/20] Updated markdown syntax for a `note`. Signed-off-by: Fredrik Skogman --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23b33881d..42b383938 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ For more information, see [Linux & BSD installation](./docs/install_linux.md). | ------------------- | --------------------| | `winget install --id GitHub.cli` | `winget upgrade --id GitHub.cli` | -> **Note** +> [!NOTE] > The Windows installer modifies your PATH. When using Windows Terminal, you will need to **open a new window** for the changes to take effect. (Simply opening a new tab will _not_ be sufficient.) #### scoop From 601982faf187d78a6ebb05243355c46c461986be Mon Sep 17 00:00:00 2001 From: Fredrik Skogman Date: Mon, 18 Nov 2024 15:16:47 +0100 Subject: [PATCH 03/20] Updated formatting to be more clear Signed-off-by: Fredrik Skogman --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42b383938..a505a201b 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ Since version 2.50.0 `gh` has been producing [Build Provenance Attestation](http There are two common ways to verify a downloaded release, depending if `gh` is aready installed or not. If `gh` is installed, it's trivial to verify a new release: +- **Option 1: Using `gh` if already installed: + ```shell $ % gh at verify -R cli/cli gh_2.62.0_macOS_arm64.zip Loaded digest sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc for file://gh_2.62.0_macOS_arm64.zip @@ -142,7 +144,9 @@ REPO PREDICATE_TYPE WORKFLOW cli/cli https://slsa.dev/provenance/v1 .github/workflows/deployment.yml@refs/heads/trunk ``` -If `gh` is not installed, you can use the `cosign` [binary](https://github.com/sigstore/cosign) from Sigstore to verify a release. To perform this, you must first download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release, then you can invoke cosign to verify the authenticity of the downloaded file: +- **Option 2: Using Sigstore [`cosign`](https://github.com/sigstore/cosign): + +To perform this, download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release and use cosign to verify the authenticity of the downloaded release: ```shell $ cosign verify-blob-attestation --bundle cli-cli-attestation-3120304.sigstore.json \ @@ -153,8 +157,6 @@ $ cosign verify-blob-attestation --bundle cli-cli-attestation-3120304.sigstore.j Verified OK ``` -This will verify that the downloaded zip file is authentic, and it can be extracted to get the `gh` binary. - ### Build from source See here on how to [build GitHub CLI from source][build from source]. From 075691b03e9d91c9de5dedfa8e8d104baee02a99 Mon Sep 17 00:00:00 2001 From: Fredrik Skogman Date: Mon, 18 Nov 2024 15:17:34 +0100 Subject: [PATCH 04/20] Formatting fix Signed-off-by: Fredrik Skogman --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a505a201b..bcd470eec 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Since version 2.50.0 `gh` has been producing [Build Provenance Attestation](http There are two common ways to verify a downloaded release, depending if `gh` is aready installed or not. If `gh` is installed, it's trivial to verify a new release: -- **Option 1: Using `gh` if already installed: +- **Option 1: Using `gh` if already installed:** ```shell $ % gh at verify -R cli/cli gh_2.62.0_macOS_arm64.zip @@ -144,7 +144,7 @@ REPO PREDICATE_TYPE WORKFLOW cli/cli https://slsa.dev/provenance/v1 .github/workflows/deployment.yml@refs/heads/trunk ``` -- **Option 2: Using Sigstore [`cosign`](https://github.com/sigstore/cosign): +- **Option 2: Using Sigstore [`cosign`](https://github.com/sigstore/cosign):** To perform this, download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release and use cosign to verify the authenticity of the downloaded release: From f48e6b56e3c947184aebd251b72a4e31b4b7006e Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 19 Nov 2024 14:38:28 -0700 Subject: [PATCH 05/20] verify cert extensions function should return filtered result list Signed-off-by: Meredith Lancaster --- .../attestation/verification/extensions.go | 32 ++++++++++++------- .../verification/extensions_test.go | 21 ++++++++---- pkg/cmd/attestation/verify/verify.go | 9 +++--- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/pkg/cmd/attestation/verification/extensions.go b/pkg/cmd/attestation/verification/extensions.go index a0827e9ec..4f70a1c2e 100644 --- a/pkg/cmd/attestation/verification/extensions.go +++ b/pkg/cmd/attestation/verification/extensions.go @@ -13,25 +13,35 @@ var ( GitHubTenantOIDCIssuer = "https://token.actions.%s.ghe.com" ) -func VerifyCertExtensions(results []*AttestationProcessingResult, ec EnforcementCriteria) error { +func VerifyCertExtensions(results []*AttestationProcessingResult, ec EnforcementCriteria) ([]*AttestationProcessingResult, error) { if len(results) == 0 { - return errors.New("no attestations proccessing results") + return nil, errors.New("no attestations processing results") } + verified := make([]*AttestationProcessingResult, len(results)) + var verifyCount int var lastErr error for _, attestation := range results { - err := verifyCertExtensions(*attestation.VerificationResult.Signature.Certificate, ec.Certificate) - if err == nil { - // if at least one attestation is verified, we're good as verification - // is defined as successful if at least one attestation is verified - return nil + if err := verifyCertExtensions(*attestation.VerificationResult.Signature.Certificate, ec.Certificate); err != nil { + lastErr = err + // move onto the next attestation in the for loop if verification fails + continue } - lastErr = err + // otherwise, add the result to the results slice and increment verifyCount + verified[verifyCount] = attestation + verifyCount++ } - // if we have exited the for loop without returning early due to successful - // verification, we need to return an error - return lastErr + // if we have exited the for loop without verifying any attestations, + // return the last error found + if verifyCount == 0 { + return nil, lastErr + } + + // truncate the verified slice to only include verified attestations + verified = verified[:verifyCount] + return verified, nil + } func verifyCertExtensions(given, expected certificate.Summary) error { diff --git a/pkg/cmd/attestation/verification/extensions_test.go b/pkg/cmd/attestation/verification/extensions_test.go index b8ef2875f..73d808119 100644 --- a/pkg/cmd/attestation/verification/extensions_test.go +++ b/pkg/cmd/attestation/verification/extensions_test.go @@ -37,8 +37,9 @@ func TestVerifyCertExtensions(t *testing.T) { } t.Run("passes with one result", func(t *testing.T) { - err := VerifyCertExtensions(results, c) + verified, err := VerifyCertExtensions(results, c) require.NoError(t, err) + require.Len(t, verified, 1) }) t.Run("passes with 1/2 valid results", func(t *testing.T) { @@ -46,8 +47,9 @@ func TestVerifyCertExtensions(t *testing.T) { require.Len(t, twoResults, 2) twoResults[1].VerificationResult.Signature.Certificate.Extensions.SourceRepositoryOwnerURI = "https://github.com/wrong" - err := VerifyCertExtensions(twoResults, c) + verified, err := VerifyCertExtensions(twoResults, c) require.NoError(t, err) + require.Len(t, verified, 1) }) t.Run("fails when all results fail verification", func(t *testing.T) { @@ -56,35 +58,40 @@ func TestVerifyCertExtensions(t *testing.T) { twoResults[0].VerificationResult.Signature.Certificate.Extensions.SourceRepositoryOwnerURI = "https://github.com/wrong" twoResults[1].VerificationResult.Signature.Certificate.Extensions.SourceRepositoryOwnerURI = "https://github.com/wrong" - err := VerifyCertExtensions(twoResults, c) + verified, err := VerifyCertExtensions(twoResults, c) require.Error(t, err) + require.Nil(t, verified) }) t.Run("with wrong SourceRepositoryOwnerURI", func(t *testing.T) { expectedCriteria := c expectedCriteria.Certificate.SourceRepositoryOwnerURI = "https://github.com/wrong" - err := VerifyCertExtensions(results, expectedCriteria) + verified, err := VerifyCertExtensions(results, expectedCriteria) require.ErrorContains(t, err, "expected SourceRepositoryOwnerURI to be https://github.com/wrong, got https://github.com/owner") + require.Nil(t, verified) }) t.Run("with wrong SourceRepositoryURI", func(t *testing.T) { expectedCriteria := c expectedCriteria.Certificate.SourceRepositoryURI = "https://github.com/foo/wrong" - err := VerifyCertExtensions(results, expectedCriteria) + verified, err := VerifyCertExtensions(results, expectedCriteria) require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/foo/wrong, got https://github.com/owner/repo") + require.Nil(t, verified) }) t.Run("with wrong OIDCIssuer", func(t *testing.T) { expectedCriteria := c expectedCriteria.Certificate.Issuer = "wrong" - err := VerifyCertExtensions(results, expectedCriteria) + verified, err := VerifyCertExtensions(results, expectedCriteria) require.ErrorContains(t, err, "expected Issuer to be wrong, got https://token.actions.githubusercontent.com") + require.Nil(t, verified) }) t.Run("with partial OIDCIssuer match", func(t *testing.T) { expectedResults := results expectedResults[0].VerificationResult.Signature.Certificate.Extensions.Issuer = "https://token.actions.githubusercontent.com/foo-bar" - err := VerifyCertExtensions(expectedResults, c) + verified, err := VerifyCertExtensions(expectedResults, c) require.ErrorContains(t, err, "expected Issuer to be https://token.actions.githubusercontent.com, got https://token.actions.githubusercontent.com/foo-bar -- if you have a custom OIDC issuer") + require.Nil(t, verified) }) } diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 1d34fdf99..86b8c6d8d 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -250,14 +250,15 @@ func runVerify(opts *Options) error { return err } - verifyResults, err := opts.SigstoreVerifier.Verify(attestations, sp) + sigstoreVerified, err := opts.SigstoreVerifier.Verify(attestations, sp) if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Sigstore verification failed")) return err } // Verify extensions - if err := verification.VerifyCertExtensions(verifyResults, ec); err != nil { + certExtVerified, err := verification.VerifyCertExtensions(sigstoreVerified, ec) + if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Policy verification failed")) return err } @@ -267,7 +268,7 @@ func runVerify(opts *Options) error { // If an exporter is provided with the --json flag, write the results to the terminal in JSON format if opts.exporter != nil { // print the results to the terminal as an array of JSON objects - if err = opts.exporter.Write(opts.Logger.IO, verifyResults); err != nil { + if err = opts.exporter.Write(opts.Logger.IO, certExtVerified); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to write JSON output")) return err } @@ -277,7 +278,7 @@ func runVerify(opts *Options) error { opts.Logger.Printf("%s was attested by:\n", artifact.DigestWithAlg()) // Otherwise print the results to the terminal in a table - tableContent, err := buildTableVerifyContent(opts.Tenant, verifyResults) + tableContent, err := buildTableVerifyContent(opts.Tenant, certExtVerified) if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse results")) return err From 9414930b5df4a176ad5f1dd3da1e7ffc8be44382 Mon Sep 17 00:00:00 2001 From: Dalius Dobravolskas Date: Tue, 19 Nov 2024 23:50:45 +0200 Subject: [PATCH 06/20] Adding option to return `baseRefOid` in `pr view` You need to know exact `baseRefOid` so you could show correct diff. `baseRefName` is not enough sometimes because branch from which PR was forked might have changes already. Example usage: ``` gh pr view --json headRefName,headRefOid,number,baseRefName,baseRefOid,reviewDecision ``` --- api/queries_pr.go | 1 + api/query_builder.go | 1 + pkg/cmd/pr/view/view_test.go | 1 + 3 files changed, 3 insertions(+) diff --git a/api/queries_pr.go b/api/queries_pr.go index 370f2db24..aa493b5e9 100644 --- a/api/queries_pr.go +++ b/api/queries_pr.go @@ -33,6 +33,7 @@ type PullRequest struct { Closed bool URL string BaseRefName string + BaseRefOid string HeadRefName string HeadRefOid string Body string diff --git a/api/query_builder.go b/api/query_builder.go index dbf889273..2112367e3 100644 --- a/api/query_builder.go +++ b/api/query_builder.go @@ -285,6 +285,7 @@ var PullRequestFields = append(sharedIssuePRFields, "additions", "autoMergeRequest", "baseRefName", + "baseRefOid", "changedFiles", "commits", "deletions", diff --git a/pkg/cmd/pr/view/view_test.go b/pkg/cmd/pr/view/view_test.go index 470e3bd27..e7f572c76 100644 --- a/pkg/cmd/pr/view/view_test.go +++ b/pkg/cmd/pr/view/view_test.go @@ -32,6 +32,7 @@ func TestJSONFields(t *testing.T) { "author", "autoMergeRequest", "baseRefName", + "baseRefOid", "body", "changedFiles", "closed", From b5788f252361e633192da95959ed6efdd7ee5da3 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 19 Nov 2024 16:24:17 -0700 Subject: [PATCH 07/20] wrap sigstore and cert ext verification into a single function Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/attestation.go | 18 +++ .../verify/attestation_integration_test.go | 105 ++++++++++++++++++ pkg/cmd/attestation/verify/verify.go | 15 +-- 3 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 pkg/cmd/attestation/verify/attestation_integration_test.go diff --git a/pkg/cmd/attestation/verify/attestation.go b/pkg/cmd/attestation/verify/attestation.go index f3f2792c4..add6f80fe 100644 --- a/pkg/cmd/attestation/verify/attestation.go +++ b/pkg/cmd/attestation/verify/attestation.go @@ -7,6 +7,7 @@ import ( "github.com/cli/cli/v2/pkg/cmd/attestation/api" "github.com/cli/cli/v2/pkg/cmd/attestation/artifact" "github.com/cli/cli/v2/pkg/cmd/attestation/verification" + "github.com/sigstore/sigstore-go/pkg/verify" ) func getAttestations(o *Options, a artifact.DigestedArtifact) ([]*api.Attestation, string, error) { @@ -48,3 +49,20 @@ func getAttestations(o *Options, a artifact.DigestedArtifact) ([]*api.Attestatio msg := fmt.Sprintf("Loaded %s from GitHub API", pluralAttestation) return attestations, msg, nil } + +func verifyAttestations(attestations []*api.Attestation, sgVerifier verification.SigstoreVerifier, sgPolicy verify.PolicyBuilder, ec verification.EnforcementCriteria) ([]*verification.AttestationProcessingResult, string, error) { + sigstoreVerified, err := sgVerifier.Verify(attestations, sgPolicy) + if err != nil { + errMsg := "✗ Sigstore verification failed" + return nil, errMsg, err + } + + // Verify extensions + certExtVerified, err := verification.VerifyCertExtensions(sigstoreVerified, ec) + if err != nil { + errMsg := "✗ Policy verification failed" + return nil, errMsg, err + } + + return certExtVerified, "", nil +} diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go new file mode 100644 index 000000000..5d787e15f --- /dev/null +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -0,0 +1,105 @@ +//go:build integration + +package verify + +import ( + "testing" + + "github.com/cli/cli/v2/pkg/cmd/attestation/api" + "github.com/cli/cli/v2/pkg/cmd/attestation/artifact" + "github.com/cli/cli/v2/pkg/cmd/attestation/io" + "github.com/cli/cli/v2/pkg/cmd/attestation/test" + "github.com/cli/cli/v2/pkg/cmd/attestation/verification" + "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" + "github.com/stretchr/testify/require" +) + +func getAttestationsFor(t *testing.T, bundlePath string) []*api.Attestation { + t.Helper() + + attestations, err := verification.GetLocalAttestations(bundlePath) + require.NoError(t, err) + + return attestations +} + +func TestVerifyAttestations(t *testing.T) { + sigstoreConfig := verification.SigstoreConfig{ + Logger: io.NewTestHandler(), + } + sgVerifier := verification.NewLiveSigstoreVerifier(sigstoreConfig) + + certSummary := certificate.Summary{} + certSummary.SourceRepositoryOwnerURI = "https://github.com/sigstore" + certSummary.SourceRepositoryURI = "https://github.com/sigstore/sigstore-js" + certSummary.Issuer = verification.GitHubOIDCIssuer + + ec := verification.EnforcementCriteria{ + Certificate: certSummary, + PredicateType: verification.SLSAPredicateV1, + SANRegex: "^https://github.com/sigstore/", + } + + artifactPath := test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz") + a, err := artifact.NewDigestedArtifact(nil, artifactPath, "sha512") + require.NoError(t, err) + + sp, err := buildSigstoreVerifyPolicy(ec, *a) + + t.Run("all attestations pass verification", func(t *testing.T) { + attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + require.NoError(t, err) + require.Zero(t, errMsg) + require.Len(t, results, 2) + }) + + t.Run("passes verification with 2/3 attestations passing Sigstore verification", func(t *testing.T) { + invalidBundle := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0-bundle-v0.1.json") + attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + attestations = append(attestations, invalidBundle[0]) + require.Len(t, attestations, 3) + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + require.NoError(t, err) + require.Zero(t, errMsg) + require.Len(t, results, 2) + }) + + t.Run("fails verification when Sigstore verification fails", func(t *testing.T) { + invalidBundle := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0-bundle-v0.1.json") + invalidBundle2 := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0-bundle-v0.1.json") + attestations := append(invalidBundle, invalidBundle2...) + require.Len(t, attestations, 2) + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + require.Error(t, err) + require.Contains(t, errMsg, "✗ Sigstore verification failed") + require.Nil(t, results) + }) + + t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { + ghAttestation := getAttestationsFor(t, "../test/data/github_provenance_demo-0.0.12-py3-none-any-bundle.jsonl") + attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + attestations = append(attestations, ghAttestation[0]) + require.Len(t, attestations, 3) + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + require.NoError(t, err) + require.Zero(t, errMsg) + require.Len(t, results, 2) + }) + + t.Run("fails verification when cert extension verification fails", func(t *testing.T) { + attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + require.Len(t, attestations, 2) + + expectedCriteria := ec + expectedCriteria.Certificate.SourceRepositoryOwnerURI = "https://github.com/wrong" + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, expectedCriteria) + require.Error(t, err) + require.Contains(t, errMsg, "✗ Policy verification failed") + require.Nil(t, results) + }) +} diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 86b8c6d8d..4efc23f44 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -250,16 +250,9 @@ func runVerify(opts *Options) error { return err } - sigstoreVerified, err := opts.SigstoreVerifier.Verify(attestations, sp) + verified, errMsg, err := verifyAttestations(attestations, opts.SigstoreVerifier, sp, ec) if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Sigstore verification failed")) - return err - } - - // Verify extensions - certExtVerified, err := verification.VerifyCertExtensions(sigstoreVerified, ec) - if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Policy verification failed")) + opts.Logger.Println(opts.Logger.ColorScheme.Red(errMsg)) return err } @@ -268,7 +261,7 @@ func runVerify(opts *Options) error { // If an exporter is provided with the --json flag, write the results to the terminal in JSON format if opts.exporter != nil { // print the results to the terminal as an array of JSON objects - if err = opts.exporter.Write(opts.Logger.IO, certExtVerified); err != nil { + if err = opts.exporter.Write(opts.Logger.IO, verified); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to write JSON output")) return err } @@ -278,7 +271,7 @@ func runVerify(opts *Options) error { opts.Logger.Printf("%s was attested by:\n", artifact.DigestWithAlg()) // Otherwise print the results to the terminal in a table - tableContent, err := buildTableVerifyContent(opts.Tenant, certExtVerified) + tableContent, err := buildTableVerifyContent(opts.Tenant, verified) if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse results")) return err From 3e6861e7e1f725f5e19ab79d5f83d659a8cace6b Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 19 Nov 2024 16:28:36 -0700 Subject: [PATCH 08/20] clean up Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/attestation_integration_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 5d787e15f..e7799247d 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -24,10 +24,10 @@ func getAttestationsFor(t *testing.T, bundlePath string) []*api.Attestation { } func TestVerifyAttestations(t *testing.T) { - sigstoreConfig := verification.SigstoreConfig{ + config := verification.SigstoreConfig{ Logger: io.NewTestHandler(), } - sgVerifier := verification.NewLiveSigstoreVerifier(sigstoreConfig) + sgVerifier := verification.NewLiveSigstoreVerifier(config) certSummary := certificate.Summary{} certSummary.SourceRepositoryOwnerURI = "https://github.com/sigstore" @@ -48,6 +48,7 @@ func TestVerifyAttestations(t *testing.T) { t.Run("all attestations pass verification", func(t *testing.T) { attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + require.Len(t, attestations, 2) results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) require.NoError(t, err) require.Zero(t, errMsg) From 78260967f923a6212c1dbbebab814b07c354a9ab Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Wed, 20 Nov 2024 10:26:00 -0500 Subject: [PATCH 09/20] Fix README.md code block formatting --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index bcd470eec..60ab7d6d9 100644 --- a/README.md +++ b/README.md @@ -133,29 +133,29 @@ There are two common ways to verify a downloaded release, depending if `gh` is a - **Option 1: Using `gh` if already installed:** -```shell -$ % gh at verify -R cli/cli gh_2.62.0_macOS_arm64.zip -Loaded digest sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc for file://gh_2.62.0_macOS_arm64.zip -Loaded 1 attestation from GitHub API -✓ Verification succeeded! + ```shell + $ % gh at verify -R cli/cli gh_2.62.0_macOS_arm64.zip + Loaded digest sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc for file://gh_2.62.0_macOS_arm64.zip + Loaded 1 attestation from GitHub API + ✓ Verification succeeded! -sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc was attested by: -REPO PREDICATE_TYPE WORKFLOW -cli/cli https://slsa.dev/provenance/v1 .github/workflows/deployment.yml@refs/heads/trunk -``` + sha256:fdb77f31b8a6dd23c3fd858758d692a45f7fc76383e37d475bdcae038df92afc was attested by: + REPO PREDICATE_TYPE WORKFLOW + cli/cli https://slsa.dev/provenance/v1 .github/workflows/deployment.yml@refs/heads/trunk + ``` - **Option 2: Using Sigstore [`cosign`](https://github.com/sigstore/cosign):** -To perform this, download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release and use cosign to verify the authenticity of the downloaded release: + To perform this, download the [attestation](https://github.com/cli/cli/attestations) for the downloaded release and use cosign to verify the authenticity of the downloaded release: -```shell -$ cosign verify-blob-attestation --bundle cli-cli-attestation-3120304.sigstore.json \ - --new-bundle-format \ - --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ - --certificate-identity-regexp="^https://github.com/cli/cli/.github/workflows/deployment.yml@refs/heads/trunk$" \ - gh_2.62.0_macOS_arm64.zip -Verified OK -``` + ```shell + $ cosign verify-blob-attestation --bundle cli-cli-attestation-3120304.sigstore.json \ + --new-bundle-format \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + --certificate-identity-regexp="^https://github.com/cli/cli/.github/workflows/deployment.yml@refs/heads/trunk$" \ + gh_2.62.0_macOS_arm64.zip + Verified OK + ``` ### Build from source From 4671b8d66b892a34a5c4aef44cf952447b0e0fbd Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 20 Nov 2024 12:46:06 -0700 Subject: [PATCH 10/20] update test Signed-off-by: Meredith Lancaster --- .../attestation/verify/attestation_integration_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index e7799247d..77ea238a8 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -80,15 +80,18 @@ func TestVerifyAttestations(t *testing.T) { }) t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { - ghAttestation := getAttestationsFor(t, "../test/data/github_provenance_demo-0.0.12-py3-none-any-bundle.jsonl") - attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") - attestations = append(attestations, ghAttestation[0]) + ghAttestations := getAttestationsFor(t, "../test/data/github_provenance_demo-0.0.12-py3-none-any-bundle.jsonl") + sgjAttestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + attestations := []*api.Attestation{sgjAttestations[0], ghAttestations[0], sgjAttestations[1]} require.Len(t, attestations, 3) results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) + for _, r := range results { + require.NotEqual(t, r.Attestation.Bundle.String(), ghAttestations[0].Bundle.String()) + } }) t.Run("fails verification when cert extension verification fails", func(t *testing.T) { From ff8844a3084e017b47f514a71dafad3b77acacf6 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 20 Nov 2024 13:22:55 -0700 Subject: [PATCH 11/20] update test Signed-off-by: Meredith Lancaster --- .../attestation/verify/attestation_integration_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 77ea238a8..6eb79cdd9 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -80,12 +80,16 @@ func TestVerifyAttestations(t *testing.T) { }) t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { - ghAttestations := getAttestationsFor(t, "../test/data/github_provenance_demo-0.0.12-py3-none-any-bundle.jsonl") + ghAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") sgjAttestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") attestations := []*api.Attestation{sgjAttestations[0], ghAttestations[0], sgjAttestations[1]} require.Len(t, attestations, 3) - results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + expectedCriteria := ec + expectedCriteria.SANRegex = "^https://github.com/" + esp, err := buildSigstoreVerifyPolicy(ec, *a) + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, esp, expectedCriteria) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) From 5601149c837c1a3a1d95adfbb4f545fdf61c7b73 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 20 Nov 2024 13:34:40 -0700 Subject: [PATCH 12/20] naming Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/attestation_integration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 6eb79cdd9..46c239f61 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -80,9 +80,9 @@ func TestVerifyAttestations(t *testing.T) { }) t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { - ghAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") + rwAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") sgjAttestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") - attestations := []*api.Attestation{sgjAttestations[0], ghAttestations[0], sgjAttestations[1]} + attestations := []*api.Attestation{sgjAttestations[0], rwAttestations[0], sgjAttestations[1]} require.Len(t, attestations, 3) expectedCriteria := ec @@ -94,7 +94,7 @@ func TestVerifyAttestations(t *testing.T) { require.Zero(t, errMsg) require.Len(t, results, 2) for _, r := range results { - require.NotEqual(t, r.Attestation.Bundle.String(), ghAttestations[0].Bundle.String()) + require.NotEqual(t, r.Attestation.Bundle.String(), rwAttestations[0].Bundle.String()) } }) From 19afe453c7068ff568087806adce0c3b111709aa Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 20 Nov 2024 14:53:02 -0700 Subject: [PATCH 13/20] update test with new test bundle Signed-off-by: Meredith Lancaster --- ...artifact-attestations-workflow-bundle.json | 59 +++++++++++++++++++ .../verify/attestation_integration_test.go | 27 ++++++--- 2 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json diff --git a/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json b/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json new file mode 100644 index 000000000..ca17f4101 --- /dev/null +++ b/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json @@ -0,0 +1,59 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", + "verificationMaterial": { + "tlogEntries": [ + { + "logIndex": "129027678", + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + }, + "kindVersion": { + "kind": "dsse", + "version": "0.0.1" + }, + "integratedTime": "1725924650", + "inclusionPromise": { + "signedEntryTimestamp": "MEQCICZHyKBtXwvRDYWke7Pp0KfrBL8YNLoDHGP7lKFgkjGbAiB6V+IfSg/SYV/ySXOUg+t/Wp8wGjXMkKWnzPT9DWNsZA==" + }, + "inclusionProof": { + "logIndex": "7123416", + "rootHash": "LZ7MbkKAnq4sU1EMcWwJWWhw778o2w5vO6+Op/DFwm4=", + "treeSize": "7123418", + "hashes": [ + "pJMeUZ4R2LEEjf0JSmsoXTfHMgBAah4aOPBdXEa9sxo=", + "4f/Xq7IIiBbWQbSZZLkEBCSuOIbUXSvzYfxTIqh9rjY=", + "gy7fFADBSd5e37RIZy86inqhsnYB29bTUxY6/EtlJDk=", + "8JY4XnAVf8weXfLSChGSEbqVSN7FKSapmaM5Xi+qowM=", + "FKlHhO4TMH2pnrZUvSKA7Drig5MbABFy2KZx4esRxJY=", + "T+Ziyo74JC0j3MWEgjiiGuTCQ0w+AzLft+r9OyRldMw=", + "naRDgCL1Ch0MNzrXrAmrV1PLa/Bi5HV5GqrqlUceCVI=", + "c4TDdYxGB0ihJtrnXDSynGSSI83D5WVHvZJxuMti4Xg=", + "bcPqJfBdq24AxJvo1LAJKwcudDBLIIyVclqFzJW5TEY=", + "7Dvc6Q8qiduX8Oka5vLLU5oWAmybo8oaecNXPgkOQvA=", + "LAdu5Ynz/wk2uMNazU0CVickvA3YhhBz6TpIl5brTko=", + "uRsmea7eVXshBNN6huh/owmfaAy9Rx4Cq2M2vFb2Ntk=", + "NeHKGVl6KVXfx3+wnQrIrxra4Pr9Fa7YDpTlf86mlTc=" + ], + "checkpoint": { + "envelope": "rekor.sigstore.dev - 1193050959916656506\n7123418\nLZ7MbkKAnq4sU1EMcWwJWWhw778o2w5vO6+Op/DFwm4=\n\n— rekor.sigstore.dev wNI9ajBGAiEAy5505Eq4vOyji5LLmRbaN4/eqwOlVpgLOnozcVCJWvoCIQD+/CNPBY1eyyNypq25OIDwTVIHVxroif3cf2MsfEfplw==\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiYzk2YzgzYjE5NTAwOTJmOWEzYjc4NWE4ZWFlM2Y4MDY4YzcwYWQyZWJlZTFhNTRhYmIxZmJmYzk0ZTg1YzA3NSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImZmZTQ0OTYwMWI0ZmJjODQ4ODE0MzZkMzhlZjE4ZGRhNGI4NWM4YWY0NGI3YzM3ZmQwZTRhNGZhZmI0MGM2MzgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRQ0FPQmRJT3M2MnR1TzQybDhBWGNyK2t5ampDa1VqMTZRZUlwWkpkUjM4SWdJZ0x3YWlLdlg0RU9IclEvVFczOFQ4UTJiRzBvYkZ2b3NDNUlqYTFMbDlJNU09IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VoWmFrTkRRblZsWjBGM1NVSkJaMGxWWVRkNWJUSktNa1kyYVd0c1ZtdE1jMGhTUzNaVU9YTnZiR3BWZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmQwOVVRVFZOYWsxNlRVUlZkMWRvWTA1TmFsRjNUMVJCTlUxcVRUQk5SRlYzVjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVlFjekpOVmpNeU9HRkdRM2gyVWxKelFuQTNkR2R2WVRVcmJuUTFTbmR0TkdrM2Frc0tNR0ZUTUZFM2JVMDFZblpZZUZwdmRFdGtLMWxwTmpSMEswaFZOaXN4WnpWVlJIZzVNRFJJVjFCSlRUQkROemhwVUdGUFEwSm5XWGRuWjFsRFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVm1iMmg2Q2s1bFRXMUNkMW9yUm5rMFN6aE5aU3MwVlc5aVkxcEpkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMlZuV1VSV1VqQlNRVkZJTDBKSVFYZGliMXB6WVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREprY0dSSGFERlphVGxvWTI1U2NBcGFiVVpxWkVNeGFHUklVbXhqTTFKb1pFZHNkbUp1VFhSa01qbDVZVEphYzJJelpIcE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1tUnZDbGt6U1hSalNGWnBZa2RzZW1GRE5UVmlWM2hCWTIxV2JXTjVPVzlhVjBaclkzazVkRmxYYkhWTlJHdEhRMmx6UjBGUlVVSm5OemgzUVZGRlJVc3lhREFLWkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpCaFZ6bDFZM2sxYm1GWVVtOWtWMG94WXpKV2VWa3lPWFZrUjFaMVpFTTFhbUl5TUhkSWQxbExTM2RaUWdwQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWMySXpaR1phUjJ4NlkwZEdNRmt5WjNkT1oxbExTM2RaUWtKQlIwUjJla0ZDUVhkUmIxa3lUVE5QUjAwMUNsa3lVVEZOUjAweldXMUdhbHBVV21wTk1rcHNUWHBhYVZsdFVYbE5iVmw1V21wR2FVNVhXVFJOVkdkNVdXcEJlVUpuYjNKQ1owVkZRVmxQTDAxQlJVVUtRa05TUW1SSVVteGpNMUZuVTFjeGFGb3lWV2RMUldSSlNVVk9kbUp1VW1oaFZ6VnNZMmxDVTFwWFpIQmpNMUo1WlZOcmQwNUJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FsRlJiVm95YkRCaFNGWnBUREpHZVdSSGJHMVpWMDR3VEZkR01HUkhWbnBrUjBZd1lWYzVkV041TVROaU0wcHlXbTE0ZG1RelRYZElVVmxMQ2t0M1dVSkNRVWRFZG5wQlFrSm5VVkJqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxRWMwZERhWE5IUVZGUlFtYzNPSGRCVVdkRlRGRjNjbUZJVWpBS1kwaE5Oa3g1T1RCaU1uUnNZbWsxYUZrelVuQmlNalY2VEcxa2NHUkhhREZaYmxaNldsaEthbUl5TlRCYVZ6VXdURzFPZG1KVVFqaENaMjl5UW1kRlJRcEJXVTh2VFVGRlNrSkhORTFpUjJnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx1WVZoU2IyUlhTWFpaV0Vvd1lWZGFhRmt6VVhSWldGSXdDbHBZVGpCWldGSndZakkxZWt4WVpIWmpiWFJ0WWtjNU0yTjVPSFZhTW13d1lVaFdhVXd6WkhaamJYUnRZa2M1TTJONU9XNWhSMDU1VEZoQ01WbHRlSEFLWXpKbmRXVlhNWE5SU0Vwc1dtNU5kbUZIVm1oYVNFMTJZbGRHY0dKcVFUUkNaMjl5UW1kRlJVRlpUeTlOUVVWTFFrTnZUVXRIVG1wT2VtaHFUMWRPYXdwT1ZFSnFUakpLYUZreVZUSlplazVwV2xSTk1sbHRTbXROYWtwdFRXMVplRmxxVm0xUFJFVTBUVzFKZDBoUldVdExkMWxDUWtGSFJIWjZRVUpEZDFGUUNrUkJNVzVoV0ZKdlpGZEpkR0ZIT1hwa1IxWnJUVVZyUjBOcGMwZEJVVkZDWnpjNGQwRlJkMFZQZDNjMVlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVUtXVEk1ZEV3eVpIQmtSMmd4V1drNWFHTnVVbkJhYlVacVpFTXhhR1JJVW14ak0xSm9aRWRzZG1KdVRYUmtNamw1WVRKYWMySXpaSHBOUkdkSFEybHpSd3BCVVZGQ1p6YzRkMEZSTUVWTFozZHZXVEpOTTA5SFRUVlpNbEV4VFVkTk0xbHRSbXBhVkZwcVRUSktiRTE2V21sWmJWRjVUVzFaZVZwcVJtbE9WMWswQ2sxVVozbFpha0ZtUW1kdmNrSm5SVVZCV1U4dlRVRkZUMEpDUlUxRU0wcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRVnBDWjI5eVFtZEZSVUZaVHk4S1RVRkZVRUpCYzAxRFZHZDNUa1JSTWs5RVJYbFBWRUZ3UW1kdmNrSm5SVVZCV1U4dlRVRkZVVUpDYzAxSFYyZ3daRWhDZWs5cE9IWmFNbXd3WVVoV2FRcE1iVTUyWWxNNWJtRllVbTlrVjBsM1JrRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVZEVRVkUxVDFSRk5VMUlkMGREYVhOSFFWRlJRbWMzT0hkQlVrbEZDbUpuZUhOaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW1Sd1pFZG9NVmxwT1doamJsSndXbTFHYW1SRE1XaGtTRkpzWXpOU2FHUkhiSFlLWW01TmRHUXlPWGxoTWxwellqTmtla3g1Tlc1aFdGSnZaRmRKZG1ReU9YbGhNbHB6WWpOa2Vrd3laRzlaTTBsMFkwaFdhV0pIYkhwaFF6VTFZbGQ0UVFwamJWWnRZM2s1YjFwWFJtdGplVGwwV1Zkc2RVMUVaMGREYVhOSFFWRlJRbWMzT0hkQlVrMUZTMmQzYjFreVRUTlBSMDAxV1RKUk1VMUhUVE5aYlVacUNscFVXbXBOTWtwc1RYcGFhVmx0VVhsTmJWbDVXbXBHYVU1WFdUUk5WR2Q1V1dwQmFFSm5iM0pDWjBWRlFWbFBMMDFCUlZWQ1FrMU5SVmhrZG1OdGRHMEtZa2M1TTFneVVuQmpNMEpvWkVkT2IwMUhNRWREYVhOSFFWRlJRbWMzT0hkQlVsVkZXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkQXBNTW1Sd1pFZG9NVmxwT1doamJsSndXbTFHYW1SRE1XaGtTRkpzWXpOU2FHUkhiSFppYmsxMFpESTVlV0V5V25OaU0yUjZUREpHYW1SSGJIWmliazEyQ21OdVZuVmplVGg0VFVSak5FMXFhM2hPVkVWNFRsTTVhR1JJVW14aVdFSXdZM2s0ZUUxQ1dVZERhWE5IUVZGUlFtYzNPSGRCVWxsRlEwRjNSMk5JVm1rS1lrZHNhazFKUjBwQ1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtoelJXVlJRak5CU0ZWQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpRcFFTek12YURSd2VXZERPSEEzYnpSQlFVRkhVakpUUVhaQlVVRkJRa0ZOUVZKcVFrVkJhVUp6UTBKUlZtTlBOVmxrZDBsQ01VNXBSVVJxU1V0aFJXdG5DblpvUm1OQ1pGWTJVbE5wTjFNM05qVlBaMGxuWWtoeldXMTVUVGRuU2xWaWFtMXNSRFIxT0M5cVdtWjFNbEZrZGpKYU1rWmFVVzQ1YUVGUFkyNWtOSGNLUTJkWlNVdHZXa2w2YWpCRlFYZE5SR0ZSUVhkYVowbDRRVWx2Wmsxd2FFMU9RbU5ZYVhGRE0wNXZZWFpWYm14d04wOVBlSE5ZTkhoUVUzcGhZbWRtVFFwc2N6Z3JiazVuTUhsRWEwSnVaazlQZVZwblZVcHFieXNyWjBsNFFVcDZOek5qTlZnMFZERm5PRmx4YWs1VVNtMUZjWG8yYW5WbFlYcEJTVzVZYTA5TUNsSmFlV0ZCYVc1RFNsVnVRVVEwVUU1SVpucEZWV0UwTDFKTGRrazNRVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=" + } + ], + "timestampVerificationData": { + }, + "certificate": { + "rawBytes": "MIIHYjCCBuegAwIBAgIUa7ym2J2F6iklVkLsHRKvT9soljUwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQwOTA5MjMzMDUwWhcNMjQwOTA5MjM0MDUwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPs2MV328aFCxvRRsBp7tgoa5+nt5Jwm4i7jK0aS0Q7mM5bvXxZotKd+Yi64t+HU6+1g5UDx904HWPIM0C78iPaOCBgYwggYCMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUfohzNeMmBwZ+Fy4K8Me+4UobcZIwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wegYDVR0RAQH/BHAwboZsaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzLy5naXRodWIvd29ya2Zsb3dzL2doY3ItcHVibGlzaC55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAyBgorBgEEAYO/MAEEBCRBdHRlc3QgSW1hZ2UgKEdIIENvbnRhaW5lciBSZWdpc3RyeSkwNAYKKwYBBAGDvzABBQQmZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MwHQYKKwYBBAGDvzABBgQPcmVmcy9oZWFkcy9tYWluMDsGCisGAQQBg78wAQgELQwraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTB8BgorBgEEAYO/MAEJBG4MbGh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93cy8uZ2l0aHViL3dvcmtmbG93cy9naGNyLXB1Ymxpc2gueW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAEKBCoMKGNjNzhjOWNkNTBjN2JhY2U2YzNiZTM2YmJkMjJmMmYxYjVmODE4MmIwHQYKKwYBBAGDvzABCwQPDA1naXRodWItaG9zdGVkMEkGCisGAQQBg78wAQwEOww5aHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzMDgGCisGAQQBg78wAQ0EKgwoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAZBgorBgEEAYO/MAEPBAsMCTgwNDQ2ODEyOTApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIwFAYKKwYBBAGDvzABEQQGDAQ5OTE5MHwGCisGAQQBg78wARIEbgxsaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzLy5naXRodWIvd29ya2Zsb3dzL2doY3ItcHVibGlzaC55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rpc3BhdGNoMG0GCisGAQQBg78wARUEXwxdaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzL2FjdGlvbnMvcnVucy8xMDc4MjkxNTExNS9hdHRlbXB0cy8xMBYGCisGAQQBg78wARYECAwGcHVibGljMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGR2SAvAQAABAMARjBEAiBsCBQVcO5YdwIB1NiEDjIKaEkgvhFcBdV6RSi7S765OgIgbHsYmyM7gJUbjmlD4u8/jZfu2Qdv2Z2FZQn9hAOcnd4wCgYIKoZIzj0EAwMDaQAwZgIxAIofMphMNBcXiqC3NoavUnlp7OOxsX4xPSzabgfMls8+nNg0yDkBnfOOyZgUJjo++gIxAJz73c5X4T1g8YqjNTJmEqz6jueazAInXkOLRZyaAinCJUnAD4PNHfzEUa4/RKvI7A==" + } + }, + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93cyIsImRpZ2VzdCI6eyJzaGEyNTYiOiIwNmU2ODE4MzI0Y2IyMGUxZWE2ZjIyZGE3ZmI5MjUzMjNmYjU3MjUxYzk4NzY4Yzg4YjkzODcyYjUwNTkwZjkyIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MSIsInByZWRpY2F0ZSI6eyJidWlsZERlZmluaXRpb24iOnsiYnVpbGRUeXBlIjoiaHR0cHM6Ly9hY3Rpb25zLmdpdGh1Yi5pby9idWlsZHR5cGVzL3dvcmtmbG93L3YxIiwiZXh0ZXJuYWxQYXJhbWV0ZXJzIjp7IndvcmtmbG93Ijp7InJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInJlcG9zaXRvcnkiOiJodHRwczovL2dpdGh1Yi5jb20vZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MiLCJwYXRoIjoiLmdpdGh1Yi93b3JrZmxvd3MvZ2hjci1wdWJsaXNoLnltbCJ9fSwiaW50ZXJuYWxQYXJhbWV0ZXJzIjp7ImdpdGh1YiI6eyJldmVudF9uYW1lIjoid29ya2Zsb3dfZGlzcGF0Y2giLCJyZXBvc2l0b3J5X2lkIjoiODA0NDY4MTI5IiwicmVwb3NpdG9yeV9vd25lcl9pZCI6Ijk5MTkiLCJydW5uZXJfZW52aXJvbm1lbnQiOiJnaXRodWItaG9zdGVkIn19LCJyZXNvbHZlZERlcGVuZGVuY2llcyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93c0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsiZ2l0Q29tbWl0IjoiY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYiJ9fV19LCJydW5EZXRhaWxzIjp7ImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MvLmdpdGh1Yi93b3JrZmxvd3MvZ2hjci1wdWJsaXNoLnltbEByZWZzL2hlYWRzL21haW4ifSwibWV0YWRhdGEiOnsiaW52b2NhdGlvbklkIjoiaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzL2FjdGlvbnMvcnVucy8xMDc4MjkxNTExNS9hdHRlbXB0cy8xIn19fX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "MEUCIQCAOBdIOs62tuO42l8AXcr+kyjjCkUj16QeIpZJdR38IgIgLwaiKvX4EOHrQ/TW38T8Q2bG0obFvosC5Ija1Ll9I5M=" + } + ] + } +} \ No newline at end of file diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 46c239f61..480368519 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -80,21 +80,32 @@ func TestVerifyAttestations(t *testing.T) { }) t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { - rwAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") - sgjAttestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") - attestations := []*api.Attestation{sgjAttestations[0], rwAttestations[0], sgjAttestations[1]} + customArtifactPath := test.NormalizeRelativePath("../test/data/reusable-workflow-artifact") + customArtifact, err := artifact.NewDigestedArtifact(nil, customArtifactPath, "sha256") + require.NoError(t, err) + + mlDemoAttestAttestation := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") + aareusableAttestation := getAttestationsFor(t, "../test/data/gh-artifact-attestations-workflow-bundle.json") + attestations := []*api.Attestation{mlDemoAttestAttestation[0], aareusableAttestation[0], mlDemoAttestAttestation[0]} require.Len(t, attestations, 3) - expectedCriteria := ec - expectedCriteria.SANRegex = "^https://github.com/" - esp, err := buildSigstoreVerifyPolicy(ec, *a) + certSummary := certificate.Summary{} + certSummary.SourceRepositoryOwnerURI = "https://github.com/malancas" + certSummary.Issuer = verification.GitHubOIDCIssuer - results, errMsg, err := verifyAttestations(attestations, sgVerifier, esp, expectedCriteria) + customEc := verification.EnforcementCriteria{ + Certificate: certSummary, + PredicateType: verification.SLSAPredicateV1, + SANRegex: "^https://github.com/github/artifact-attestations-workflows/", + } + esp, err := buildSigstoreVerifyPolicy(customEc, *customArtifact) + + results, errMsg, err := verifyAttestations(attestations, sgVerifier, esp, customEc) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) for _, r := range results { - require.NotEqual(t, r.Attestation.Bundle.String(), rwAttestations[0].Bundle.String()) + require.NotEqual(t, r.Attestation.Bundle.String(), aareusableAttestation[0].Bundle.String()) } }) From 4d277df559c8b5a6c349738bf1c7ef740e0b8ae8 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 08:43:21 -0700 Subject: [PATCH 14/20] add more testing testing fixtures Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/inspect/inspect_test.go | 14 ++--- .../attestation/verification/mock_verifier.go | 55 +++++++++++++++++-- pkg/cmd/attestation/verification/sigstore.go | 2 +- pkg/cmd/attestation/verify/attestation.go | 19 ++++--- .../verify/attestation_integration_test.go | 45 +++++---------- pkg/cmd/attestation/verify/verify.go | 8 +-- pkg/cmd/attestation/verify/verify_test.go | 30 +++++----- 7 files changed, 101 insertions(+), 72 deletions(-) diff --git a/pkg/cmd/attestation/inspect/inspect_test.go b/pkg/cmd/attestation/inspect/inspect_test.go index 368cc54f5..29ca9aec7 100644 --- a/pkg/cmd/attestation/inspect/inspect_test.go +++ b/pkg/cmd/attestation/inspect/inspect_test.go @@ -58,7 +58,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha384", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -70,7 +70,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -82,7 +82,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha512", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -93,7 +93,7 @@ func TestNewInspectCmd(t *testing.T) { ArtifactPath: artifactPath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -105,7 +105,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -148,7 +148,7 @@ func TestRunInspect(t *testing.T) { DigestAlgorithm: "sha512", Logger: io.NewTestHandler(), OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), } t.Run("with valid artifact and bundle", func(t *testing.T) { @@ -176,7 +176,7 @@ func TestJSONOutput(t *testing.T) { DigestAlgorithm: "sha512", Logger: io.NewHandler(testIO), OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), exporter: cmdutil.NewJSONExporter(), } require.Nil(t, runInspect(&opts)) diff --git a/pkg/cmd/attestation/verification/mock_verifier.go b/pkg/cmd/attestation/verification/mock_verifier.go index 41332dc62..44ed9405e 100644 --- a/pkg/cmd/attestation/verification/mock_verifier.go +++ b/pkg/cmd/attestation/verification/mock_verifier.go @@ -6,6 +6,7 @@ import ( "github.com/cli/cli/v2/pkg/cmd/attestation/api" "github.com/cli/cli/v2/pkg/cmd/attestation/test/data" + "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" in_toto "github.com/in-toto/attestation/go/v1" @@ -13,10 +14,15 @@ import ( ) type MockSigstoreVerifier struct { - t *testing.T + t *testing.T + mockResults []*AttestationProcessingResult } -func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { +func (v *MockSigstoreVerifier) Verify([]*api.Attestation, verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { + if v.mockResults != nil { + return v.mockResults, nil + } + statement := &in_toto.Statement{} statement.PredicateType = SLSAPredicateV1 @@ -44,12 +50,51 @@ func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve return results, nil } -func NewMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { - return &MockSigstoreVerifier{t} +func NewMockSigstoreVerifier(t *testing.T, mockResults []*AttestationProcessingResult) *MockSigstoreVerifier { + return &MockSigstoreVerifier{t, mockResults} +} + +func NewDefaultMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { + result := BuildDefaultMockResult(t) + results := []*AttestationProcessingResult{&result} + return &MockSigstoreVerifier{t, results} } type FailSigstoreVerifier struct{} -func (v *FailSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { +func (v *FailSigstoreVerifier) Verify([]*api.Attestation, verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { return nil, fmt.Errorf("failed to verify attestations") } + +func BuildMockResult(b *bundle.Bundle, buildSignerURI, sourceRepoOwnerURI, sourceRepoURI, issuer string) AttestationProcessingResult { + statement := &in_toto.Statement{} + statement.PredicateType = SLSAPredicateV1 + + return AttestationProcessingResult{ + Attestation: &api.Attestation{ + Bundle: b, + }, + VerificationResult: &verify.VerificationResult{ + Statement: statement, + Signature: &verify.SignatureVerificationResult{ + Certificate: &certificate.Summary{ + Extensions: certificate.Extensions{ + BuildSignerURI: buildSignerURI, + SourceRepositoryOwnerURI: sourceRepoOwnerURI, + SourceRepositoryURI: sourceRepoURI, + Issuer: issuer, + }, + }, + }, + }, + } +} + +func BuildDefaultMockResult(t *testing.T) AttestationProcessingResult { + bundle := data.SigstoreBundle(t) + buildSignerURI := "https://github.com/github/example/.github/workflows/release.yml@refs/heads/main" + sourceRepoOwnerURI := "https://github.com/sigstore" + sourceRepoURI := "https://github.com/sigstore/sigstore-js" + issuer := "https://token.actions.githubusercontent.com" + return BuildMockResult(bundle, buildSignerURI, sourceRepoOwnerURI, sourceRepoURI, issuer) +} diff --git a/pkg/cmd/attestation/verification/sigstore.go b/pkg/cmd/attestation/verification/sigstore.go index 66005d62e..da4a45e2e 100644 --- a/pkg/cmd/attestation/verification/sigstore.go +++ b/pkg/cmd/attestation/verification/sigstore.go @@ -180,7 +180,7 @@ func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verif // if verification fails, create the error and exit verification early if err != nil { v.config.Logger.VerbosePrint(v.config.Logger.ColorScheme.Redf( - "Failed to verify against issuer \"%s\" \n\n", issuer, + "Failed to verify against issuer \"%s\": %v\n\n", issuer, err, )) return nil, fmt.Errorf("verifying with issuer \"%s\"", issuer) diff --git a/pkg/cmd/attestation/verify/attestation.go b/pkg/cmd/attestation/verify/attestation.go index add6f80fe..bb96c9526 100644 --- a/pkg/cmd/attestation/verify/attestation.go +++ b/pkg/cmd/attestation/verify/attestation.go @@ -7,7 +7,6 @@ import ( "github.com/cli/cli/v2/pkg/cmd/attestation/api" "github.com/cli/cli/v2/pkg/cmd/attestation/artifact" "github.com/cli/cli/v2/pkg/cmd/attestation/verification" - "github.com/sigstore/sigstore-go/pkg/verify" ) func getAttestations(o *Options, a artifact.DigestedArtifact) ([]*api.Attestation, string, error) { @@ -50,18 +49,24 @@ func getAttestations(o *Options, a artifact.DigestedArtifact) ([]*api.Attestatio return attestations, msg, nil } -func verifyAttestations(attestations []*api.Attestation, sgVerifier verification.SigstoreVerifier, sgPolicy verify.PolicyBuilder, ec verification.EnforcementCriteria) ([]*verification.AttestationProcessingResult, string, error) { - sigstoreVerified, err := sgVerifier.Verify(attestations, sgPolicy) +func verifyAttestations(art artifact.DigestedArtifact, att []*api.Attestation, sgVerifier verification.SigstoreVerifier, ec verification.EnforcementCriteria) ([]*verification.AttestationProcessingResult, string, error) { + sgPolicy, err := buildSigstoreVerifyPolicy(ec, art) if err != nil { - errMsg := "✗ Sigstore verification failed" - return nil, errMsg, err + logMsg := "✗ Failed to build Sigstore verification policy" + return nil, logMsg, err + } + + sigstoreVerified, err := sgVerifier.Verify(att, sgPolicy) + if err != nil { + logMsg := "✗ Sigstore verification failed" + return nil, logMsg, err } // Verify extensions certExtVerified, err := verification.VerifyCertExtensions(sigstoreVerified, ec) if err != nil { - errMsg := "✗ Policy verification failed" - return nil, errMsg, err + logMsg := "✗ Policy verification failed" + return nil, logMsg, err } return certExtVerified, "", nil diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 480368519..bd358355e 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -24,10 +24,9 @@ func getAttestationsFor(t *testing.T, bundlePath string) []*api.Attestation { } func TestVerifyAttestations(t *testing.T) { - config := verification.SigstoreConfig{ + sgVerifier := verification.NewLiveSigstoreVerifier(verification.SigstoreConfig{ Logger: io.NewTestHandler(), - } - sgVerifier := verification.NewLiveSigstoreVerifier(config) + }) certSummary := certificate.Summary{} certSummary.SourceRepositoryOwnerURI = "https://github.com/sigstore" @@ -39,17 +38,16 @@ func TestVerifyAttestations(t *testing.T) { PredicateType: verification.SLSAPredicateV1, SANRegex: "^https://github.com/sigstore/", } + require.NoError(t, ec.Valid()) artifactPath := test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz") a, err := artifact.NewDigestedArtifact(nil, artifactPath, "sha512") require.NoError(t, err) - sp, err := buildSigstoreVerifyPolicy(ec, *a) - t.Run("all attestations pass verification", func(t *testing.T) { attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") require.Len(t, attestations, 2) - results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + results, errMsg, err := verifyAttestations(*a, attestations, sgVerifier, ec) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) @@ -61,7 +59,7 @@ func TestVerifyAttestations(t *testing.T) { attestations = append(attestations, invalidBundle[0]) require.Len(t, attestations, 3) - results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + results, errMsg, err := verifyAttestations(*a, attestations, sgVerifier, ec) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) @@ -73,40 +71,27 @@ func TestVerifyAttestations(t *testing.T) { attestations := append(invalidBundle, invalidBundle2...) require.Len(t, attestations, 2) - results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, ec) + results, errMsg, err := verifyAttestations(*a, attestations, sgVerifier, ec) require.Error(t, err) require.Contains(t, errMsg, "✗ Sigstore verification failed") require.Nil(t, results) }) t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { - customArtifactPath := test.NormalizeRelativePath("../test/data/reusable-workflow-artifact") - customArtifact, err := artifact.NewDigestedArtifact(nil, customArtifactPath, "sha256") - require.NoError(t, err) - - mlDemoAttestAttestation := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") - aareusableAttestation := getAttestationsFor(t, "../test/data/gh-artifact-attestations-workflow-bundle.json") - attestations := []*api.Attestation{mlDemoAttestAttestation[0], aareusableAttestation[0], mlDemoAttestAttestation[0]} + sgjAttestation := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") + reusableWorkflowAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") + attestations := []*api.Attestation{sgjAttestation[0], reusableWorkflowAttestations[0], sgjAttestation[1]} require.Len(t, attestations, 3) - certSummary := certificate.Summary{} - certSummary.SourceRepositoryOwnerURI = "https://github.com/malancas" - certSummary.Issuer = verification.GitHubOIDCIssuer + rwfResult := verification.BuildMockResult(reusableWorkflowAttestations[0].Bundle, "", "https://github.com/malancas", "", verification.GitHubOIDCIssuer) + sgjResult := verification.BuildDefaultMockResult(t) + mockResults := []*verification.AttestationProcessingResult{&sgjResult, &rwfResult, &sgjResult} - customEc := verification.EnforcementCriteria{ - Certificate: certSummary, - PredicateType: verification.SLSAPredicateV1, - SANRegex: "^https://github.com/github/artifact-attestations-workflows/", - } - esp, err := buildSigstoreVerifyPolicy(customEc, *customArtifact) - - results, errMsg, err := verifyAttestations(attestations, sgVerifier, esp, customEc) + mockSgVerifier := verification.NewMockSigstoreVerifier(t, mockResults) + results, errMsg, err := verifyAttestations(*a, attestations, mockSgVerifier, ec) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) - for _, r := range results { - require.NotEqual(t, r.Attestation.Bundle.String(), aareusableAttestation[0].Bundle.String()) - } }) t.Run("fails verification when cert extension verification fails", func(t *testing.T) { @@ -116,7 +101,7 @@ func TestVerifyAttestations(t *testing.T) { expectedCriteria := ec expectedCriteria.Certificate.SourceRepositoryOwnerURI = "https://github.com/wrong" - results, errMsg, err := verifyAttestations(attestations, sgVerifier, sp, expectedCriteria) + results, errMsg, err := verifyAttestations(*a, attestations, sgVerifier, expectedCriteria) require.Error(t, err) require.Contains(t, errMsg, "✗ Policy verification failed") require.Nil(t, results) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 4efc23f44..5b31371ff 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -244,13 +244,7 @@ func runVerify(opts *Options) error { opts.Logger.VerbosePrintf("Verifying attestations with predicate type: %s\n", ec.PredicateType) - sp, err := buildSigstoreVerifyPolicy(ec, *artifact) - if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to build Sigstore verification policy")) - return err - } - - verified, errMsg, err := verifyAttestations(attestations, opts.SigstoreVerifier, sp, ec) + verified, errMsg, err := verifyAttestations(*artifact, attestations, opts.SigstoreVerifier, ec) if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red(errMsg)) return err diff --git a/pkg/cmd/attestation/verify/verify_test.go b/pkg/cmd/attestation/verify/verify_test.go index 9a2e9f18c..75fd18c44 100644 --- a/pkg/cmd/attestation/verify/verify_test.go +++ b/pkg/cmd/attestation/verify/verify_test.go @@ -75,7 +75,7 @@ func TestNewVerifyCmd(t *testing.T) { OIDCIssuer: verification.GitHubOIDCIssuer, Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -92,7 +92,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -109,7 +109,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://foo.ghe.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -126,7 +126,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -143,7 +143,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -159,7 +159,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -175,7 +175,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, Repo: "sigstore/sigstore-js", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -191,7 +191,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -207,7 +207,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -223,7 +223,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -240,7 +240,7 @@ func TestNewVerifyCmd(t *testing.T) { PredicateType: verification.SLSAPredicateV1, SAN: "https://github.com/sigstore/", SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -257,7 +257,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -274,7 +274,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: "https://spdx.dev/Document/v2.3", SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -365,7 +365,7 @@ func TestJSONOutput(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), exporter: cmdutil.NewJSONExporter(), } require.NoError(t, runVerify(&opts)) @@ -389,7 +389,7 @@ func TestRunVerify(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "^https://github.com/sigstore/", - SigstoreVerifier: verification.NewMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), } t.Run("with valid artifact and bundle", func(t *testing.T) { From 7a271b008a06df8d62e60ae8e6e32f42c803303e Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 08:58:23 -0700 Subject: [PATCH 15/20] undo change Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verification/sigstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verification/sigstore.go b/pkg/cmd/attestation/verification/sigstore.go index da4a45e2e..66005d62e 100644 --- a/pkg/cmd/attestation/verification/sigstore.go +++ b/pkg/cmd/attestation/verification/sigstore.go @@ -180,7 +180,7 @@ func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verif // if verification fails, create the error and exit verification early if err != nil { v.config.Logger.VerbosePrint(v.config.Logger.ColorScheme.Redf( - "Failed to verify against issuer \"%s\": %v\n\n", issuer, err, + "Failed to verify against issuer \"%s\" \n\n", issuer, )) return nil, fmt.Errorf("verifying with issuer \"%s\"", issuer) From 28565dc1f83f1e8c7f409faf90b8cfbcc64ee2ce Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 08:58:55 -0700 Subject: [PATCH 16/20] remove unused test file Signed-off-by: Meredith Lancaster --- ...artifact-attestations-workflow-bundle.json | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json diff --git a/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json b/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json deleted file mode 100644 index ca17f4101..000000000 --- a/pkg/cmd/attestation/test/data/gh-artifact-attestations-workflow-bundle.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", - "verificationMaterial": { - "tlogEntries": [ - { - "logIndex": "129027678", - "logId": { - "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" - }, - "kindVersion": { - "kind": "dsse", - "version": "0.0.1" - }, - "integratedTime": "1725924650", - "inclusionPromise": { - "signedEntryTimestamp": "MEQCICZHyKBtXwvRDYWke7Pp0KfrBL8YNLoDHGP7lKFgkjGbAiB6V+IfSg/SYV/ySXOUg+t/Wp8wGjXMkKWnzPT9DWNsZA==" - }, - "inclusionProof": { - "logIndex": "7123416", - "rootHash": "LZ7MbkKAnq4sU1EMcWwJWWhw778o2w5vO6+Op/DFwm4=", - "treeSize": "7123418", - "hashes": [ - "pJMeUZ4R2LEEjf0JSmsoXTfHMgBAah4aOPBdXEa9sxo=", - "4f/Xq7IIiBbWQbSZZLkEBCSuOIbUXSvzYfxTIqh9rjY=", - "gy7fFADBSd5e37RIZy86inqhsnYB29bTUxY6/EtlJDk=", - "8JY4XnAVf8weXfLSChGSEbqVSN7FKSapmaM5Xi+qowM=", - "FKlHhO4TMH2pnrZUvSKA7Drig5MbABFy2KZx4esRxJY=", - "T+Ziyo74JC0j3MWEgjiiGuTCQ0w+AzLft+r9OyRldMw=", - "naRDgCL1Ch0MNzrXrAmrV1PLa/Bi5HV5GqrqlUceCVI=", - "c4TDdYxGB0ihJtrnXDSynGSSI83D5WVHvZJxuMti4Xg=", - "bcPqJfBdq24AxJvo1LAJKwcudDBLIIyVclqFzJW5TEY=", - "7Dvc6Q8qiduX8Oka5vLLU5oWAmybo8oaecNXPgkOQvA=", - "LAdu5Ynz/wk2uMNazU0CVickvA3YhhBz6TpIl5brTko=", - "uRsmea7eVXshBNN6huh/owmfaAy9Rx4Cq2M2vFb2Ntk=", - "NeHKGVl6KVXfx3+wnQrIrxra4Pr9Fa7YDpTlf86mlTc=" - ], - "checkpoint": { - "envelope": "rekor.sigstore.dev - 1193050959916656506\n7123418\nLZ7MbkKAnq4sU1EMcWwJWWhw778o2w5vO6+Op/DFwm4=\n\n— rekor.sigstore.dev wNI9ajBGAiEAy5505Eq4vOyji5LLmRbaN4/eqwOlVpgLOnozcVCJWvoCIQD+/CNPBY1eyyNypq25OIDwTVIHVxroif3cf2MsfEfplw==\n" - } - }, - "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiYzk2YzgzYjE5NTAwOTJmOWEzYjc4NWE4ZWFlM2Y4MDY4YzcwYWQyZWJlZTFhNTRhYmIxZmJmYzk0ZTg1YzA3NSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImZmZTQ0OTYwMWI0ZmJjODQ4ODE0MzZkMzhlZjE4ZGRhNGI4NWM4YWY0NGI3YzM3ZmQwZTRhNGZhZmI0MGM2MzgifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRQ0FPQmRJT3M2MnR1TzQybDhBWGNyK2t5ampDa1VqMTZRZUlwWkpkUjM4SWdJZ0x3YWlLdlg0RU9IclEvVFczOFQ4UTJiRzBvYkZ2b3NDNUlqYTFMbDlJNU09IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VoWmFrTkRRblZsWjBGM1NVSkJaMGxWWVRkNWJUSktNa1kyYVd0c1ZtdE1jMGhTUzNaVU9YTnZiR3BWZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwUmQwOVVRVFZOYWsxNlRVUlZkMWRvWTA1TmFsRjNUMVJCTlUxcVRUQk5SRlYzVjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVlFjekpOVmpNeU9HRkdRM2gyVWxKelFuQTNkR2R2WVRVcmJuUTFTbmR0TkdrM2Frc0tNR0ZUTUZFM2JVMDFZblpZZUZwdmRFdGtLMWxwTmpSMEswaFZOaXN4WnpWVlJIZzVNRFJJVjFCSlRUQkROemhwVUdGUFEwSm5XWGRuWjFsRFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVm1iMmg2Q2s1bFRXMUNkMW9yUm5rMFN6aE5aU3MwVlc5aVkxcEpkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMlZuV1VSV1VqQlNRVkZJTDBKSVFYZGliMXB6WVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREprY0dSSGFERlphVGxvWTI1U2NBcGFiVVpxWkVNeGFHUklVbXhqTTFKb1pFZHNkbUp1VFhSa01qbDVZVEphYzJJelpIcE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1tUnZDbGt6U1hSalNGWnBZa2RzZW1GRE5UVmlWM2hCWTIxV2JXTjVPVzlhVjBaclkzazVkRmxYYkhWTlJHdEhRMmx6UjBGUlVVSm5OemgzUVZGRlJVc3lhREFLWkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpCaFZ6bDFZM2sxYm1GWVVtOWtWMG94WXpKV2VWa3lPWFZrUjFaMVpFTTFhbUl5TUhkSWQxbExTM2RaUWdwQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWMySXpaR1phUjJ4NlkwZEdNRmt5WjNkT1oxbExTM2RaUWtKQlIwUjJla0ZDUVhkUmIxa3lUVE5QUjAwMUNsa3lVVEZOUjAweldXMUdhbHBVV21wTk1rcHNUWHBhYVZsdFVYbE5iVmw1V21wR2FVNVhXVFJOVkdkNVdXcEJlVUpuYjNKQ1owVkZRVmxQTDAxQlJVVUtRa05TUW1SSVVteGpNMUZuVTFjeGFGb3lWV2RMUldSSlNVVk9kbUp1VW1oaFZ6VnNZMmxDVTFwWFpIQmpNMUo1WlZOcmQwNUJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FsRlJiVm95YkRCaFNGWnBUREpHZVdSSGJHMVpWMDR3VEZkR01HUkhWbnBrUjBZd1lWYzVkV041TVROaU0wcHlXbTE0ZG1RelRYZElVVmxMQ2t0M1dVSkNRVWRFZG5wQlFrSm5VVkJqYlZadFkzazViMXBYUm10amVUbDBXVmRzZFUxRWMwZERhWE5IUVZGUlFtYzNPSGRCVVdkRlRGRjNjbUZJVWpBS1kwaE5Oa3g1T1RCaU1uUnNZbWsxYUZrelVuQmlNalY2VEcxa2NHUkhhREZaYmxaNldsaEthbUl5TlRCYVZ6VXdURzFPZG1KVVFqaENaMjl5UW1kRlJRcEJXVTh2VFVGRlNrSkhORTFpUjJnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemx1WVZoU2IyUlhTWFpaV0Vvd1lWZGFhRmt6VVhSWldGSXdDbHBZVGpCWldGSndZakkxZWt4WVpIWmpiWFJ0WWtjNU0yTjVPSFZhTW13d1lVaFdhVXd6WkhaamJYUnRZa2M1TTJONU9XNWhSMDU1VEZoQ01WbHRlSEFLWXpKbmRXVlhNWE5SU0Vwc1dtNU5kbUZIVm1oYVNFMTJZbGRHY0dKcVFUUkNaMjl5UW1kRlJVRlpUeTlOUVVWTFFrTnZUVXRIVG1wT2VtaHFUMWRPYXdwT1ZFSnFUakpLYUZreVZUSlplazVwV2xSTk1sbHRTbXROYWtwdFRXMVplRmxxVm0xUFJFVTBUVzFKZDBoUldVdExkMWxDUWtGSFJIWjZRVUpEZDFGUUNrUkJNVzVoV0ZKdlpGZEpkR0ZIT1hwa1IxWnJUVVZyUjBOcGMwZEJVVkZDWnpjNGQwRlJkMFZQZDNjMVlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVUtXVEk1ZEV3eVpIQmtSMmd4V1drNWFHTnVVbkJhYlVacVpFTXhhR1JJVW14ak0xSm9aRWRzZG1KdVRYUmtNamw1WVRKYWMySXpaSHBOUkdkSFEybHpSd3BCVVZGQ1p6YzRkMEZSTUVWTFozZHZXVEpOTTA5SFRUVlpNbEV4VFVkTk0xbHRSbXBhVkZwcVRUSktiRTE2V21sWmJWRjVUVzFaZVZwcVJtbE9WMWswQ2sxVVozbFpha0ZtUW1kdmNrSm5SVVZCV1U4dlRVRkZUMEpDUlUxRU0wcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRVnBDWjI5eVFtZEZSVUZaVHk4S1RVRkZVRUpCYzAxRFZHZDNUa1JSTWs5RVJYbFBWRUZ3UW1kdmNrSm5SVVZCV1U4dlRVRkZVVUpDYzAxSFYyZ3daRWhDZWs5cE9IWmFNbXd3WVVoV2FRcE1iVTUyWWxNNWJtRllVbTlrVjBsM1JrRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVZEVRVkUxVDFSRk5VMUlkMGREYVhOSFFWRlJRbWMzT0hkQlVrbEZDbUpuZUhOaFNGSXdZMGhOTmt4NU9XNWhXRkp2WkZkSmRWa3lPWFJNTW1Sd1pFZG9NVmxwT1doamJsSndXbTFHYW1SRE1XaGtTRkpzWXpOU2FHUkhiSFlLWW01TmRHUXlPWGxoTWxwellqTmtla3g1Tlc1aFdGSnZaRmRKZG1ReU9YbGhNbHB6WWpOa2Vrd3laRzlaTTBsMFkwaFdhV0pIYkhwaFF6VTFZbGQ0UVFwamJWWnRZM2s1YjFwWFJtdGplVGwwV1Zkc2RVMUVaMGREYVhOSFFWRlJRbWMzT0hkQlVrMUZTMmQzYjFreVRUTlBSMDAxV1RKUk1VMUhUVE5aYlVacUNscFVXbXBOTWtwc1RYcGFhVmx0VVhsTmJWbDVXbXBHYVU1WFdUUk5WR2Q1V1dwQmFFSm5iM0pDWjBWRlFWbFBMMDFCUlZWQ1FrMU5SVmhrZG1OdGRHMEtZa2M1TTFneVVuQmpNMEpvWkVkT2IwMUhNRWREYVhOSFFWRlJRbWMzT0hkQlVsVkZXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkQXBNTW1Sd1pFZG9NVmxwT1doamJsSndXbTFHYW1SRE1XaGtTRkpzWXpOU2FHUkhiSFppYmsxMFpESTVlV0V5V25OaU0yUjZUREpHYW1SSGJIWmliazEyQ21OdVZuVmplVGg0VFVSak5FMXFhM2hPVkVWNFRsTTVhR1JJVW14aVdFSXdZM2s0ZUUxQ1dVZERhWE5IUVZGUlFtYzNPSGRCVWxsRlEwRjNSMk5JVm1rS1lrZHNhazFKUjBwQ1oyOXlRbWRGUlVGa1dqVkJaMUZEUWtoelJXVlJRak5CU0ZWQk0xUXdkMkZ6WWtoRlZFcHFSMUkwWTIxWFl6TkJjVXBMV0hKcVpRcFFTek12YURSd2VXZERPSEEzYnpSQlFVRkhVakpUUVhaQlVVRkJRa0ZOUVZKcVFrVkJhVUp6UTBKUlZtTlBOVmxrZDBsQ01VNXBSVVJxU1V0aFJXdG5DblpvUm1OQ1pGWTJVbE5wTjFNM05qVlBaMGxuWWtoeldXMTVUVGRuU2xWaWFtMXNSRFIxT0M5cVdtWjFNbEZrZGpKYU1rWmFVVzQ1YUVGUFkyNWtOSGNLUTJkWlNVdHZXa2w2YWpCRlFYZE5SR0ZSUVhkYVowbDRRVWx2Wmsxd2FFMU9RbU5ZYVhGRE0wNXZZWFpWYm14d04wOVBlSE5ZTkhoUVUzcGhZbWRtVFFwc2N6Z3JiazVuTUhsRWEwSnVaazlQZVZwblZVcHFieXNyWjBsNFFVcDZOek5qTlZnMFZERm5PRmx4YWs1VVNtMUZjWG8yYW5WbFlYcEJTVzVZYTA5TUNsSmFlV0ZCYVc1RFNsVnVRVVEwVUU1SVpucEZWV0UwTDFKTGRrazNRVDA5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn1dfX0=" - } - ], - "timestampVerificationData": { - }, - "certificate": { - "rawBytes": "MIIHYjCCBuegAwIBAgIUa7ym2J2F6iklVkLsHRKvT9soljUwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjQwOTA5MjMzMDUwWhcNMjQwOTA5MjM0MDUwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPs2MV328aFCxvRRsBp7tgoa5+nt5Jwm4i7jK0aS0Q7mM5bvXxZotKd+Yi64t+HU6+1g5UDx904HWPIM0C78iPaOCBgYwggYCMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUfohzNeMmBwZ+Fy4K8Me+4UobcZIwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wegYDVR0RAQH/BHAwboZsaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzLy5naXRodWIvd29ya2Zsb3dzL2doY3ItcHVibGlzaC55bWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAyBgorBgEEAYO/MAEEBCRBdHRlc3QgSW1hZ2UgKEdIIENvbnRhaW5lciBSZWdpc3RyeSkwNAYKKwYBBAGDvzABBQQmZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MwHQYKKwYBBAGDvzABBgQPcmVmcy9oZWFkcy9tYWluMDsGCisGAQQBg78wAQgELQwraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTB8BgorBgEEAYO/MAEJBG4MbGh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93cy8uZ2l0aHViL3dvcmtmbG93cy9naGNyLXB1Ymxpc2gueW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAEKBCoMKGNjNzhjOWNkNTBjN2JhY2U2YzNiZTM2YmJkMjJmMmYxYjVmODE4MmIwHQYKKwYBBAGDvzABCwQPDA1naXRodWItaG9zdGVkMEkGCisGAQQBg78wAQwEOww5aHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzMDgGCisGAQQBg78wAQ0EKgwoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAZBgorBgEEAYO/MAEPBAsMCTgwNDQ2ODEyOTApBgorBgEEAYO/MAEQBBsMGWh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIwFAYKKwYBBAGDvzABEQQGDAQ5OTE5MHwGCisGAQQBg78wARIEbgxsaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzLy5naXRodWIvd29ya2Zsb3dzL2doY3ItcHVibGlzaC55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYjAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rpc3BhdGNoMG0GCisGAQQBg78wARUEXwxdaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzL2FjdGlvbnMvcnVucy8xMDc4MjkxNTExNS9hdHRlbXB0cy8xMBYGCisGAQQBg78wARYECAwGcHVibGljMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGR2SAvAQAABAMARjBEAiBsCBQVcO5YdwIB1NiEDjIKaEkgvhFcBdV6RSi7S765OgIgbHsYmyM7gJUbjmlD4u8/jZfu2Qdv2Z2FZQn9hAOcnd4wCgYIKoZIzj0EAwMDaQAwZgIxAIofMphMNBcXiqC3NoavUnlp7OOxsX4xPSzabgfMls8+nNg0yDkBnfOOyZgUJjo++gIxAJz73c5X4T1g8YqjNTJmEqz6jueazAInXkOLRZyaAinCJUnAD4PNHfzEUa4/RKvI7A==" - } - }, - "dsseEnvelope": { - "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93cyIsImRpZ2VzdCI6eyJzaGEyNTYiOiIwNmU2ODE4MzI0Y2IyMGUxZWE2ZjIyZGE3ZmI5MjUzMjNmYjU3MjUxYzk4NzY4Yzg4YjkzODcyYjUwNTkwZjkyIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MSIsInByZWRpY2F0ZSI6eyJidWlsZERlZmluaXRpb24iOnsiYnVpbGRUeXBlIjoiaHR0cHM6Ly9hY3Rpb25zLmdpdGh1Yi5pby9idWlsZHR5cGVzL3dvcmtmbG93L3YxIiwiZXh0ZXJuYWxQYXJhbWV0ZXJzIjp7IndvcmtmbG93Ijp7InJlZiI6InJlZnMvaGVhZHMvbWFpbiIsInJlcG9zaXRvcnkiOiJodHRwczovL2dpdGh1Yi5jb20vZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MiLCJwYXRoIjoiLmdpdGh1Yi93b3JrZmxvd3MvZ2hjci1wdWJsaXNoLnltbCJ9fSwiaW50ZXJuYWxQYXJhbWV0ZXJzIjp7ImdpdGh1YiI6eyJldmVudF9uYW1lIjoid29ya2Zsb3dfZGlzcGF0Y2giLCJyZXBvc2l0b3J5X2lkIjoiODA0NDY4MTI5IiwicmVwb3NpdG9yeV9vd25lcl9pZCI6Ijk5MTkiLCJydW5uZXJfZW52aXJvbm1lbnQiOiJnaXRodWItaG9zdGVkIn19LCJyZXNvbHZlZERlcGVuZGVuY2llcyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9naXRodWIvYXJ0aWZhY3QtYXR0ZXN0YXRpb25zLXdvcmtmbG93c0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsiZ2l0Q29tbWl0IjoiY2M3OGM5Y2Q1MGM3YmFjZTZjM2JlMzZiYmQyMmYyZjFiNWY4MTgyYiJ9fV19LCJydW5EZXRhaWxzIjp7ImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vZ2l0aHViL2FydGlmYWN0LWF0dGVzdGF0aW9ucy13b3JrZmxvd3MvLmdpdGh1Yi93b3JrZmxvd3MvZ2hjci1wdWJsaXNoLnltbEByZWZzL2hlYWRzL21haW4ifSwibWV0YWRhdGEiOnsiaW52b2NhdGlvbklkIjoiaHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9hcnRpZmFjdC1hdHRlc3RhdGlvbnMtd29ya2Zsb3dzL2FjdGlvbnMvcnVucy8xMDc4MjkxNTExNS9hdHRlbXB0cy8xIn19fX0=", - "payloadType": "application/vnd.in-toto+json", - "signatures": [ - { - "sig": "MEUCIQCAOBdIOs62tuO42l8AXcr+kyjjCkUj16QeIpZJdR38IgIgLwaiKvX4EOHrQ/TW38T8Q2bG0obFvosC5Ija1Ll9I5M=" - } - ] - } -} \ No newline at end of file From 2d41225dd557abfc8c4bfa89937146aaff1dc56e Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 09:11:18 -0700 Subject: [PATCH 17/20] pr feedback Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verification/extensions.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/attestation/verification/extensions.go b/pkg/cmd/attestation/verification/extensions.go index 4f70a1c2e..cc21d5ea0 100644 --- a/pkg/cmd/attestation/verification/extensions.go +++ b/pkg/cmd/attestation/verification/extensions.go @@ -18,8 +18,7 @@ func VerifyCertExtensions(results []*AttestationProcessingResult, ec Enforcement return nil, errors.New("no attestations processing results") } - verified := make([]*AttestationProcessingResult, len(results)) - var verifyCount int + verified := make([]*AttestationProcessingResult, 0, len(results)) var lastErr error for _, attestation := range results { if err := verifyCertExtensions(*attestation.VerificationResult.Signature.Certificate, ec.Certificate); err != nil { @@ -28,20 +27,16 @@ func VerifyCertExtensions(results []*AttestationProcessingResult, ec Enforcement continue } // otherwise, add the result to the results slice and increment verifyCount - verified[verifyCount] = attestation - verifyCount++ + verified = append(verified, attestation) } // if we have exited the for loop without verifying any attestations, // return the last error found - if verifyCount == 0 { + if len(verified) == 0 { return nil, lastErr } - // truncate the verified slice to only include verified attestations - verified = verified[:verifyCount] return verified, nil - } func verifyCertExtensions(given, expected certificate.Summary) error { From fed3c8142c92c81a8f666b312e65f1da4aac53b7 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 15:20:44 -0700 Subject: [PATCH 18/20] Update pkg/cmd/attestation/verify/attestation_integration_test.go Co-authored-by: Phill MV --- pkg/cmd/attestation/verify/attestation_integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index bd358355e..64d02984d 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -77,7 +77,7 @@ func TestVerifyAttestations(t *testing.T) { require.Nil(t, results) }) - t.Run("passes verification with 2/3 attestations passing cert extension verification", func(t *testing.T) { + t.Run("attestations fail to verify when cert extensions don't match enforcement criteria", func(t *testing.T) { sgjAttestation := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl") reusableWorkflowAttestations := getAttestationsFor(t, "../test/data/reusable-workflow-attestation.sigstore.json") attestations := []*api.Attestation{sgjAttestation[0], reusableWorkflowAttestations[0], sgjAttestation[1]} From 0fd09eb5ffe42fa780979f134ab9e42fa41e2a2d Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 15:30:41 -0700 Subject: [PATCH 19/20] pr feedback Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verification/extensions.go | 1 + .../attestation/verify/attestation_integration_test.go | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verification/extensions.go b/pkg/cmd/attestation/verification/extensions.go index cc21d5ea0..2958408d0 100644 --- a/pkg/cmd/attestation/verification/extensions.go +++ b/pkg/cmd/attestation/verification/extensions.go @@ -13,6 +13,7 @@ var ( GitHubTenantOIDCIssuer = "https://token.actions.%s.ghe.com" ) +// VerifyCertExtensions allows us to perform case insensitive comparisons of certificate extensions func VerifyCertExtensions(results []*AttestationProcessingResult, ec EnforcementCriteria) ([]*AttestationProcessingResult, error) { if len(results) == 0 { return nil, errors.New("no attestations processing results") diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index 64d02984d..f07fb2c1e 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -86,12 +86,20 @@ func TestVerifyAttestations(t *testing.T) { rwfResult := verification.BuildMockResult(reusableWorkflowAttestations[0].Bundle, "", "https://github.com/malancas", "", verification.GitHubOIDCIssuer) sgjResult := verification.BuildDefaultMockResult(t) mockResults := []*verification.AttestationProcessingResult{&sgjResult, &rwfResult, &sgjResult} - mockSgVerifier := verification.NewMockSigstoreVerifier(t, mockResults) + + // we want to test that attestations that pass Sigstore verification but fail + // cert extension verification are filtered out properly in the second step + // in verifyAttestations. By using a mock Sigstore verifier, we can ensure + // that the call to verification.VerifyCertExtensions in verifyAttestations + // is filtering out attestations as expected results, errMsg, err := verifyAttestations(*a, attestations, mockSgVerifier, ec) require.NoError(t, err) require.Zero(t, errMsg) require.Len(t, results, 2) + for _, result := range results { + require.NotEqual(t, result.Attestation.Bundle, reusableWorkflowAttestations[0].Bundle) + } }) t.Run("fails verification when cert extension verification fails", func(t *testing.T) { From f92d7035541bfa62eb33488adc047ce81215b1d3 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 21 Nov 2024 15:40:15 -0700 Subject: [PATCH 20/20] pr feedback Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/inspect/inspect_test.go | 14 ++++----- .../attestation/verification/mock_verifier.go | 15 +++++----- .../verify/attestation_integration_test.go | 4 +-- pkg/cmd/attestation/verify/verify_test.go | 30 +++++++++---------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/pkg/cmd/attestation/inspect/inspect_test.go b/pkg/cmd/attestation/inspect/inspect_test.go index 29ca9aec7..368cc54f5 100644 --- a/pkg/cmd/attestation/inspect/inspect_test.go +++ b/pkg/cmd/attestation/inspect/inspect_test.go @@ -58,7 +58,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha384", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -70,7 +70,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -82,7 +82,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha512", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -93,7 +93,7 @@ func TestNewInspectCmd(t *testing.T) { ArtifactPath: artifactPath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -105,7 +105,7 @@ func TestNewInspectCmd(t *testing.T) { BundlePath: bundlePath, DigestAlgorithm: "sha256", OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -148,7 +148,7 @@ func TestRunInspect(t *testing.T) { DigestAlgorithm: "sha512", Logger: io.NewTestHandler(), OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), } t.Run("with valid artifact and bundle", func(t *testing.T) { @@ -176,7 +176,7 @@ func TestJSONOutput(t *testing.T) { DigestAlgorithm: "sha512", Logger: io.NewHandler(testIO), OCIClient: oci.MockClient{}, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), exporter: cmdutil.NewJSONExporter(), } require.Nil(t, runInspect(&opts)) diff --git a/pkg/cmd/attestation/verification/mock_verifier.go b/pkg/cmd/attestation/verification/mock_verifier.go index 44ed9405e..be828e66f 100644 --- a/pkg/cmd/attestation/verification/mock_verifier.go +++ b/pkg/cmd/attestation/verification/mock_verifier.go @@ -50,14 +50,15 @@ func (v *MockSigstoreVerifier) Verify([]*api.Attestation, verify.PolicyBuilder) return results, nil } -func NewMockSigstoreVerifier(t *testing.T, mockResults []*AttestationProcessingResult) *MockSigstoreVerifier { - return &MockSigstoreVerifier{t, mockResults} +func NewMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { + result := BuildSigstoreJsMockResult(t) + results := []*AttestationProcessingResult{&result} + + return &MockSigstoreVerifier{t, results} } -func NewDefaultMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { - result := BuildDefaultMockResult(t) - results := []*AttestationProcessingResult{&result} - return &MockSigstoreVerifier{t, results} +func NewMockSigstoreVerifierWithMockResults(t *testing.T, mockResults []*AttestationProcessingResult) *MockSigstoreVerifier { + return &MockSigstoreVerifier{t, mockResults} } type FailSigstoreVerifier struct{} @@ -90,7 +91,7 @@ func BuildMockResult(b *bundle.Bundle, buildSignerURI, sourceRepoOwnerURI, sourc } } -func BuildDefaultMockResult(t *testing.T) AttestationProcessingResult { +func BuildSigstoreJsMockResult(t *testing.T) AttestationProcessingResult { bundle := data.SigstoreBundle(t) buildSignerURI := "https://github.com/github/example/.github/workflows/release.yml@refs/heads/main" sourceRepoOwnerURI := "https://github.com/sigstore" diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index f07fb2c1e..caa02281f 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -84,9 +84,9 @@ func TestVerifyAttestations(t *testing.T) { require.Len(t, attestations, 3) rwfResult := verification.BuildMockResult(reusableWorkflowAttestations[0].Bundle, "", "https://github.com/malancas", "", verification.GitHubOIDCIssuer) - sgjResult := verification.BuildDefaultMockResult(t) + sgjResult := verification.BuildSigstoreJsMockResult(t) mockResults := []*verification.AttestationProcessingResult{&sgjResult, &rwfResult, &sgjResult} - mockSgVerifier := verification.NewMockSigstoreVerifier(t, mockResults) + mockSgVerifier := verification.NewMockSigstoreVerifierWithMockResults(t, mockResults) // we want to test that attestations that pass Sigstore verification but fail // cert extension verification are filtered out properly in the second step diff --git a/pkg/cmd/attestation/verify/verify_test.go b/pkg/cmd/attestation/verify/verify_test.go index 75fd18c44..9a2e9f18c 100644 --- a/pkg/cmd/attestation/verify/verify_test.go +++ b/pkg/cmd/attestation/verify/verify_test.go @@ -75,7 +75,7 @@ func TestNewVerifyCmd(t *testing.T) { OIDCIssuer: verification.GitHubOIDCIssuer, Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -92,7 +92,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -109,7 +109,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://foo.ghe.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -126,7 +126,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -143,7 +143,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -159,7 +159,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -175,7 +175,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, Repo: "sigstore/sigstore-js", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -191,7 +191,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -207,7 +207,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: false, }, @@ -223,7 +223,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -240,7 +240,7 @@ func TestNewVerifyCmd(t *testing.T) { PredicateType: verification.SLSAPredicateV1, SAN: "https://github.com/sigstore/", SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsErr: true, }, @@ -257,7 +257,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -274,7 +274,7 @@ func TestNewVerifyCmd(t *testing.T) { Owner: "sigstore", PredicateType: "https://spdx.dev/Document/v2.3", SANRegex: "(?i)^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), }, wantsExporter: true, }, @@ -365,7 +365,7 @@ func TestJSONOutput(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), exporter: cmdutil.NewJSONExporter(), } require.NoError(t, runVerify(&opts)) @@ -389,7 +389,7 @@ func TestRunVerify(t *testing.T) { Owner: "sigstore", PredicateType: verification.SLSAPredicateV1, SANRegex: "^https://github.com/sigstore/", - SigstoreVerifier: verification.NewDefaultMockSigstoreVerifier(t), + SigstoreVerifier: verification.NewMockSigstoreVerifier(t), } t.Run("with valid artifact and bundle", func(t *testing.T) {