add signer and source ref, commit options
Signed-off-by: Meredith Lancaster <malancas@github.com>
This commit is contained in:
parent
728aa3d83f
commit
313faf9cd0
3 changed files with 81 additions and 30 deletions
|
|
@ -13,8 +13,7 @@ import (
|
|||
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
||||
)
|
||||
|
||||
const hostRegex = `^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+.*$`
|
||||
const workflowURIRegex = `^https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+.*\/.github\/workflows\/[a-zA-Z0-9-]+.(yml|yaml)$`
|
||||
const workflowURIRegex = `^https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+.*\/.github\/workflows\/.*\/[a-zA-Z0-9-]+.(yml|yaml)$`
|
||||
|
||||
func expandToGitHubURL(tenant, ownerOrRepo string) string {
|
||||
if tenant == "" {
|
||||
|
|
@ -57,11 +56,17 @@ func newEnforcementCriteria(opts *Options) (verification.EnforcementCriteria, er
|
|||
signedRepoRegex := expandToGitHubURLRegex(opts.Tenant, opts.SignerRepo)
|
||||
c.SANRegex = signedRepoRegex
|
||||
} else if opts.SignerWorkflow != "" {
|
||||
validatedWorkflowRegex, err := validateSignerWorkflow(opts.Hostname, opts.SignerWorkflow)
|
||||
validatedWorkflow, err := validateSignerWorkflow(opts.Hostname, opts.SignerWorkflow)
|
||||
if err != nil {
|
||||
return verification.EnforcementCriteria{}, err
|
||||
}
|
||||
c.SANRegex = validatedWorkflowRegex
|
||||
|
||||
workflowRegex := fmt.Sprintf("^%s", validatedWorkflow)
|
||||
c.SANRegex = workflowRegex
|
||||
|
||||
if opts.SignerRef != "" {
|
||||
c.Certificate.BuildSignerURI = fmt.Sprintf("%s@%s", validatedWorkflow, opts.SignerRef)
|
||||
}
|
||||
} else if opts.Repo != "" {
|
||||
// if the user has not provided the SAN, SANRegex, SignerRepo, or SignerWorkflow options
|
||||
// then we default to the repo option
|
||||
|
|
@ -99,26 +104,11 @@ func newEnforcementCriteria(opts *Options) (verification.EnforcementCriteria, er
|
|||
c.Certificate.Issuer = opts.OIDCIssuer
|
||||
}
|
||||
|
||||
if opts.SignerDigest != "" {
|
||||
c.Certificate.BuildSignerDigest = opts.SignerDigest
|
||||
}
|
||||
|
||||
if opts.SignerRef != "" {
|
||||
// need to build the full URI value
|
||||
uri, err := getFullWorkflowURI(c.SANRegex)
|
||||
if err != nil {
|
||||
return verification.EnforcementCriteria{}, err
|
||||
}
|
||||
c.Certificate.BuildSignerURI = uri
|
||||
}
|
||||
|
||||
if opts.SourceDigest != "" {
|
||||
c.Certificate.SourceRepositoryDigest = opts.SourceDigest
|
||||
}
|
||||
|
||||
if opts.SourceRef != "" {
|
||||
c.Certificate.SourceRepositoryRef = opts.SourceRef
|
||||
}
|
||||
// set the SourceRepositoryDigest, SourceRepositoryRef, and BuildSignerDigest
|
||||
// extensions if the options are provided
|
||||
c.Certificate.BuildSignerDigest = opts.SignerDigest
|
||||
c.Certificate.SourceRepositoryDigest = opts.SourceDigest
|
||||
c.Certificate.SourceRepositoryRef = opts.SourceRef
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
|
@ -179,13 +169,13 @@ func buildSigstoreVerifyPolicy(c verification.EnforcementCriteria, a artifact.Di
|
|||
func validateSignerWorkflow(hostname, signerWorkflow string) (string, error) {
|
||||
// we expect a provided workflow argument be in the format [HOST/]/<OWNER>/<REPO>/path/to/workflow.yml
|
||||
// if the provided workflow does not contain a host, set the host
|
||||
match, err := regexp.MatchString(hostRegex, signerWorkflow)
|
||||
match, err := regexp.MatchString(workflowURIRegex, signerWorkflow)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if match {
|
||||
return fmt.Sprintf("^https://%s", signerWorkflow), nil
|
||||
return fmt.Sprintf("https://%s", signerWorkflow), nil
|
||||
}
|
||||
|
||||
// if the provided workflow did not match the expect format
|
||||
|
|
@ -194,5 +184,5 @@ func validateSignerWorkflow(hostname, signerWorkflow string) (string, error) {
|
|||
return "", errors.New("unknown host")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("^https://%s/%s", hostname, signerWorkflow), nil
|
||||
return fmt.Sprintf("https://%s/%s", hostname, signerWorkflow), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,63 @@ func TestNewEnforcementCriteria(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, "https://foo.com", c.Certificate.Issuer)
|
||||
})
|
||||
|
||||
t.Run("sets Certificate.BuildSignerURI using SignerWorkflow and SignerRef", func(t *testing.T) {
|
||||
opts := &Options{
|
||||
ArtifactPath: artifactPath,
|
||||
Owner: "wrong",
|
||||
Repo: "wrong/value",
|
||||
SignerWorkflow: "foo/bar/.github/workflows/attest.yml",
|
||||
SignerRef: "refs/heads/main",
|
||||
Hostname: "github.com",
|
||||
}
|
||||
|
||||
c, err := newEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "https://github.com/foo/bar/.github/workflows/attest.yml@refs/heads/main", c.Certificate.BuildSignerURI)
|
||||
})
|
||||
|
||||
t.Run("sets Certificate.BuildSignerDigest using opts.SignerDigest", func(t *testing.T) {
|
||||
opts := &Options{
|
||||
ArtifactPath: artifactPath,
|
||||
Owner: "wrong",
|
||||
Repo: "wrong/value",
|
||||
SignerDigest: "foo",
|
||||
Hostname: "github.com",
|
||||
}
|
||||
|
||||
c, err := newEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", c.Certificate.BuildSignerDigest)
|
||||
})
|
||||
|
||||
t.Run("sets Certificate.SourceRepositoryDigest using opts.SourceDigest", func(t *testing.T) {
|
||||
opts := &Options{
|
||||
ArtifactPath: artifactPath,
|
||||
Owner: "wrong",
|
||||
Repo: "wrong/value",
|
||||
SourceDigest: "foo",
|
||||
Hostname: "github.com",
|
||||
}
|
||||
|
||||
c, err := newEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", c.Certificate.SourceRepositoryDigest)
|
||||
})
|
||||
|
||||
t.Run("sets Certificate.SourceRepositoryRef using opts.SourceRef", func(t *testing.T) {
|
||||
opts := &Options{
|
||||
ArtifactPath: artifactPath,
|
||||
Owner: "wrong",
|
||||
Repo: "wrong/value",
|
||||
SourceRef: "refs/heads/main",
|
||||
Hostname: "github.com",
|
||||
}
|
||||
|
||||
c, err := newEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "refs/heads/main", c.Certificate.SourceRepositoryRef)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidateSignerWorkflow(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -187,14 +187,18 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command
|
|||
verifyCmd.Flags().IntVarP(&opts.Limit, "limit", "L", api.DefaultLimit, "Maximum number of attestations to fetch")
|
||||
cmdutil.AddFormatFlags(verifyCmd, &opts.exporter)
|
||||
// policy enforcement flags
|
||||
verifyCmd.Flags().BoolVarP(&opts.DenySelfHostedRunner, "deny-self-hosted-runners", "", false, "Fail verification for attestations generated on self-hosted runners")
|
||||
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.Flags().StringVarP(&opts.SignerRepo, "signer-repo", "", "", "Repository of reusable workflow that signed attestation in the format <owner>/<repo>")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Workflow that signed attestation in the format [host/]<owner>/<repo>/<path>/<to>/<workflow>")
|
||||
verifyCmd.MarkFlagsMutuallyExclusive("cert-identity", "cert-identity-regex", "signer-repo", "signer-workflow")
|
||||
verifyCmd.Flags().StringVarP(&opts.OIDCIssuer, "cert-oidc-issuer", "", verification.GitHubOIDCIssuer, "Issuer of the OIDC token")
|
||||
verifyCmd.Flags().BoolVarP(&opts.DenySelfHostedRunner, "deny-self-hosted-runners", "", false, "Fail verification for attestations generated on self-hosted runners")
|
||||
verifyCmd.Flags().StringVarP(&opts.Hostname, "hostname", "", "", "Configure host to use")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerDigest, "signer-digest", "", "", "Digest associated with the signer workflow")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerRepo, "signer-repo", "", "", "Repository of reusable workflow that signed attestation in the format <owner>/<repo>")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Workflow that signed attestation in the format [host/]<owner>/<repo>/<path>/<to>/<workflow>")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerRef, "signer-ref", "", "", "Ref associated with the signer workflow. signer-workflow must be provided to use this")
|
||||
verifyCmd.Flags().StringVarP(&opts.SourceRef, "source-ref", "", "", "Ref associated with the source workflow")
|
||||
verifyCmd.Flags().StringVarP(&opts.SourceDigest, "source-digest", "", "", "Digest associated with the source workflow")
|
||||
|
||||
return verifyCmd
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue