Merge pull request #9825 from malancas/verify-provenance-predicate-by-default
`gh attestation verify` should only verify provenance attestations by default
This commit is contained in:
commit
1652051fc2
6 changed files with 76 additions and 37 deletions
|
|
@ -15,6 +15,8 @@ import (
|
|||
"github.com/sigstore/sigstore-go/pkg/bundle"
|
||||
)
|
||||
|
||||
const SLSAPredicateV1 = "https://slsa.dev/provenance/v1"
|
||||
|
||||
var ErrUnrecognisedBundleExtension = errors.New("bundle file extension not supported, must be json or jsonl")
|
||||
var ErrEmptyBundleFile = errors.New("provided bundle file is empty")
|
||||
|
||||
|
|
|
|||
|
|
@ -12,15 +12,13 @@ import (
|
|||
"github.com/sigstore/sigstore-go/pkg/verify"
|
||||
)
|
||||
|
||||
const SLSAPredicateType = "https://slsa.dev/provenance/v1"
|
||||
|
||||
type MockSigstoreVerifier struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) *SigstoreResults {
|
||||
statement := &in_toto.Statement{}
|
||||
statement.PredicateType = SLSAPredicateType
|
||||
statement.PredicateType = SLSAPredicateV1
|
||||
|
||||
result := AttestationProcessingResult{
|
||||
Attestation: &api.Attestation{
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func (v *LiveSigstoreVerifier) chooseVerifier(b *bundle.Bundle) (*verify.SignedE
|
|||
// issuer. We *must* use the trusted root provided.
|
||||
if issuer == PublicGoodIssuerOrg {
|
||||
if v.config.NoPublicGood {
|
||||
return nil, "", fmt.Errorf("Detected public good instance but requested verification without public good instance")
|
||||
return nil, "", fmt.Errorf("detected public good instance but requested verification without public good instance")
|
||||
}
|
||||
verifier, err := newPublicGoodVerifierWithTrustedRoot(trustedRoot)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -52,10 +52,15 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command
|
|||
The %[1]s--owner%[1]s flag value must match the name of the GitHub organization
|
||||
that the artifact's linked repository belongs to.
|
||||
|
||||
By default, the verify command will attempt to fetch attestations associated
|
||||
with the provided artifact from the GitHub API. If you would prefer to verify
|
||||
the artifact using attestations stored on disk (c.f. the %[1]sdownload%[1]s command),
|
||||
provide a path to the %[1]s--bundle%[1]s flag.
|
||||
By default, the verify command will:
|
||||
- only verify provenance attestations
|
||||
- attempt to fetch relevant attestations via the GitHub API.
|
||||
|
||||
To verify other types of attestations, use the %[1]s--predicate-type%[1]s flag.
|
||||
|
||||
To use your artifact's OCI registry instead of GitHub's API, use the
|
||||
%[1]s--bundle-from-oci%[1]s flag. For offline verification, using attestations
|
||||
stored on desk (c.f. the download command), provide a path to the %[1]s--bundle%[1]s flag.
|
||||
|
||||
To see the full results that are generated upon successful verification, i.e.
|
||||
for use with a policy engine, provide the %[1]s--format=json%[1]s flag.
|
||||
|
|
@ -179,7 +184,7 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command
|
|||
verifyCmd.Flags().StringVarP(&opts.Repo, "repo", "R", "", "Repository name in the format <owner>/<repo>")
|
||||
verifyCmd.MarkFlagsMutuallyExclusive("owner", "repo")
|
||||
verifyCmd.MarkFlagsOneRequired("owner", "repo")
|
||||
verifyCmd.Flags().StringVarP(&opts.PredicateType, "predicate-type", "", "", "Filter attestations by provided predicate type")
|
||||
verifyCmd.Flags().StringVarP(&opts.PredicateType, "predicate-type", "", verification.SLSAPredicateV1, "Filter attestations by provided predicate type")
|
||||
verifyCmd.Flags().BoolVarP(&opts.NoPublicGood, "no-public-good", "", false, "Do not verify attestations signed with Sigstore public good instance")
|
||||
verifyCmd.Flags().StringVarP(&opts.TrustedRoot, "custom-trusted-root", "", "", "Path to a trusted_root.jsonl file; likely for offline verification")
|
||||
verifyCmd.Flags().IntVarP(&opts.Limit, "limit", "L", api.DefaultLimit, "Maximum number of attestations to fetch")
|
||||
|
|
@ -244,16 +249,12 @@ func runVerify(opts *Options) error {
|
|||
}
|
||||
|
||||
// Apply predicate type filter to returned attestations
|
||||
if opts.PredicateType != "" {
|
||||
filteredAttestations := verification.FilterAttestations(opts.PredicateType, attestations)
|
||||
|
||||
if len(filteredAttestations) == 0 {
|
||||
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found with predicate type: %s\n"), opts.PredicateType)
|
||||
return err
|
||||
}
|
||||
|
||||
attestations = filteredAttestations
|
||||
filteredAttestations := verification.FilterAttestations(opts.PredicateType, attestations)
|
||||
if len(filteredAttestations) == 0 {
|
||||
opts.Logger.Printf(opts.Logger.ColorScheme.Red("✗ No attestations found with predicate type: %s\n"), opts.PredicateType)
|
||||
return err
|
||||
}
|
||||
attestations = filteredAttestations
|
||||
|
||||
policy, err := buildVerifyPolicy(opts, *artifact)
|
||||
if err != nil {
|
||||
|
|
@ -261,6 +262,8 @@ func runVerify(opts *Options) error {
|
|||
return err
|
||||
}
|
||||
|
||||
opts.Logger.VerbosePrintf("Verifying attestations with predicate type: %s\n", opts.PredicateType)
|
||||
|
||||
sigstoreRes := opts.SigstoreVerifier.Verify(attestations, policy)
|
||||
if sigstoreRes.Error != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
OCIClient: oci.NewLiveClient(),
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewLiveSigstoreVerifier(sigstoreConfig),
|
||||
}
|
||||
|
|
@ -139,6 +140,7 @@ func TestVerifyIntegrationCustomIssuer(t *testing.T) {
|
|||
Logger: logger,
|
||||
OCIClient: oci.NewLiveClient(),
|
||||
OIDCIssuer: "https://token.actions.githubusercontent.com/hammer-time",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SigstoreVerifier: verification.NewLiveSigstoreVerifier(sigstoreConfig),
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +210,7 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) {
|
|||
Logger: logger,
|
||||
OCIClient: oci.NewLiveClient(),
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SigstoreVerifier: verification.NewLiveSigstoreVerifier(sigstoreConfig),
|
||||
}
|
||||
|
||||
|
|
@ -298,6 +301,7 @@ func TestVerifyIntegrationReusableWorkflowSignerWorkflow(t *testing.T) {
|
|||
OCIClient: oci.NewLiveClient(),
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "malancas",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
Repo: "malancas/attest-demo",
|
||||
SigstoreVerifier: verification.NewLiveSigstoreVerifier(sigstoreConfig),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,11 +70,12 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
||||
DigestAlgorithm: "sha384",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -85,12 +86,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: false,
|
||||
},
|
||||
|
|
@ -101,12 +103,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "foo.ghe.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://foo.ghe.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "foo.ghe.com",
|
||||
},
|
||||
wantsErr: false,
|
||||
},
|
||||
|
|
@ -117,12 +120,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "foo.ghe.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "foo.ghe.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -133,12 +137,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
||||
DigestAlgorithm: "sha512",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: false,
|
||||
},
|
||||
|
|
@ -148,12 +153,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 30,
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -163,12 +169,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
Repo: "sigstore/sigstore-js",
|
||||
Limit: 30,
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -178,12 +185,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: false,
|
||||
},
|
||||
|
|
@ -193,12 +201,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 101,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 101,
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: false,
|
||||
},
|
||||
|
|
@ -208,12 +217,13 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 0,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 0,
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -223,13 +233,14 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SAN: "https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
Hostname: "github.com",
|
||||
},
|
||||
wantsErr: true,
|
||||
},
|
||||
|
|
@ -240,12 +251,30 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
ArtifactPath: artifactPath,
|
||||
BundlePath: bundlePath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsExporter: true,
|
||||
},
|
||||
{
|
||||
name: "Use specified predicate type",
|
||||
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --predicate-type https://spdx.dev/Document/v2.3 --format json", artifactPath, bundlePath),
|
||||
wants: Options{
|
||||
ArtifactPath: artifactPath,
|
||||
BundlePath: bundlePath,
|
||||
DigestAlgorithm: "sha256",
|
||||
Hostname: "github.com",
|
||||
Limit: 30,
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: "https://spdx.dev/Document/v2.3",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsExporter: true,
|
||||
},
|
||||
|
|
@ -273,17 +302,18 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
|
||||
assert.Equal(t, tc.wants.ArtifactPath, opts.ArtifactPath)
|
||||
assert.Equal(t, tc.wants.BundlePath, opts.BundlePath)
|
||||
assert.Equal(t, tc.wants.TrustedRoot, opts.TrustedRoot)
|
||||
assert.Equal(t, tc.wants.DenySelfHostedRunner, opts.DenySelfHostedRunner)
|
||||
assert.Equal(t, tc.wants.DigestAlgorithm, opts.DigestAlgorithm)
|
||||
assert.Equal(t, tc.wants.Hostname, opts.Hostname)
|
||||
assert.Equal(t, tc.wants.Limit, opts.Limit)
|
||||
assert.Equal(t, tc.wants.NoPublicGood, opts.NoPublicGood)
|
||||
assert.Equal(t, tc.wants.OIDCIssuer, opts.OIDCIssuer)
|
||||
assert.Equal(t, tc.wants.Owner, opts.Owner)
|
||||
assert.Equal(t, tc.wants.PredicateType, opts.PredicateType)
|
||||
assert.Equal(t, tc.wants.Repo, opts.Repo)
|
||||
assert.Equal(t, tc.wants.SAN, opts.SAN)
|
||||
assert.Equal(t, tc.wants.SANRegex, opts.SANRegex)
|
||||
assert.Equal(t, tc.wants.Hostname, opts.Hostname)
|
||||
assert.Equal(t, tc.wants.TrustedRoot, opts.TrustedRoot)
|
||||
assert.NotNil(t, opts.APIClient)
|
||||
assert.NotNil(t, opts.Logger)
|
||||
assert.NotNil(t, opts.OCIClient)
|
||||
|
|
@ -333,11 +363,12 @@ func TestJSONOutput(t *testing.T) {
|
|||
OCIClient: oci.MockClient{},
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
exporter: cmdutil.NewJSONExporter(),
|
||||
}
|
||||
require.Nil(t, runVerify(&opts))
|
||||
require.NoError(t, runVerify(&opts))
|
||||
|
||||
var target []*verification.AttestationProcessingResult
|
||||
err := json.Unmarshal(out.Bytes(), &target)
|
||||
|
|
@ -356,12 +387,13 @@ func TestRunVerify(t *testing.T) {
|
|||
OCIClient: oci.MockClient{},
|
||||
OIDCIssuer: verification.GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
PredicateType: verification.SLSAPredicateV1,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
}
|
||||
|
||||
t.Run("with valid artifact and bundle", func(t *testing.T) {
|
||||
require.Nil(t, runVerify(&publicGoodOpts))
|
||||
require.NoError(t, runVerify(&publicGoodOpts))
|
||||
})
|
||||
|
||||
t.Run("with failing OCI artifact fetch", func(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue