From 40e8f3e79cba30f7e7f91246b07625de85239c4b Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Tue, 28 May 2024 12:59:47 -0600 Subject: [PATCH] add signer-repo and signer-workflow flags Signed-off-by: Meredith Lancaster --- pkg/cmd/attestation/verify/options.go | 6 ++- pkg/cmd/attestation/verify/policy.go | 19 +++++----- pkg/cmd/attestation/verify/verify.go | 5 +++ .../verify/verify_integration_test.go | 38 +++++++++++++++++++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/attestation/verify/options.go b/pkg/cmd/attestation/verify/options.go index bfa03f16e72..6fd6849a687 100644 --- a/pkg/cmd/attestation/verify/options.go +++ b/pkg/cmd/attestation/verify/options.go @@ -27,6 +27,8 @@ type Options struct { Repo string SAN string SANRegex string + SignerRepo string + SignerWorkflow string APIClient api.Client Logger *io.Handler OCIClient oci.Client @@ -51,12 +53,12 @@ func (opts *Options) SetPolicyFlags() { // to Owner opts.Owner = splitRepo[0] - if opts.SAN == "" && opts.SANRegex == "" { + if opts.SAN == "" && opts.SANRegex == "" && opts.SignerRepo == "" && opts.SignerWorkflow == "" { opts.SANRegex = expandToGitHubURL(opts.Repo) } return } - if opts.SAN == "" && opts.SANRegex == "" { + if opts.SAN == "" && opts.SANRegex == "" && opts.SignerRepo == "" && opts.SignerWorkflow == "" { opts.SANRegex = expandToGitHubURL(opts.Owner) } } diff --git a/pkg/cmd/attestation/verify/policy.go b/pkg/cmd/attestation/verify/policy.go index 34a56294b90..e18b9fef039 100644 --- a/pkg/cmd/attestation/verify/policy.go +++ b/pkg/cmd/attestation/verify/policy.go @@ -16,16 +16,17 @@ const ( GitHubRunner = "github-hosted" ) -func buildSANMatcher(san, sanRegex string) (verify.SubjectAlternativeNameMatcher, error) { - if san == "" && sanRegex == "" { - return verify.SubjectAlternativeNameMatcher{}, nil +func buildSANMatcher(opts *Options) (verify.SubjectAlternativeNameMatcher, error) { + if opts.SignerRepo != "" { + signedRepoRegex := expandToGitHubURL(opts.SignerRepo) + return verify.NewSANMatcher(opts.SignerWorkflow, "", signedRepoRegex) + } else if opts.SignerWorkflow != "" { + return verify.NewSANMatcher(opts.SignerWorkflow, "", "") + } else if opts.SAN != "" || opts.SANRegex != "" { + return verify.NewSANMatcher(opts.SAN, "", opts.SANRegex) } - sanMatcher, err := verify.NewSANMatcher(san, "", sanRegex) - if err != nil { - return verify.SubjectAlternativeNameMatcher{}, err - } - return sanMatcher, nil + return verify.SubjectAlternativeNameMatcher{}, nil } func buildCertExtensions(opts *Options, runnerEnv string) certificate.Extensions { @@ -43,7 +44,7 @@ func buildCertExtensions(opts *Options, runnerEnv string) certificate.Extensions } func buildCertificateIdentityOption(opts *Options, runnerEnv string) (verify.PolicyOption, error) { - sanMatcher, err := buildSANMatcher(opts.SAN, opts.SANRegex) + sanMatcher, err := buildSANMatcher(opts) if err != nil { return nil, err } diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 182a857228d..dac56cdef59 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -149,6 +149,11 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command verifyCmd.Flags().StringVarP(&opts.SAN, "cert-identity", "", "", "Enforce that the certificate's subject alternative name matches the provided value exactly") verifyCmd.Flags().StringVarP(&opts.SANRegex, "cert-identity-regex", "i", "", "Enforce that the certificate's subject alternative name matches the provided regex") verifyCmd.MarkFlagsMutuallyExclusive("cert-identity", "cert-identity-regex") + verifyCmd.Flags().StringVarP(&opts.SignerRepo, "signer-repo", "", "", "Enforce that the attestation signer workflow originated from a given repository") + verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Enforce that the attestation signer workflow originated from a given workflow") + verifyCmd.MarkFlagsMutuallyExclusive("signer-repo", "signer-workflow") + verifyCmd.MarkFlagsMutuallyExclusive("signer-repo", "cert-identity-regex") + verifyCmd.MarkFlagsMutuallyExclusive("signer-workflow", "cert-identity") verifyCmd.Flags().StringVarP(&opts.OIDCIssuer, "cert-oidc-issuer", "", GitHubOIDCIssuer, "Issuer of the OIDC token") return verifyCmd diff --git a/pkg/cmd/attestation/verify/verify_integration_test.go b/pkg/cmd/attestation/verify/verify_integration_test.go index 66e8d8dc60f..7a5e589410b 100644 --- a/pkg/cmd/attestation/verify/verify_integration_test.go +++ b/pkg/cmd/attestation/verify/verify_integration_test.go @@ -119,6 +119,15 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) { require.NoError(t, err) }) + t.Run("with owner and valid reusable signer workflow", func(t *testing.T) { + opts := baseOpts + opts.Owner = "malancas" + opts.SignerWorkflow = "https://github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml@09b495c3f12c7881b3cc17209a327792065c1a1d" + + err := runVerify(&opts) + require.NoError(t, err) + }) + t.Run("with owner and valid reusable workflow SAN regex", func(t *testing.T) { opts := baseOpts opts.Owner = "malancas" @@ -128,6 +137,15 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) { require.NoError(t, err) }) + t.Run("with owner and valid reusable signer repo", func(t *testing.T) { + opts := baseOpts + opts.Owner = "malancas" + opts.SignerRepo = "github/artifact-attestations-workflows" + + err := runVerify(&opts) + require.NoError(t, err) + }) + t.Run("with repo and valid reusable workflow SAN", func(t *testing.T) { opts := baseOpts opts.Owner = "malancas" @@ -138,6 +156,16 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) { require.NoError(t, err) }) + t.Run("with repo and valid reusable signer workflow", func(t *testing.T) { + opts := baseOpts + opts.Owner = "malancas" + opts.Repo = "malancas/attest-demo" + opts.SignerWorkflow = "https://github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml@09b495c3f12c7881b3cc17209a327792065c1a1d" + + err := runVerify(&opts) + require.NoError(t, err) + }) + t.Run("with repo and valid reusable workflow SAN regex", func(t *testing.T) { opts := baseOpts opts.Owner = "malancas" @@ -147,4 +175,14 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) { err := runVerify(&opts) require.NoError(t, err) }) + + t.Run("with repo and valid reusable signer repo", func(t *testing.T) { + opts := baseOpts + opts.Owner = "malancas" + opts.Repo = "malancas/attest-demo" + opts.SignerRepo = "github/artifact-attestations-workflows" + + err := runVerify(&opts) + require.NoError(t, err) + }) }