From f3982b278cb1fc42f3a0664c03ac318df1a407ef Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Mon, 16 Dec 2024 16:57:05 -0700 Subject: [PATCH 01/18] add more verification integration tests Signed-off-by: Meredith Lancaster --- .../verify/verify-with-custom-trusted-root.sh | 22 +++++++++++++++++++ .../verify-with-internal-github-sigstore.sh | 16 ++++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 test/integration/attestation-cmd/verify/verify-with-custom-trusted-root.sh create mode 100644 test/integration/attestation-cmd/verify/verify-with-internal-github-sigstore.sh diff --git a/test/integration/attestation-cmd/verify/verify-with-custom-trusted-root.sh b/test/integration/attestation-cmd/verify/verify-with-custom-trusted-root.sh new file mode 100755 index 000000000..89a3a4556 --- /dev/null +++ b/test/integration/attestation-cmd/verify/verify-with-custom-trusted-root.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Get the root directory of the repository +rootDir="$(git rev-parse --show-toplevel)" + +ghBuildPath="$rootDir/bin/gh" + +artifactPath="$rootDir/pkg/cmd/attestation/test/data/sigstore-js-2.1.0.tgz" +bundlePath="$rootDir/pkg/cmd/attestation/test/data/sigstore-js-2.1.0_with_2_bundles.jsonl" + +# Download a custom trusted root for verification +if ! $ghBuildPath attestation trusted-root > trusted_root.jsonl; then + # cleanup test data + echo "Failed to download trusted root" + exit 1 +fi + +if ! $ghBuildPath attestation verify "$artifactPath" -b "$bundlePath" --digest-alg=sha512 --owner=sigstore --custom-trusted-root trusted_root.jsonl; then + echo "Failed to verify package with a Sigstore v0.2.0 bundle" + exit 1 +fi diff --git a/test/integration/attestation-cmd/verify/verify-with-internal-github-sigstore.sh b/test/integration/attestation-cmd/verify/verify-with-internal-github-sigstore.sh new file mode 100644 index 000000000..647a13a4c --- /dev/null +++ b/test/integration/attestation-cmd/verify/verify-with-internal-github-sigstore.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Get the root directory of the repository +rootDir="$(git rev-parse --show-toplevel)" + +ghBuildPath="$rootDir/bin/gh" + +ghCLIArtifact="$rootDir/pkg/cmd/attestation/test/data/gh_2.60.1_windows_arm64.zip" + +# Verify the gh CLI artifact +echo "Testing with package $ghCLIArtifact" +if ! $ghBuildPath attestation verify "$ghCLIArtifact" --digest-alg=sha256 --owner=cli; then + echo "Failed to verify" + exit 1 +fi From ce6150d136a048e4d235e313ebeeaf451f5dcb60 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 19 Dec 2024 07:18:01 -0700 Subject: [PATCH 02/18] simplify func params Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/policy.go | 12 ++++++------ pkg/cmd/attestation/verify/policy_test.go | 9 +-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/attestation/verify/policy.go b/pkg/cmd/attestation/verify/policy.go index 41b9ea27e..1e1dcd4d8 100644 --- a/pkg/cmd/attestation/verify/policy.go +++ b/pkg/cmd/attestation/verify/policy.go @@ -56,7 +56,7 @@ func newEnforcementCriteria(opts *Options) (verification.EnforcementCriteria, er signedRepoRegex := expandToGitHubURLRegex(opts.Tenant, opts.SignerRepo) c.SANRegex = signedRepoRegex } else if opts.SignerWorkflow != "" { - validatedWorkflowRegex, err := validateSignerWorkflow(opts) + validatedWorkflowRegex, err := validateSignerWorkflow(opts.Hostname, opts.SignerWorkflow) if err != nil { return verification.EnforcementCriteria{}, err } @@ -140,23 +140,23 @@ func buildSigstoreVerifyPolicy(c verification.EnforcementCriteria, a artifact.Di return policy, nil } -func validateSignerWorkflow(opts *Options) (string, error) { +func validateSignerWorkflow(hostname, signerWorkflow string) (string, error) { // we expect a provided workflow argument be in the format [HOST/]///path/to/workflow.yml // if the provided workflow does not contain a host, set the host - match, err := regexp.MatchString(hostRegex, opts.SignerWorkflow) + match, err := regexp.MatchString(hostRegex, signerWorkflow) if err != nil { return "", err } if match { - return fmt.Sprintf("^https://%s", opts.SignerWorkflow), nil + return fmt.Sprintf("^https://%s", signerWorkflow), nil } // if the provided workflow did not match the expect format // we move onto creating a signer workflow using the provided host name - if opts.Hostname == "" { + if hostname == "" { return "", errors.New("unknown host") } - return fmt.Sprintf("^https://%s/%s", opts.Hostname, opts.SignerWorkflow), nil + return fmt.Sprintf("^https://%s/%s", hostname, signerWorkflow), nil } diff --git a/pkg/cmd/attestation/verify/policy_test.go b/pkg/cmd/attestation/verify/policy_test.go index 30724afef..d033ba4fa 100644 --- a/pkg/cmd/attestation/verify/policy_test.go +++ b/pkg/cmd/attestation/verify/policy_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/cli/cli/v2/pkg/cmd/attestation/verification" - "github.com/cli/cli/v2/pkg/cmd/factory" "github.com/stretchr/testify/require" ) @@ -263,14 +262,8 @@ func TestValidateSignerWorkflow(t *testing.T) { } for _, tc := range testcases { - opts := &Options{ - Config: factory.New("test").Config, - SignerWorkflow: tc.providedSignerWorkflow, - } - // All host resolution is done verify.go:RunE - opts.Hostname = tc.host - workflowRegex, err := validateSignerWorkflow(opts) + workflowRegex, err := validateSignerWorkflow(tc.host, tc.providedSignerWorkflow) require.Equal(t, tc.expectedWorkflowRegex, workflowRegex) if tc.expectErr { From 73cd38ac07fd7195b9d8778995b86dce296848cb Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 19 Dec 2024 12:47:55 -0700 Subject: [PATCH 03/18] update verify output table Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 016ec1fa8..3171b1278 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -6,6 +6,7 @@ import ( "regexp" "github.com/cli/cli/v2/internal/ghinstance" + "github.com/cli/cli/v2/internal/text" "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/artifact/oci" @@ -261,7 +262,7 @@ func runVerify(opts *Options) error { return nil } - opts.Logger.Printf("%s was attested by:\n", artifact.DigestWithAlg()) + opts.Logger.Printf("%s was attested by %s with the following attributes:\n\n", artifact.DigestWithAlg(), text.Pluralize(len(verified), "attestation")) // Otherwise print the results to the terminal in a table tableContent, err := buildTableVerifyContent(opts.Tenant, verified) @@ -270,7 +271,7 @@ func runVerify(opts *Options) error { return err } - headers := []string{"repo", "predicate_type", "workflow"} + headers := []string{"signer repo", "signer workflow", "build repo", "build workflow"} if err = opts.Logger.PrintTable(headers, tableContent); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to print attestation details to table")) return err @@ -304,13 +305,13 @@ func extractAttestationDetail(tenant, builderSignerURI string) (string, string, match := orgAndRepoRegexp.FindStringSubmatch(builderSignerURI) if len(match) < 2 { - return "", "", fmt.Errorf("no match found for org and repo") + return "", "", fmt.Errorf("no match found for org and repo: %s", builderSignerURI) } orgAndRepo := match[1] match = workflowRegexp.FindStringSubmatch(builderSignerURI) if len(match) < 2 { - return "", "", fmt.Errorf("no match found for workflow") + return "", "", fmt.Errorf("no match found for workflow: %s", builderSignerURI) } workflow := match[1] @@ -327,15 +328,19 @@ func buildTableVerifyContent(tenant string, results []*verification.AttestationP return nil, fmt.Errorf("bundle missing verification result fields") } builderSignerURI := res.VerificationResult.Signature.Certificate.Extensions.BuildSignerURI - repoAndOrg, workflow, err := extractAttestationDetail(tenant, builderSignerURI) + buildRepoAndOrg, buildWorkflow, err := extractAttestationDetail(tenant, builderSignerURI) if err != nil { return nil, err } - if res.VerificationResult.Statement == nil { - return nil, fmt.Errorf("bundle missing attestation statement (bundle must originate from GitHub Artifact Attestations)") + + //sourceRepoURI := res.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryURI + buildConfigURI := res.VerificationResult.Signature.Certificate.Extensions.BuildConfigURI + sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(tenant, buildConfigURI) + if err != nil { + return nil, err } - predicateType := res.VerificationResult.Statement.PredicateType - content[i] = []string{repoAndOrg, predicateType, workflow} + + content[i] = []string{buildRepoAndOrg, buildWorkflow, sourceRepoAndOrg, sourceWorkflow} } return content, nil From 1e91828cdfd06df2211e90eea8f49982f77cc0ad Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 7 Jan 2025 13:36:50 -0700 Subject: [PATCH 04/18] remove commented out code Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 3171b1278..73987d8a2 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -333,7 +333,6 @@ func buildTableVerifyContent(tenant string, results []*verification.AttestationP return nil, err } - //sourceRepoURI := res.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryURI buildConfigURI := res.VerificationResult.Signature.Certificate.Extensions.BuildConfigURI sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(tenant, buildConfigURI) if err != nil { From 1c16e12a666e3fe90cdcc894eaf622686e97095a Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 8 Jan 2025 11:16:23 -0700 Subject: [PATCH 05/18] update test fixtures to include buildConfigURI Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verification/mock_verifier.go | 8 +++++--- .../attestation/verify/attestation_integration_test.go | 2 +- pkg/cmd/attestation/verify/verify_test.go | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/attestation/verification/mock_verifier.go b/pkg/cmd/attestation/verification/mock_verifier.go index be828e66f..84a7ce3b8 100644 --- a/pkg/cmd/attestation/verification/mock_verifier.go +++ b/pkg/cmd/attestation/verification/mock_verifier.go @@ -67,7 +67,7 @@ func (v *FailSigstoreVerifier) Verify([]*api.Attestation, verify.PolicyBuilder) return nil, fmt.Errorf("failed to verify attestations") } -func BuildMockResult(b *bundle.Bundle, buildSignerURI, sourceRepoOwnerURI, sourceRepoURI, issuer string) AttestationProcessingResult { +func BuildMockResult(b *bundle.Bundle, buildConfigURI, buildSignerURI, sourceRepoOwnerURI, sourceRepoURI, issuer string) AttestationProcessingResult { statement := &in_toto.Statement{} statement.PredicateType = SLSAPredicateV1 @@ -80,10 +80,11 @@ func BuildMockResult(b *bundle.Bundle, buildSignerURI, sourceRepoOwnerURI, sourc Signature: &verify.SignatureVerificationResult{ Certificate: &certificate.Summary{ Extensions: certificate.Extensions{ + BuildConfigURI: buildConfigURI, BuildSignerURI: buildSignerURI, + Issuer: issuer, SourceRepositoryOwnerURI: sourceRepoOwnerURI, SourceRepositoryURI: sourceRepoURI, - Issuer: issuer, }, }, }, @@ -93,9 +94,10 @@ func BuildMockResult(b *bundle.Bundle, buildSignerURI, sourceRepoOwnerURI, sourc func BuildSigstoreJsMockResult(t *testing.T) AttestationProcessingResult { bundle := data.SigstoreBundle(t) + buildConfigURI := "https://github.com/sigstore/sigstore-js/.github/workflows/build.yml@refs/heads/main" 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) + return BuildMockResult(bundle, buildConfigURI, buildSignerURI, sourceRepoOwnerURI, sourceRepoURI, issuer) } diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go index caa02281f..630eb1dc5 100644 --- a/pkg/cmd/attestation/verify/attestation_integration_test.go +++ b/pkg/cmd/attestation/verify/attestation_integration_test.go @@ -83,7 +83,7 @@ func TestVerifyAttestations(t *testing.T) { attestations := []*api.Attestation{sgjAttestation[0], reusableWorkflowAttestations[0], sgjAttestation[1]} require.Len(t, attestations, 3) - rwfResult := verification.BuildMockResult(reusableWorkflowAttestations[0].Bundle, "", "https://github.com/malancas", "", verification.GitHubOIDCIssuer) + rwfResult := verification.BuildMockResult(reusableWorkflowAttestations[0].Bundle, "", "", "https://github.com/malancas", "", verification.GitHubOIDCIssuer) sgjResult := verification.BuildSigstoreJsMockResult(t) mockResults := []*verification.AttestationProcessingResult{&sgjResult, &rwfResult, &sgjResult} mockSgVerifier := verification.NewMockSigstoreVerifierWithMockResults(t, mockResults) diff --git a/pkg/cmd/attestation/verify/verify_test.go b/pkg/cmd/attestation/verify/verify_test.go index 5e4f33507..87ffa96f0 100644 --- a/pkg/cmd/attestation/verify/verify_test.go +++ b/pkg/cmd/attestation/verify/verify_test.go @@ -415,7 +415,7 @@ func TestRunVerify(t *testing.T) { opts.BundlePath = "" opts.Owner = "sigstore" - require.Nil(t, runVerify(&opts)) + require.NoError(t, runVerify(&opts)) }) t.Run("with owner which not matches SourceRepositoryOwnerURI", func(t *testing.T) { From c2fdac272cd9769626369d7daf108dc4709e07e2 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 8 Jan 2025 13:27:38 -0700 Subject: [PATCH 06/18] update table column headers Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 73987d8a2..b0d82be3c 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -271,7 +271,7 @@ func runVerify(opts *Options) error { return err } - headers := []string{"signer repo", "signer workflow", "build repo", "build workflow"} + headers := []string{"signer repo", "signer workflow", "builder repo", "builder workflow"} if err = opts.Logger.PrintTable(headers, tableContent); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to print attestation details to table")) return err From 2ddfe865f4b41618c3005ff81d02d6c890ad3372 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Mon, 13 Jan 2025 13:02:05 -0700 Subject: [PATCH 07/18] Update pkg/cmd/attestation/verify/verify.go Co-authored-by: Phill MV --- pkg/cmd/attestation/verify/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index b0d82be3c..7b6250c0c 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -262,7 +262,7 @@ func runVerify(opts *Options) error { return nil } - opts.Logger.Printf("%s was attested by %s with the following attributes:\n\n", artifact.DigestWithAlg(), text.Pluralize(len(verified), "attestation")) + opts.Logger.Printf("The following %d %s matched the policy criteria:\n\n", len(verified), text.Pluralize(len(verified), "attestation")) // Otherwise print the results to the terminal in a table tableContent, err := buildTableVerifyContent(opts.Tenant, verified) From 2ffce8ae9fbccfa8e6f1d6d5ec7061cadc7da228 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:11:54 -0700 Subject: [PATCH 08/18] print attestation output info as bullet points instead of table Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/io/handler.go | 18 +++++++++++++++++- pkg/cmd/attestation/verify/verify.go | 25 +++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/attestation/io/handler.go b/pkg/cmd/attestation/io/handler.go index 167128a42..1e5114040 100644 --- a/pkg/cmd/attestation/io/handler.go +++ b/pkg/cmd/attestation/io/handler.go @@ -2,6 +2,7 @@ package io import ( "fmt" + "strings" "github.com/cli/cli/v2/internal/tableprinter" "github.com/cli/cli/v2/pkg/iostreams" @@ -65,10 +66,25 @@ func (h *Handler) VerbosePrintf(f string, v ...interface{}) (int, error) { if !h.debugEnabled || !h.IO.IsStdoutTTY() { return 0, nil } - return fmt.Fprintf(h.IO.ErrOut, f, v...) } +func (h *Handler) PrintBulletPoints(rows [][]string) (int, error) { + maxColLen := 0 + for _, row := range rows { + if len(row[0]) > maxColLen { + maxColLen = len(row[0]) + } + } + + info := "" + for _, row := range rows { + dots := strings.Repeat(".", maxColLen-len(row[0])) + info += fmt.Sprintf("%s:%s %s\n", row[0], dots, row[1]) + } + return fmt.Fprintln(h.IO.ErrOut, info) +} + func (h *Handler) PrintTable(headers []string, rows [][]string) error { if !h.IO.IsStdoutTTY() { return nil diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 7b6250c0c..73689a1b8 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -262,20 +262,29 @@ func runVerify(opts *Options) error { return nil } - opts.Logger.Printf("The following %d %s matched the policy criteria:\n\n", len(verified), text.Pluralize(len(verified), "attestation")) + opts.Logger.Printf("%s matched the policy criteria:\n", text.Pluralize(len(verified), "attestation")) - // Otherwise print the results to the terminal in a table - tableContent, err := buildTableVerifyContent(opts.Tenant, verified) + // Otherwise print the results to the terminal + buildConfigURI := verified[0].VerificationResult.Signature.Certificate.Extensions.BuildConfigURI + sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(opts.Tenant, buildConfigURI) if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse results")) + opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build config URI")) + return err + } + builderSignerURI := verified[0].VerificationResult.Signature.Certificate.Extensions.BuildSignerURI + signerRepoAndOrg, signerWorkflow, err := extractAttestationDetail(opts.Tenant, builderSignerURI) + if err != nil { + opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build signer URI")) return err } - headers := []string{"signer repo", "signer workflow", "builder repo", "builder workflow"} - if err = opts.Logger.PrintTable(headers, tableContent); err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to print attestation details to table")) - return err + rows := [][]string{ + []string{"- Build repo", sourceRepoAndOrg}, + []string{"- Build workflow", sourceWorkflow}, + []string{"- Signer repo", signerRepoAndOrg}, + []string{"- Signer workflow", signerWorkflow}, } + opts.Logger.PrintBulletPoints(rows) // All attestations passed verification and policy evaluation return nil From 555c9b65f56f4b7681fcaf12a14ec3544027da00 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:13:16 -0700 Subject: [PATCH 09/18] drop unused handler method Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/io/handler.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/pkg/cmd/attestation/io/handler.go b/pkg/cmd/attestation/io/handler.go index 1e5114040..3cbfc656a 100644 --- a/pkg/cmd/attestation/io/handler.go +++ b/pkg/cmd/attestation/io/handler.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/cli/cli/v2/internal/tableprinter" "github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/utils" ) @@ -84,23 +83,3 @@ func (h *Handler) PrintBulletPoints(rows [][]string) (int, error) { } return fmt.Fprintln(h.IO.ErrOut, info) } - -func (h *Handler) PrintTable(headers []string, rows [][]string) error { - if !h.IO.IsStdoutTTY() { - return nil - } - - t := tableprinter.New(h.IO, tableprinter.WithHeader(headers...)) - - for _, row := range rows { - for _, field := range row { - t.AddField(field, tableprinter.WithTruncate(nil)) - } - t.EndRow() - } - - if err := t.Render(); err != nil { - return fmt.Errorf("failed to print output: %v", err) - } - return nil -} From 6d2f71d0f4bfb4e5c78037237f5d06ae1e4b7670 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:17:49 -0700 Subject: [PATCH 10/18] delete unused function Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 73689a1b8..ff1d34b5a 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -326,30 +326,3 @@ func extractAttestationDetail(tenant, builderSignerURI string) (string, string, return orgAndRepo, workflow, nil } - -func buildTableVerifyContent(tenant string, results []*verification.AttestationProcessingResult) ([][]string, error) { - content := make([][]string, len(results)) - - for i, res := range results { - if res.VerificationResult == nil || - res.VerificationResult.Signature == nil || - res.VerificationResult.Signature.Certificate == nil { - return nil, fmt.Errorf("bundle missing verification result fields") - } - builderSignerURI := res.VerificationResult.Signature.Certificate.Extensions.BuildSignerURI - buildRepoAndOrg, buildWorkflow, err := extractAttestationDetail(tenant, builderSignerURI) - if err != nil { - return nil, err - } - - buildConfigURI := res.VerificationResult.Signature.Certificate.Extensions.BuildConfigURI - sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(tenant, buildConfigURI) - if err != nil { - return nil, err - } - - content[i] = []string{buildRepoAndOrg, buildWorkflow, sourceRepoAndOrg, sourceWorkflow} - } - - return content, nil -} From 4fb265c5970091df04e3230b7823278fdf46a0b3 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:22:05 -0700 Subject: [PATCH 11/18] formatting Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index ff1d34b5a..837fcb727 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -279,10 +279,10 @@ func runVerify(opts *Options) error { } rows := [][]string{ - []string{"- Build repo", sourceRepoAndOrg}, - []string{"- Build workflow", sourceWorkflow}, - []string{"- Signer repo", signerRepoAndOrg}, - []string{"- Signer workflow", signerWorkflow}, + {"- Build repo", sourceRepoAndOrg}, + {"- Build workflow", sourceWorkflow}, + {"- Signer repo", signerRepoAndOrg}, + {"- Signer workflow", signerWorkflow}, } opts.Logger.PrintBulletPoints(rows) From daf63543ea92c0e8e9d64303f6d9bd67817a5862 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:33:13 -0700 Subject: [PATCH 12/18] add tty check Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/io/handler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cmd/attestation/io/handler.go b/pkg/cmd/attestation/io/handler.go index 3cbfc656a..06d747bf4 100644 --- a/pkg/cmd/attestation/io/handler.go +++ b/pkg/cmd/attestation/io/handler.go @@ -69,6 +69,9 @@ func (h *Handler) VerbosePrintf(f string, v ...interface{}) (int, error) { } func (h *Handler) PrintBulletPoints(rows [][]string) (int, error) { + if !h.IO.IsStdoutTTY() { + return 0, nil + } maxColLen := 0 for _, row := range rows { if len(row[0]) > maxColLen { From 5d26f0b767a98faf3b4b0962dbb7ce50a9ca472b Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 15 Jan 2025 14:39:22 -0700 Subject: [PATCH 13/18] skip check Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 837fcb727..9c285e5c3 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -284,6 +284,7 @@ func runVerify(opts *Options) error { {"- Signer repo", signerRepoAndOrg}, {"- Signer workflow", signerWorkflow}, } + //nolint:errcheck opts.Logger.PrintBulletPoints(rows) // All attestations passed verification and policy evaluation From d4aebb1cbb3c56b173356844a6bc222c0027297e Mon Sep 17 00:00:00 2001 From: Dennis Ameling Date: Wed, 22 Jan 2025 18:06:40 +0100 Subject: [PATCH 14/18] Enable MSI building for Windows arm64 --- .github/workflows/deployment.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index a8356272e..ebafc2ec8 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -203,10 +203,8 @@ jobs: platform="x64" ;; *_arm64 ) - echo "skipping building MSI for arm64 because WiX 3.11 doesn't support it: https://github.com/wixtoolset/issues/issues/6141" >&2 - continue - #source_dir="$PWD/dist/windows_windows_arm64" - #platform="arm64" + source_dir="$PWD/dist/windows_windows_arm64" + platform="arm64" ;; * ) printf "unsupported architecture: %s\n" "$MSI_NAME" >&2 From 2ec70a5dc39e4bdbe110214d72761062f74c9b5d Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 22 Jan 2025 13:22:45 -0700 Subject: [PATCH 15/18] bold bullet point list header text Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/io/handler.go | 2 +- pkg/cmd/attestation/verify/verify.go | 43 +++++++++++++++------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/pkg/cmd/attestation/io/handler.go b/pkg/cmd/attestation/io/handler.go index 06d747bf4..579b9a948 100644 --- a/pkg/cmd/attestation/io/handler.go +++ b/pkg/cmd/attestation/io/handler.go @@ -82,7 +82,7 @@ func (h *Handler) PrintBulletPoints(rows [][]string) (int, error) { info := "" for _, row := range rows { dots := strings.Repeat(".", maxColLen-len(row[0])) - info += fmt.Sprintf("%s:%s %s\n", row[0], dots, row[1]) + info += fmt.Sprintf(h.ColorScheme.Bold("%s:%s %s\n"), h.ColorScheme.Bold(row[0]), dots, row[1]) } return fmt.Fprintln(h.IO.ErrOut, info) } diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 9c285e5c3..ea7502f00 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -262,30 +262,33 @@ func runVerify(opts *Options) error { return nil } - opts.Logger.Printf("%s matched the policy criteria:\n", text.Pluralize(len(verified), "attestation")) + opts.Logger.Printf("The following %s matched the policy criteria\n\n", text.Pluralize(len(verified), "attestation")) // Otherwise print the results to the terminal - buildConfigURI := verified[0].VerificationResult.Signature.Certificate.Extensions.BuildConfigURI - sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(opts.Tenant, buildConfigURI) - if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build config URI")) - return err - } - builderSignerURI := verified[0].VerificationResult.Signature.Certificate.Extensions.BuildSignerURI - signerRepoAndOrg, signerWorkflow, err := extractAttestationDetail(opts.Tenant, builderSignerURI) - if err != nil { - opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build signer URI")) - return err - } + for i, v := range verified { + buildConfigURI := v.VerificationResult.Signature.Certificate.Extensions.BuildConfigURI + sourceRepoAndOrg, sourceWorkflow, err := extractAttestationDetail(opts.Tenant, buildConfigURI) + if err != nil { + opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build config URI")) + return err + } + builderSignerURI := v.VerificationResult.Signature.Certificate.Extensions.BuildSignerURI + signerRepoAndOrg, signerWorkflow, err := extractAttestationDetail(opts.Tenant, builderSignerURI) + if err != nil { + opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse build signer URI")) + return err + } - rows := [][]string{ - {"- Build repo", sourceRepoAndOrg}, - {"- Build workflow", sourceWorkflow}, - {"- Signer repo", signerRepoAndOrg}, - {"- Signer workflow", signerWorkflow}, + opts.Logger.Printf("- Attestation #%d\n", i+1) + rows := [][]string{ + {" - Build repo", sourceRepoAndOrg}, + {" - Build workflow", sourceWorkflow}, + {" - Signer repo", signerRepoAndOrg}, + {" - Signer workflow", signerWorkflow}, + } + //nolint:errcheck + opts.Logger.PrintBulletPoints(rows) } - //nolint:errcheck - opts.Logger.PrintBulletPoints(rows) // All attestations passed verification and policy evaluation return nil From 5f6e720a5030e3a289d3b53fd242a7d0112da9b5 Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 22 Jan 2025 13:27:56 -0700 Subject: [PATCH 16/18] reorder policy enforcement criteria print out Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verification/policy.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/cmd/attestation/verification/policy.go b/pkg/cmd/attestation/verification/policy.go index 7bae2eff9..f2bd126d0 100644 --- a/pkg/cmd/attestation/verification/policy.go +++ b/pkg/cmd/attestation/verification/policy.go @@ -54,10 +54,7 @@ func (c EnforcementCriteria) Valid() error { func (c EnforcementCriteria) BuildPolicyInformation() string { policyAttr := make([][]string, 0, 6) - policyAttr = appendStr(policyAttr, "- OIDC Issuer must match", c.Certificate.Issuer) - if c.Certificate.RunnerEnvironment == GitHubRunner { - policyAttr = appendStr(policyAttr, "- Action workflow Runner Environment must match ", GitHubRunner) - } + policyAttr = appendStr(policyAttr, "- Predicate type must match", c.PredicateType) policyAttr = appendStr(policyAttr, "- Source Repository Owner URI must match", c.Certificate.SourceRepositoryOwnerURI) @@ -65,14 +62,17 @@ func (c EnforcementCriteria) BuildPolicyInformation() string { policyAttr = appendStr(policyAttr, "- Source Repository URI must match", c.Certificate.SourceRepositoryURI) } - policyAttr = appendStr(policyAttr, "- Predicate type must match", c.PredicateType) - if c.SAN != "" { policyAttr = appendStr(policyAttr, "- Subject Alternative Name must match", c.SAN) } else if c.SANRegex != "" { policyAttr = appendStr(policyAttr, "- Subject Alternative Name must match regex", c.SANRegex) } + policyAttr = appendStr(policyAttr, "- OIDC Issuer must match", c.Certificate.Issuer) + if c.Certificate.RunnerEnvironment == GitHubRunner { + policyAttr = appendStr(policyAttr, "- Action workflow Runner Environment must match ", GitHubRunner) + } + maxColLen := 0 for _, attr := range policyAttr { if len(attr[0]) > maxColLen { From 4134f9606e4ec7329cfc700009589c358604c1df Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Thu, 23 Jan 2025 07:47:38 -0700 Subject: [PATCH 17/18] remove bolding from bullet point output Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/io/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/io/handler.go b/pkg/cmd/attestation/io/handler.go index 579b9a948..06d747bf4 100644 --- a/pkg/cmd/attestation/io/handler.go +++ b/pkg/cmd/attestation/io/handler.go @@ -82,7 +82,7 @@ func (h *Handler) PrintBulletPoints(rows [][]string) (int, error) { info := "" for _, row := range rows { dots := strings.Repeat(".", maxColLen-len(row[0])) - info += fmt.Sprintf(h.ColorScheme.Bold("%s:%s %s\n"), h.ColorScheme.Bold(row[0]), dots, row[1]) + info += fmt.Sprintf("%s:%s %s\n", row[0], dots, row[1]) } return fmt.Fprintln(h.IO.ErrOut, info) } From fe3d18c96a28e6b2a38fdd16a0e7304ed888e386 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 14:53:49 +0000 Subject: [PATCH 18/18] Bump actions/attest-build-provenance from 2.1.0 to 2.2.0 Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/7668571508540a607bdfd90a87a560489fe372eb...520d128f165991a6c774bcb264f323e3d70747f4) --- updated-dependencies: - dependency-name: actions/attest-build-provenance dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index a8356272e..617d9d824 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -299,7 +299,7 @@ jobs: rpmsign --addsign dist/*.rpm - name: Attest release artifacts if: inputs.environment == 'production' - uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0 + uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0 with: subject-path: "dist/gh_*" - name: Run createrepo