cli/pkg/cmd/attestation/verify/policy.go
Meredith Lancaster c9e8fd6c64
Fix attestation verify source repository check bug (#9053)
* add build source repo URI extension when repo is provided, add integration tests for this change

Signed-off-by: Meredith Lancaster <malancas@github.com>

* add initial docs on specifying cert identity

Signed-off-by: Meredith Lancaster <malancas@github.com>

* wording

Signed-off-by: Meredith Lancaster <malancas@github.com>

* add reusable workflow example

Signed-off-by: Meredith Lancaster <malancas@github.com>

* add more test cases

Signed-off-by: Meredith Lancaster <malancas@github.com>

* tweak to verify docs

---------

Signed-off-by: Meredith Lancaster <malancas@github.com>
Co-authored-by: Phill MV <phillmv@github.com>
2024-05-08 07:44:52 -06:00

95 lines
2.7 KiB
Go

package verify
import (
"fmt"
"github.com/sigstore/sigstore-go/pkg/fulcio/certificate"
"github.com/sigstore/sigstore-go/pkg/verify"
"github.com/cli/cli/v2/pkg/cmd/attestation/artifact"
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
)
const (
GitHubOIDCIssuer = "https://token.actions.githubusercontent.com"
// represents the GitHub hosted runner in the certificate RunnerEnvironment extension
GitHubRunner = "github-hosted"
)
func buildSANMatcher(san, sanRegex string) (verify.SubjectAlternativeNameMatcher, error) {
if san == "" && sanRegex == "" {
return verify.SubjectAlternativeNameMatcher{}, nil
}
sanMatcher, err := verify.NewSANMatcher(san, "", sanRegex)
if err != nil {
return verify.SubjectAlternativeNameMatcher{}, err
}
return sanMatcher, nil
}
func buildCertExtensions(opts *Options, runnerEnv string) certificate.Extensions {
extensions := certificate.Extensions{
Issuer: opts.OIDCIssuer,
SourceRepositoryOwnerURI: fmt.Sprintf("https://github.com/%s", opts.Owner),
RunnerEnvironment: runnerEnv,
}
// if opts.Repo is set, set the SourceRepositoryURI field before returning the extensions
if opts.Repo != "" {
extensions.SourceRepositoryURI = fmt.Sprintf("https://github.com/%s", opts.Repo)
}
return extensions
}
func buildCertificateIdentityOption(opts *Options, runnerEnv string) (verify.PolicyOption, error) {
sanMatcher, err := buildSANMatcher(opts.SAN, opts.SANRegex)
if err != nil {
return nil, err
}
extensions := buildCertExtensions(opts, runnerEnv)
certId, err := verify.NewCertificateIdentity(sanMatcher, extensions)
if err != nil {
return nil, err
}
return verify.WithCertificateIdentity(certId), nil
}
func buildVerifyCertIdOption(opts *Options) (verify.PolicyOption, error) {
if opts.DenySelfHostedRunner {
withGHRunner, err := buildCertificateIdentityOption(opts, GitHubRunner)
if err != nil {
return nil, err
}
return withGHRunner, nil
}
// if Extensions.RunnerEnvironment value is set to the empty string
// through the second function argument,
// no certificate matching will happen on the RunnerEnvironment field
withAnyRunner, err := buildCertificateIdentityOption(opts, "")
if err != nil {
return nil, err
}
return withAnyRunner, nil
}
func buildVerifyPolicy(opts *Options, a artifact.DigestedArtifact) (verify.PolicyBuilder, error) {
artifactDigestPolicyOption, err := verification.BuildDigestPolicyOption(a)
if err != nil {
return verify.PolicyBuilder{}, err
}
certIdOption, err := buildVerifyCertIdOption(opts)
if err != nil {
return verify.PolicyBuilder{}, err
}
policy := verify.NewPolicy(artifactDigestPolicyOption, certIdOption)
return policy, nil
}