diff --git a/cli/slsa-verifier/verify.go b/cli/slsa-verifier/verify.go index 84f6a4ee0..848367529 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -164,10 +164,6 @@ func verifyNpmPackageCmd() *cobra.Command { fmt.Fprintf(os.Stderr, "%s: --source-versioned-tag not supported\n", FAILURE) os.Exit(1) } - if cmd.Flags().Changed("print-provenance") { - fmt.Fprintf(os.Stderr, "%s: --print-provenance not supported\n", FAILURE) - os.Exit(1) - } if cmd.Flags().Changed("builder-id") { v.BuilderID = &o.BuilderID } diff --git a/docs/API-Library.md b/docs/API-Library.md new file mode 100644 index 000000000..0633d0f75 --- /dev/null +++ b/docs/API-Library.md @@ -0,0 +1,87 @@ +# Api Library + +## Verifers + +We have exported functions for using slsa-verifier within your own Go packages + +- slsa-verifier/verifiers/verifier.go + +### Npmjs + +With `VerifyNpmPackageWithSigstoreTUFClient`, you can pass in your own TUF client with custom options. +For example, use the embedded TUF root with `sigstoreTUF.DefaultOptions().WithForceCache()`. + +Example: + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + + sigstoreTUF "github.com/sigstore/sigstore-go/pkg/tuf" + options "github.com/slsa-framework/slsa-verifier/v2/options" + apiVerify "github.com/slsa-framework/slsa-verifier/v2/verifiers" + apiUtils "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" +) + +func main() { + provBytes, builderID, err := doVerify() + if err != nil { + log.Fatalf("Verifying npm package: FAILED: %w", err) + } + fmt.Fprintf(os.Stderr, "builderID: %s\n", builderID.Name()) + fmt.Fprintf(os.Stderr, "Verifying npm package: PASSED") + fmt.Printf("%s", provBytes) +} + +func doVerify() ([]byte, *apiUtils.TrustedBuilderID, error) { + packageVersion := "0.1.127" + packageName := "@ianlewis/actions-test" + builderID := "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_nodejs_slsa3.yml" + attestations, err := os.ReadFile("attestations.json") + if err != nil { + return nil, nil, fmt.Errorf("creating sigstoreTUF client: %w", err) + } + tarballHash := "ab786dbef723164a605e55ff0ebe83f8e879159bd411980d4423c9b1646b858a537b4bc4d494fc8f71195db715e5c5e9ab4b8809f8b1b399cd30ac053d180ba7" + provenanceOpts := &options.ProvenanceOpts{ + ExpectedSourceURI: "github.com/ianlewis/actions-test", + ExpectedDigest: "ab786dbef723164a605e55ff0ebe83f8e879159bd411980d4423c9b1646b858a537b4bc4d494fc8f71195db715e5c5e9ab4b8809f8b1b399cd30ac053d180ba7", + ExpectedPackageName: &packageName, + ExpectedPackageVersion: &packageVersion, + } + builderOpts := &options.BuilderOpts{ + ExpectedID: &builderID, + } + + // example: all the default ClientOpts + // clientOpts, err := options.NewDefaultClientOpts() + + // example: get the default Sigstore TUF client that the library caches + // client, err := apiUtils.GetDefaultSigstoreTUFClient() + + // example: force using the embedded root, without going online for a refresh + // opts := sigstoreTUF.DefaultOptions().WithForceCache() + + // example: supply your own root + // opts := sigstoreTUF.DefaultOptions().WithRoot([]byte(`{"signed":{"_type":"root","spec_version":"1.0","version":9,"expires":"2024-09-12T06:53:10Z","keys":{"1e1d65ce98b10 ...`)).WithForceCache() + + // example: supply your own default Sigstore TUF client + opts := sigstoreTUF.DefaultOptions() + client, err := sigstoreTUF.New(opts) + if err != nil { + return nil, fmt.Errorf("creating SigstoreTUF client: %w", err) + } + clientOpts := options.ClientOpts{ + SigstoreTUFClient: client, + } + provBytes, outBuilderID, err := apiVerify.VerifyNpmPackage(context.Background(), attestations, tarballHash, provenanceOpts, builderOpts, clientOpts) + if err != nil { + return nil, nil, fmt.Errorf("Verifying npm package: FAILED: %w", err) + } + return provBytes, outBuilderID, nil +} +``` diff --git a/errors/errors.go b/errors/errors.go index 91ff45711..f87f451af 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -44,6 +44,7 @@ var ( ErrorInvalidHash = errors.New("invalid hash") ErrorNotPresent = errors.New("not present") ErrorInvalidPublicKey = errors.New("invalid public key") + ErrorCouldNotFindTarget = errors.New("could not get the target from the tuf root") ErrorInvalidVerificationResult = errors.New("verificationResult is not PASSED") ErrorMismatchVerifiedLevels = errors.New("verified levels do not match") ErrorMissingSubjectDigest = errors.New("missing subject digest") @@ -51,4 +52,5 @@ var ( ErrorMismatchResourceURI = errors.New("resource URI does not match") ErrorMismatchVerifierID = errors.New("verifier ID does not match") ErrorInvalidSLSALevel = errors.New("invalid SLSA level") + ErrorInvalidClientOpts = errors.New("no more than one of options.ClientOpts should be provided") ) diff --git a/options/options.go b/options/options.go index aadcb123b..5e16a38b7 100644 --- a/options/options.go +++ b/options/options.go @@ -1,6 +1,10 @@ package options -import "crypto" +import ( + "crypto" + + "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" +) // ProvenanceOpts are the options for checking provenance information. type ProvenanceOpts struct { @@ -65,3 +69,22 @@ type VerificationOpts struct { // PublicKeyHashAlgo is the hash algorithm used to compute digest that was signed. PublicKeyHashAlgo crypto.Hash } + +// ClientOpts contain clinets to be used by slsa-verifier. +// In the future, this can include a logger and a rekor client. +type ClientOpts struct { + // SigstoreTufClient is the Sigstore TUF client, used for retrieving the Npmjs public keys + SigstoreTUFClient utils.SigstoreTUFClient +} + +// NewDefaultClientOpts returns default clients to be used by slsa-verifier. +func NewDefaultClientOpts() (*ClientOpts, error) { + sigstoreTUFClient, err := utils.GetDefaultSigstoreTUFClient() + if err != nil { + return nil, err + } + opts := &ClientOpts{ + SigstoreTUFClient: sigstoreTUFClient, + } + return opts, nil +} diff --git a/register/register.go b/register/register.go index 787bb1f3c..4f3ecfdbe 100644 --- a/register/register.go +++ b/register/register.go @@ -33,6 +33,7 @@ type SLSAVerifier interface { attestations []byte, tarballHash string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, + clientOpts *options.ClientOpts, ) ([]byte, *utils.TrustedBuilderID, error) } diff --git a/verifiers/internal/gcb/verifier.go b/verifiers/internal/gcb/verifier.go index 511065009..990cf8270 100644 --- a/verifiers/internal/gcb/verifier.go +++ b/verifiers/internal/gcb/verifier.go @@ -44,6 +44,7 @@ func (v *GCBVerifier) VerifyNpmPackage(ctx context.Context, attestations []byte, tarballHash string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, + clientOpts *options.ClientOpts, ) ([]byte, *utils.TrustedBuilderID, error) { return nil, nil, serrors.ErrorNotSupported } diff --git a/verifiers/internal/gha/npm.go b/verifiers/internal/gha/npm.go index 67d25fbf8..53cc3d388 100644 --- a/verifiers/internal/gha/npm.go +++ b/verifiers/internal/gha/npm.go @@ -9,7 +9,7 @@ import ( "fmt" "net/url" "strings" - "sync/atomic" + "sync" intoto "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" @@ -42,7 +42,8 @@ var publishPredicates = map[string]bool{ publishAttestationV01: true, } var errrorInvalidAttestations = errors.New("invalid npm attestations") -var attestationKeyAtomicValue atomic.Value +var attestationKey string +var attestationKeyOnce sync.Once type attestationSet struct { Attestations []attestation `json:"attestations"` @@ -68,6 +69,7 @@ type Npm struct { verifiedPublishAtt *SignedAttestation provenanceAttestation *attestation publishAttestation *attestation + clientOpts *options.ClientOpts } func (n *Npm) ProvenanceEnvelope() *dsse.Envelope { @@ -78,12 +80,12 @@ func (n *Npm) ProvenanceLeafCertificate() *x509.Certificate { return n.verifiedProvenanceAtt.SigningCert } -func NpmNew(ctx context.Context, root *sigstoreRoot.LiveTrustedRoot, attestationBytes []byte) (*Npm, error) { +// NpmNew creates a new Npm verifier. +func NpmNew(ctx context.Context, root *sigstoreRoot.LiveTrustedRoot, attestationBytes []byte, clientOpts *options.ClientOpts) (*Npm, error) { var aSet attestationSet if err := json.Unmarshal(attestationBytes, &aSet); err != nil { return nil, fmt.Errorf("%w: json.Unmarshal: %v", errrorInvalidAttestations, err) } - prov, pub, err := extractAttestations(aSet.Attestations) if err != nil { return nil, err @@ -94,6 +96,7 @@ func NpmNew(ctx context.Context, root *sigstoreRoot.LiveTrustedRoot, attestation provenanceAttestation: prov, publishAttestation: pub, + clientOpts: clientOpts, }, nil } @@ -123,17 +126,15 @@ func extractAttestations(attestations []attestation) (*attestation, *attestation } // getAttestationKey retrieves the attestation key and holds it in memory. -func getAttestationKey(npmRegistryPublicKeyID string) (string, error) { - value := attestationKeyAtomicValue.Load() - if value != nil { - return value.(string), nil - } - npmRegistryPublicKey, err := getKeyDataFromSigstoreTuf(npmRegistryPublicKeyID, attestationKeyUsage) +func getAttestationKey(sigstoreTUFClient utils.SigstoreTUFClient, npmRegistryPublicKeyID string) (string, error) { + var err error + attestationKeyOnce.Do(func() { + attestationKey, err = getKeyDataFromSigstoreTUF(sigstoreTUFClient, npmRegistryPublicKeyID, attestationKeyUsage) + }) if err != nil { return "", err } - attestationKeyAtomicValue.Store(npmRegistryPublicKey) - return npmRegistryPublicKey, nil + return attestationKey, nil } func (n *Npm) verifyProvenanceAttestationSignature() error { @@ -159,7 +160,7 @@ func (n *Npm) verifyPublishAttestationSignature() error { // Retrieve the key material. // We found the associated public key in the TUF root, so now we can trust this KeyID. - npmRegistryPublicKey, err := getAttestationKey(npmRegistryPublicKeyID) + npmRegistryPublicKey, err := getAttestationKey(n.clientOpts.SigstoreTUFClient, npmRegistryPublicKeyID) if err != nil { return err } @@ -241,13 +242,6 @@ func verifyIntotoTypes(att *SignedAttestation, predicateTypes map[string]bool, p return nil } -func (n *Npm) verifiedProvenanceBytes() ([]byte, error) { - // TODO(#493): prune the provenance and return only - // verified fields. - // NOTE: we currently don't verify the materials' commit sha. - return []byte{}, nil -} - func (n *Npm) verifyPackageName(name *string) error { if name == nil { return nil diff --git a/verifiers/internal/gha/npm_sigstore_tuf.go b/verifiers/internal/gha/npm_sigstore_tuf.go index a3470ac20..40782a8ae 100644 --- a/verifiers/internal/gha/npm_sigstore_tuf.go +++ b/verifiers/internal/gha/npm_sigstore_tuf.go @@ -6,7 +6,8 @@ import ( "fmt" "time" - sigstoreTuf "github.com/sigstore/sigstore-go/pkg/tuf" + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" ) const ( @@ -16,7 +17,6 @@ const ( var ( errorMissingNpmjsKeyIDKeyUsage = errors.New("could not find a key with the specified 'keyId' and 'keyUsage'") - errorCouldNotFindTarget = errors.New("could not get the target from the tuf root") errorCouldNotParseKeys = errors.New("could not parse keys file content") ) @@ -38,27 +38,13 @@ type validFor struct { Start time.Time `json:"start"` } -type sigstoreTufClient interface { - GetTarget(target string) ([]byte, error) -} - -// newSigstoreTufClient gets a Sigstore TUF client, which itself is a wrapper around the official TUF client. -func newSigstoreTufClient() (*sigstoreTuf.Client, error) { - opts := sigstoreTuf.DefaultOptions() - client, err := sigstoreTuf.New(opts) - if err != nil { - return nil, fmt.Errorf("creating SigstoreTuf client: %w", err) - } - return client, nil -} - // getNpmjsKeysTarget will fetch and parse the keys.json file in Sigstore's root for npmjs // The inner TUF client will verify this "blob" is signed with correct delegate TUF roles // https://github.com/sigstore/root-signing/blob/5fd11f7ec0a993b0f20c335b33e53cfffb986b2e/repository/repository/targets/registry.npmjs.org/7a8ec9678ad824cdccaa7a6dc0961caf8f8df61bc7274189122c123446248426.keys.json#L4 -func getNpmjsKeysTarget(client sigstoreTufClient, targetPath string) (*npmjsKeysTarget, error) { +func getNpmjsKeysTarget(client utils.SigstoreTUFClient, targetPath string) (*npmjsKeysTarget, error) { blob, err := client.GetTarget(targetPath) if err != nil { - return nil, fmt.Errorf("%w: %w", errorCouldNotFindTarget, err) + return nil, fmt.Errorf("%w: %w", serrors.ErrorCouldNotFindTarget, err) } var keys npmjsKeysTarget if err := json.Unmarshal(blob, &keys); err != nil { @@ -79,18 +65,15 @@ func getKeyDataWithNpmjsKeysTarget(keys *npmjsKeysTarget, keyID, keyUsage string return "", fmt.Errorf("%w: 'keyId': %q, 'keyUsage':%q", errorMissingNpmjsKeyIDKeyUsage, keyID, keyUsage) } -// getKeyDataFromSigstoreTuf retrieves the keyfile from sigstore's TUF root, parses the file and returns the target key's material. +// getKeyDataFromSigstoreTUF retrieves the keyfile from sigstore's TUF root, parses the file and returns the target key's material. // See documentation for getNpmjsKeysTarget // // example params: // -// keyID: "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" -// keyUsage: "npm:attestations" -func getKeyDataFromSigstoreTuf(keyID, keyUsage string) (string, error) { - client, err := newSigstoreTufClient() - if err != nil { - return "", err - } +// client: sigstoreTUFClient +// keyID: "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" +// keyUsage: "npm:attestations" +func getKeyDataFromSigstoreTUF(client utils.SigstoreTUFClient, keyID, keyUsage string) (string, error) { keys, err := getNpmjsKeysTarget(client, targetPath) if err != nil { return "", err diff --git a/verifiers/internal/gha/npm_sigstore_tuf_test.go b/verifiers/internal/gha/npm_sigstore_tuf_test.go index 7f358c4a5..635657e01 100644 --- a/verifiers/internal/gha/npm_sigstore_tuf_test.go +++ b/verifiers/internal/gha/npm_sigstore_tuf_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" ) var ( @@ -82,19 +83,19 @@ var ( testTargetKeyData = testTargetKey.PublicKey.RawBytes ) -// mockSigstoreTufClient a mock implementation of sigstoreTufClient. -type mockSigstoreTufClient struct { +// mockSigstoreTUFClient a mock implementation of sigstoreTUFClient. +type mockSigstoreTUFClient struct { fileContentMap map[string]string } -// NewMockSigstoreTufClient returns an instance of the mock client, +// newMockSigstoreTUFClient returns an instance of the mock client, // with fileContentMap as input and outputs of the GetTarget() method. -func NewMockSigstoreTufClient() *mockSigstoreTufClient { - return &mockSigstoreTufClient{fileContentMap: mockFileContentMap} +func newMockSigstoreTUFClient() *mockSigstoreTUFClient { + return &mockSigstoreTUFClient{fileContentMap: mockFileContentMap} } -// GetTarget mock implementation of GetTarget for the mockSigstoreTufClient. -func (c mockSigstoreTufClient) GetTarget(targetPath string) ([]byte, error) { +// GetTarget mock implementation of GetTarget for the mockSigstoreTUFClient. +func (c mockSigstoreTUFClient) GetTarget(targetPath string) ([]byte, error) { content, exists := c.fileContentMap[targetPath] if !exists { return nil, fmt.Errorf("content not definied in this mock, key: %s", targetPath) @@ -118,7 +119,7 @@ func TestGetNpmjsKeysTarget(t *testing.T) { { name: "parsing non-existent registry.npmjs.org_keys.json", targetPath: "my-fake-path.json", - expectedErr: errorCouldNotFindTarget, + expectedErr: serrors.ErrorCouldNotFindTarget, }, { name: "parsing invalid json", @@ -128,7 +129,7 @@ func TestGetNpmjsKeysTarget(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockClient := NewMockSigstoreTufClient() + mockClient := newMockSigstoreTUFClient() actualKeys, err := getNpmjsKeysTarget(mockClient, tt.targetPath) if keyDataDiff := cmp.Diff(tt.expectedKeys, actualKeys, cmpopts.EquateComparable()); keyDataDiff != "" { t.Errorf("expected equal values (-want +got):\n%s", keyDataDiff) @@ -168,7 +169,7 @@ func TestGetKeyDataWithNpmjsKeysTarget(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockClient := NewMockSigstoreTufClient() + mockClient := newMockSigstoreTUFClient() keys, err := getNpmjsKeysTarget(mockClient, tt.targetPath) if err != nil { t.Fatalf("getNpmjsKeysTarget: %v", err) diff --git a/verifiers/internal/gha/npm_test.go b/verifiers/internal/gha/npm_test.go index 2e279c08e..3602ead8d 100644 --- a/verifiers/internal/gha/npm_test.go +++ b/verifiers/internal/gha/npm_test.go @@ -8,6 +8,8 @@ import ( "path/filepath" "testing" + "github.com/slsa-framework/slsa-verifier/v2/options" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -18,12 +20,26 @@ import ( "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" ) -var mismatchProvenancePredicates = map[string]bool{ - common.ProvenanceV02Type + "a": true, - common.ProvenanceV1Type + "a": true, -} -var mismatchPublishPredicates = map[string]bool{ - publishAttestationV01 + "a": true, +var ( + mismatchProvenancePredicates = map[string]bool{ + common.ProvenanceV02Type + "a": true, + common.ProvenanceV1Type + "a": true, + } + mismatchPublishPredicates = map[string]bool{ + publishAttestationV01 + "a": true, + } + clientOpts *options.ClientOpts +) + +// TestMain intercepts the test runner to run some setup code before running the tests. +func TestMain(m *testing.M) { + // Initialize the default ClientOpts for parallel tests + var err error + clientOpts, err = options.NewDefaultClientOpts() + if err != nil { + panic(err) + } + os.Exit(m.Run()) } func Test_verifyName(t *testing.T) { @@ -772,7 +788,7 @@ func Test_verifyPackageName(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { panic(fmt.Errorf("NpmNew: %w", err)) } @@ -855,7 +871,7 @@ func Test_verifyPublishAttestationSubjectDigest(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { panic(fmt.Errorf("NpmNew: %w", err)) } @@ -933,7 +949,7 @@ func Test_verifyPackageVersion(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { panic(fmt.Errorf("NpmNew: %w", err)) } @@ -1137,7 +1153,7 @@ func Test_verifyIntotoHeaders(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { panic(fmt.Errorf("NpmNew: %w", err)) } @@ -1206,7 +1222,7 @@ func Test_NpmNew(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - _, err = NpmNew(ctx, trustedRoot, content) + _, err = NpmNew(ctx, trustedRoot, content, clientOpts) if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" { t.Fatalf("unexpected error (-want +got): \n%s", diff) } @@ -1249,7 +1265,7 @@ func Test_verifyPublishAttestationSignature(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { t.Fatalf("unexpected error: \n%s", err) } @@ -1296,7 +1312,7 @@ func Test_verifyProvenanceAttestationSignature(t *testing.T) { panic(fmt.Errorf("os.ReadFile: %w", err)) } - npm, err := NpmNew(ctx, trustedRoot, content) + npm, err := NpmNew(ctx, trustedRoot, content, clientOpts) if err != nil { t.Fatalf("unexpected error: \n%s", err) } diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index b384df730..b74e039bf 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -323,13 +323,14 @@ func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context, attestations []byte, tarballHash string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, + clientOpts *options.ClientOpts, ) ([]byte, *utils.TrustedBuilderID, error) { trustedRoot, err := utils.GetSigstoreTrustedRoot() if err != nil { return nil, nil, err } - npm, err := NpmNew(ctx, trustedRoot, attestations) + npm, err := NpmNew(ctx, trustedRoot, attestations, clientOpts) if err != nil { return nil, nil, err } @@ -373,10 +374,11 @@ func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context, } } - prov, err := npm.verifiedProvenanceBytes() + // Extract the payload from the verified provenance. + provBytes, err := npm.ProvenanceEnvelope().DecodeB64Payload() if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("%w: %w", serrors.ErrorInvalidDssePayload, err) } - return prov, builder, nil + return provBytes, builder, nil } diff --git a/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz new file mode 100644 index 000000000..693c7fffa Binary files /dev/null and b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz differ diff --git a/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz.json b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz.json new file mode 100644 index 000000000..e64205b33 --- /dev/null +++ b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag-invalidsigprov.tgz.json @@ -0,0 +1 @@ +{"attestations":[{"predicateType":"https://github.com/npm/attestation/tree/main/specs/publish/v0.1","bundle":{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"publicKey":{"hint":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"},"tlogEntries":[{"logIndex":"20783673","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1684258381","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCl9eQtjdbH3NYhjRI2NhrP68kBYs9LCLAF3zPUA7WWeAIhAJGvcgIZRzJYRC0yaeGlKdeJ7lvTxH6nkGk5T1kuwi6v"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7ImtleWlkIjoiU0hBMjU2OmpsM2J3c3d1ODBQampva0NnaDBvMnc1YzJVNExoUUFFNTdnajljejFrekEiLCJwdWJsaWNLZXkiOiJMUzB0TFMxQ1JVZEpUaUJRVlVKTVNVTWdTMFZaTFMwdExTMEtUVVpyZDBWM1dVaExiMXBKZW1vd1EwRlJXVWxMYjFwSmVtb3dSRUZSWTBSUlowRkZNVTlzWWpONlRVRkdSbmhZUzBocFNXdFJUelZqU2pOWmFHdzFhVFpWVUhBclNXaDFkR1ZDU21KMVNHTkJOVlZ2WjB0dk1FVlhkR3hYZDFjMlMxTmhTMjlVVGtWWlREZEtiRU5SYVZadWEyaENhM1JWWjJjOVBRb3RMUzB0TFVWT1JDQlFWVUpNU1VNZ1MwVlpMUzB0TFMwPSIsInNpZyI6IlRVVlJRMGxJT1ZWUWF5OXhRUzlFV0ZOeVp6VXJhVVpCVTNWTFYxRmhRVnBwTDIxUVIwUkJhMDF4YlhCcE0zVkZRV2xDV210MWFVRkRhbFpDVTI5U04wWlVabkoxY0dkSmREQjNZbFJMWm10SGRFRlFlSFJ0U0RkaWEwZDVkejA5In1dfSwiaGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjlhMTViMmJmNTNlOTA5MjdjZmFkZjcyOTEzYTRiNWNkMDA1YmI4NjU1ZTYyZTdjZjY3NWNkYmY5MjY1NjEyYTQifSwicGF5bG9hZEhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5NjljYzgyZmZiZDcwOGY5NTkyOTdjMDk5N2U0NTdiZjY4YjhiM2JjYWM2ODNlMGE1MjdmNDg2MDNkOTBjM2Q1In19fX0="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtLyU0MHRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlc0AxLjAuNSIsImRpZ2VzdCI6eyJzaGE1MTIiOiIxZTJlYmVjZTc1NzI1MDg3NmNkZTlkMGY2YzYzNmVkNmUwMDg4YTIzYTZjNDc3ZmUwY2QxYWZjYzExODAwYTViYTBjOTMyZjRhNTdhMTI1MzcwNjNkNDlkNzE3YmI3YWU3NmI4YTI5MzhiM2Q0OGU3ZjAyNjE3ZjY1NjRhZDkxOSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2dpdGh1Yi5jb20vbnBtL2F0dGVzdGF0aW9uL3RyZWUvbWFpbi9zcGVjcy9wdWJsaXNoL3YwLjEiLCJwcmVkaWNhdGUiOnsibmFtZSI6IkB0cmlzaGFua2F0ZGF0YWRvZy9zdXByZW1lLWdvZ2dsZXMiLCJ2ZXJzaW9uIjoiMS4wLjUiLCJyZWdpc3RyeSI6Imh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnIn19","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEQCIH9UPk/qA/DXSrg5+iFASuKWQaAZi/mPGDAkMqmpi3uEAiBZkuiACjVBSoR7FTfrupgIt0wbTKfkGtAPxtmH7bkGyw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]}}},{"predicateType":"https://slsa.dev/provenance/v0.2","bundle":{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"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"},{"rawBytes":"MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="},{"rawBytes":"MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"}]},"tlogEntries":[{"logIndex":"20783663","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1684258374","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCPJ3WKZudSVvTRYl6s3WEBiWQviNfwkk/MsFI/B1rcGgIhAJs9r4sDUp/IPm+fJ0TegrQrTugkAs1pxxtIYXq705eY"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVWhHUkVORFFuQnhaMEYzU1VKQlowbFZTRXdyUWs1aVNHUlRaelJQTmpscE1UUjJWVTVWWVdoSGFGUkpkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA1VVJUSk5WR042VFdwVk1GZG9ZMDVOYWsxM1RsUkZNazFVWXpCTmFsVXdWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWYWVVNVhTMFJpWmpCd1lsWm1VMVI2VVd4TFRteFVRV1YzYWxGRlZVWTJlR0ZQVFU0S1IwSm1UVTAyUmsxMFprdGFjakIzUWt4UlQzVTRkblY0TDBFNGQwSkJURFJRWW1OcFN6VnJMM0YwWkV3eWFHMDBiVFpQUTBKaWEzZG5aMWN4VFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWbGQzaEJDblpITm14Sk9YVjVabkZKUzNJMGMxZDJhMnhSZGsxemQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQyUlJXVVJXVWpCU1FWRklMMEpIYzNkaFdWcHVZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETlNlV0ZZVG05WlZ6VnlXVmhTYXdwWldGSm9Xa2M1Ymt3elRqRmpTRXBzWWxkVmRGb3lPVzVhTW5oc1kzazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWRXTkhNSFJqU0ZacENtSkhiSHBoUXpVMVlsZDRRV050Vm0xamVUa3dXVmRrZWt3eldYaE1ha0YxVGxSQk5VSm5iM0pDWjBWRlFWbFBMMDFCUlVKQ1EzUnZaRWhTZDJONmIzWUtURE5TZG1FeVZuVk1iVVpxWkVkc2RtSnVUWFZhTW13d1lVaFdhV1JZVG14amJVNTJZbTVTYkdKdVVYVlpNamwwVFVKVlIwTnBjMGRCVVZGQ1p6YzRkd3BCVVVsRlFqTktiR0pIVm1oak1sVjNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlOZW1oc1dXMVpOVTlVVVRCT1IxVjNUWHBPYVUxdFdYaE9WRlYzQ2xsNmJHaFpWMFpzV1ZkT2EwNXFTbXROUkVwb1RWUkthVmxVUVdSQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtFNVQySXlVbXhNYlhCNlNVWkNhRmt5ZEdnS1dqSlZkMHgzV1V0TGQxbENRa0ZIUkhaNlFVSkNVVkZvWkVoS2NHTXlhR2hpYlhSb1pFZFNhR1JIUm10aU1tTjJZek5XZDJOdFZuUmFVekZ1WWpKa2JncGlSMVo2VFVJMFIwTnBjMGRCVVZGQ1p6YzRkMEZSV1VWRlNFcHNXbTVOZG1SSFJtNWplVGt5VFZNMGQweHFWWGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNDa05CVVhSRVEzUnZaRWhTZDJONmIzWk1NMUoyWVRKV2RVeHRSbXBrUjJ4MlltNU5kVm95YkRCaFNGWnBaRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFFLVFVoalIwTnBjMGRCVVZGQ1p6YzRkMEZSYTBWaFVYaHVZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETlNlV0ZZVG05WlZ6VnlXVmhTYXdwWldGSm9Xa2M1Ymt3elRqRmpTRXBzWWxkVmRGb3lPVzVhTW5oc1kzazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWRXTkhNSFJqU0ZacENtSkhiSHBoUXpVMVlsZDRRV050Vm0xamVUa3dXVmRrZWt3eldYaE1ha0YxVGxSQk5FSm5iM0pDWjBWRlFWbFBMMDFCUlV0Q1EyOU5TMFJOTkZwWFNtMEtUMVJyTUU1RVVteE5SRTE2V1dwS2JVMVVWVEZOUjAwMVdWZEdhRnBYUm1wYVJGbDVXa1JCZVZsVVJYbFpiVVYzU0ZGWlMwdDNXVUpDUVVkRWRucEJRZ3BEZDFGUVJFRXhibUZZVW05a1YwbDBZVWM1ZW1SSFZtdE5SVkZIUTJselIwRlJVVUpuTnpoM1FWRjNSVTVuZHpCaFNGSXdZMGhOTmt4NU9XNWhXRkp2Q21SWFNYVlpNamwwVEROU2VXRllUbTlaVnpWeVdWaFNhMWxZVW1oYVJ6bHVURE5PTVdOSVNteGlWMVYwV2pJNWJsb3llR3hqZWtFMFFtZHZja0puUlVVS1FWbFBMMDFCUlU1Q1EyOU5TMFJOTkZwWFNtMVBWR3N3VGtSU2JFMUVUWHBaYWtwdFRWUlZNVTFIVFRWWlYwWm9XbGRHYWxwRVdYbGFSRUY1V1ZSRmVRcFpiVVYzU1VGWlMwdDNXVUpDUVVkRWRucEJRa1JuVVZORVFrSjVXbGRhZWt3elVtaGFNMDEyWkdwRmRVMUROREZOUW10SFEybHpSMEZSVVVKbk56aDNDa0ZST0VWRGQzZEtUbXBOTWsxcWEzbE5SRUV5VFVSUlIwTnBjMGRCVVZGQ1p6YzRkMEZTUVVWS1ozZHJZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFVLV1RJNWRFd3pVbmxoV0U1dldWYzFjbGxZVW10WldGSm9Xa2M1YmsxQ1owZERhWE5IUVZGUlFtYzNPSGRCVWtWRlEyZDNTVTE2VFhoTmVrMTNUbnBOZHdwa2QxbExTM2RaUWtKQlIwUjJla0ZDUldkU2NFUkhaRzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmtTRXB3WXpKb2FHSnRkR2hrUjFKb0NtUkhSbXRpTW1OMll6TldkMk50Vm5SYVV6RnVZakprYm1KSFZucE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1qVjNZbE14ZDJSWFNuTUtZVmhPYjB4dWJIUmlSVUo1V2xkYWVrd3pVbWhhTTAxMlpHcEZkVTFETkRGTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZKTlJVdG5kMjlOZW1oc1dXMVpOUXBQVkZFd1RrZFZkMDE2VG1sTmJWbDRUbFJWZDFsNmJHaFpWMFpzV1ZkT2EwNXFTbXROUkVwb1RWUkthVmxVUVZoQ1oyOXlRbWRGUlVGWlR5OU5RVVZWQ2tKQmEwMUNNMHBzWWtkV2FHTXlWWGRhZDFsTFMzZFpRa0pCUjBSMmVrRkNSbEZTV2tSR1pHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhZS1pFaEtjR015YUdoaWJYUm9aRWRTYUdSSFJtdGlNbU4yWXpOV2QyTnRWblJhVXpGdVlqSmtibUpIVm5wTU1rWnFaRWRzZG1KdVRYWmpibFoxWTNrNE1BcFBWR3N3VDFSak1VNTZhM2RNTWtZd1pFZFdkR05JVW5wTWVrVjNaMWx6UjBOcGMwZEJVVkZDTVc1clEwSkJTVVZtVVZJM1FVaHJRV1IzUkdSUVZFSnhDbmh6WTFKTmJVMWFTR2g1V2xwNlkwTnZhM0JsZFU0ME9ISm1LMGhwYmt0QlRIbHVkV3BuUVVGQldXZHNiakJSV1VGQlFVVkJkMEpKVFVWWlEwbFJRMm9LVUdGRmR6TlJhRVpXZGxKTFJ6WXJiV1ZZV2tKU1ozZFVObEpGY1ZwMVdWUTRZVkp3ZG5aU05IWjNTV2hCVFhweWVtSnRZMGRHYUhsNFIwSkxZV3h6UVFwa05rZDFaMGhRWldoVFpXdDZaak5ZZUZoRlpHcENja1pOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpuUVUxSFZVTk5RMGRoWmxSek9WWXZVMEpuYUZkUUNtSmplWFU0VUM4dk1VTnhjREpIVjFZelVVeEJRVEpSSzBKM1NucFZVU3RoYlhndmFsRjFkM1IwYW5sU1VHNVdNa1JuU1hoQlNVSXhSRlJvY2toWVVqY0tNSEp6TkdKSFRVeHlWVFpsYVhNNWNUaHJjV2htWjBGSWJHdzBibXR3UTJKWVZqRlZSbXM1TTJSQ01VTkdiemxTU1VRMWMzVkJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUT09Iiwic2lnIjoiVFVWUlEwbERTSGhWWjJSbVJtcFhja3hvTlRsSmRsaG1PRXQyVnpNeFN6bHRVVEJsTlZWaFlUVkJiV1F2YVhoVlFXbENaekZWZG1WUWMyRTJaR0V2YWpSbWIyWlZTR1pMV1VaVFoybENUeTgwY0hKeFFuWXJhV3N3ZFdSNVp6MDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMDQ3Mjg3NjhlYjBmNTEwNzc1YTkxNzNlYmJlMDVmMzM3MTMyMDg2MTRlYmVhMjZhYWQxNjk1M2UwYWJlN2JjNiJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjNmNWQ2M2MwOTM4N2Y2OTZhNDNiMWFiNDc0YzE5NzYxODg4NGY0NTIyZjllNWVkOGZiN2YxYzgzZjU5MGZiYjYifX19fQ=="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtLyU0MHRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlc0AxLjAuNSIsImRpZ2VzdCI6eyJzaGE1MTIiOiIxZTJlYmVjZTc1NzI1MDg3NmNkZTlkMGY2YzYzNmVkNmUwMDg4YTIzYTZjNDc3ZmUwY2QxYWZjYzExODAwYTViYTBjOTMyZjRhNTdhMTI1MzcwNjNkNDlkNzE3YmI3YWU3NmI4YTI5MzhiM2Q0OGU3ZjAyNjE3ZjY1NjRhZDkxOSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInByZWRpY2F0ZSI6eyJidWlsZFR5cGUiOiJodHRwczovL2dpdGh1Yi5jb20vbnBtL2NsaS9naGEvdjIiLCJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9naXRodWIuY29tL2FjdGlvbnMvcnVubmVyIn0sImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7InVyaSI6ImdpdCtodHRwczovL2dpdGh1Yi5jb20vdHJpc2hhbmthdGRhdGFkb2cvc3VwcmVtZS1nb2dnbGVzQHJlZnMvdGFncy92MS4wLjUiLCJkaWdlc3QiOnsic2hhMSI6IjM4ZWJmOTk0NDRlMDMzYjJmMTU1MGM5YWFhZWFjZDYyZDAyYTEyYmEifSwiZW50cnlQb2ludCI6Ii5naXRodWIvd29ya2Zsb3dzL25wbS1wdWJsaXNoLnltbCJ9LCJwYXJhbWV0ZXJzIjp7fSwiZW52aXJvbm1lbnQiOnsiR0lUSFVCX0VWRU5UX05BTUUiOiJyZWxlYXNlIiwiR0lUSFVCX1JFRiI6InJlZnMvdGFncy92MS4wLjUiLCJHSVRIVUJfUkVQT1NJVE9SWSI6InRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlcyIsIkdJVEhVQl9SRVBPU0lUT1JZX0lEIjoiNjM2MjkyMDA2IiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVJfSUQiOiIzMzEzMzA3MyIsIkdJVEhVQl9SVU5fQVRURU1QVCI6IjEiLCJHSVRIVUJfUlVOX0lEIjoiNDk5NDk3NTc5MCIsIkdJVEhVQl9TSEEiOiIzOGViZjk5NDQ0ZTAzM2IyZjE1NTBjOWFhYWVhY2Q2MmQwMmExMmJhIiwiR0lUSFVCX1dPUktGTE9XX1JFRiI6InRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlcy8uZ2l0aHViL3dvcmtmbG93cy9ucG0tcHVibGlzaC55bWxAcmVmcy90YWdzL3YxLjAuNSIsIkdJVEhVQl9XT1JLRkxPV19TSEEiOiIzOGViZjk5NDQ0ZTAzM2IyZjE1NTBjOWFhYWVhY2Q2MmQwMmExMmJhIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JZCI6IjQ5OTQ5NzU3OTAtMSIsImNvbXBsZXRlbmVzcyI6eyJwYXJhbWV0ZXJzIjpmYWxzZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS90cmlzaGFua2F0ZGF0YWRvZy9zdXByZW1lLWdvZ2dsZXNAcmVmcy90YWdzL3YxLjAuNSIsImRpZ2VzdCI6eyJzaGExIjoiMzhlYmY5OTQ0NGUwMzNiMmYxNTUwYzlhYWFlYWNkNjJkMDJhMTJiYiJ9fV19fQo=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEQCICHxUgdfFjWrLh59IvXf8KvW31K9mQ0e5Uaa5Amd/ixUAiBg1UvePsa6da/j4fofUHfKYFSgiBO/4prqBv+ik0udyg==","keyid":""}]}}}]} \ No newline at end of file diff --git a/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz new file mode 100644 index 000000000..693c7fffa Binary files /dev/null and b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz differ diff --git a/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz.json b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz.json new file mode 100644 index 000000000..fbb4b7b41 --- /dev/null +++ b/verifiers/testdata/npm/gha/supreme-googles-cli-v02-tag.tgz.json @@ -0,0 +1 @@ +{"attestations":[{"predicateType":"https://github.com/npm/attestation/tree/main/specs/publish/v0.1","bundle":{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"publicKey":{"hint":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"},"tlogEntries":[{"logIndex":"20783673","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1684258381","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCl9eQtjdbH3NYhjRI2NhrP68kBYs9LCLAF3zPUA7WWeAIhAJGvcgIZRzJYRC0yaeGlKdeJ7lvTxH6nkGk5T1kuwi6v"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7ImtleWlkIjoiU0hBMjU2OmpsM2J3c3d1ODBQampva0NnaDBvMnc1YzJVNExoUUFFNTdnajljejFrekEiLCJwdWJsaWNLZXkiOiJMUzB0TFMxQ1JVZEpUaUJRVlVKTVNVTWdTMFZaTFMwdExTMEtUVVpyZDBWM1dVaExiMXBKZW1vd1EwRlJXVWxMYjFwSmVtb3dSRUZSWTBSUlowRkZNVTlzWWpONlRVRkdSbmhZUzBocFNXdFJUelZqU2pOWmFHdzFhVFpWVUhBclNXaDFkR1ZDU21KMVNHTkJOVlZ2WjB0dk1FVlhkR3hYZDFjMlMxTmhTMjlVVGtWWlREZEtiRU5SYVZadWEyaENhM1JWWjJjOVBRb3RMUzB0TFVWT1JDQlFWVUpNU1VNZ1MwVlpMUzB0TFMwPSIsInNpZyI6IlRVVlJRMGxJT1ZWUWF5OXhRUzlFV0ZOeVp6VXJhVVpCVTNWTFYxRmhRVnBwTDIxUVIwUkJhMDF4YlhCcE0zVkZRV2xDV210MWFVRkRhbFpDVTI5U04wWlVabkoxY0dkSmREQjNZbFJMWm10SGRFRlFlSFJ0U0RkaWEwZDVkejA5In1dfSwiaGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjlhMTViMmJmNTNlOTA5MjdjZmFkZjcyOTEzYTRiNWNkMDA1YmI4NjU1ZTYyZTdjZjY3NWNkYmY5MjY1NjEyYTQifSwicGF5bG9hZEhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5NjljYzgyZmZiZDcwOGY5NTkyOTdjMDk5N2U0NTdiZjY4YjhiM2JjYWM2ODNlMGE1MjdmNDg2MDNkOTBjM2Q1In19fX0="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtLyU0MHRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlc0AxLjAuNSIsImRpZ2VzdCI6eyJzaGE1MTIiOiIxZTJlYmVjZTc1NzI1MDg3NmNkZTlkMGY2YzYzNmVkNmUwMDg4YTIzYTZjNDc3ZmUwY2QxYWZjYzExODAwYTViYTBjOTMyZjRhNTdhMTI1MzcwNjNkNDlkNzE3YmI3YWU3NmI4YTI5MzhiM2Q0OGU3ZjAyNjE3ZjY1NjRhZDkxOSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2dpdGh1Yi5jb20vbnBtL2F0dGVzdGF0aW9uL3RyZWUvbWFpbi9zcGVjcy9wdWJsaXNoL3YwLjEiLCJwcmVkaWNhdGUiOnsibmFtZSI6IkB0cmlzaGFua2F0ZGF0YWRvZy9zdXByZW1lLWdvZ2dsZXMiLCJ2ZXJzaW9uIjoiMS4wLjUiLCJyZWdpc3RyeSI6Imh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnIn19","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEQCIH9UPk/qA/DXSrg5+iFASuKWQaAZi/mPGDAkMqmpi3uEAiBZkuiACjVBSoR7FTfrupgIt0wbTKfkGtAPxtmH7bkGyw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]}}},{"predicateType":"https://slsa.dev/provenance/v0.2","bundle":{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"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"},{"rawBytes":"MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="},{"rawBytes":"MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"}]},"tlogEntries":[{"logIndex":"20783663","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1684258374","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCPJ3WKZudSVvTRYl6s3WEBiWQviNfwkk/MsFI/B1rcGgIhAJs9r4sDUp/IPm+fJ0TegrQrTugkAs1pxxtIYXq705eY"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVWhHUkVORFFuQnhaMEYzU1VKQlowbFZTRXdyUWs1aVNHUlRaelJQTmpscE1UUjJWVTVWWVdoSGFGUkpkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA1VVJUSk5WR042VFdwVk1GZG9ZMDVOYWsxM1RsUkZNazFVWXpCTmFsVXdWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWYWVVNVhTMFJpWmpCd1lsWm1VMVI2VVd4TFRteFVRV1YzYWxGRlZVWTJlR0ZQVFU0S1IwSm1UVTAyUmsxMFprdGFjakIzUWt4UlQzVTRkblY0TDBFNGQwSkJURFJRWW1OcFN6VnJMM0YwWkV3eWFHMDBiVFpQUTBKaWEzZG5aMWN4VFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWbGQzaEJDblpITm14Sk9YVjVabkZKUzNJMGMxZDJhMnhSZGsxemQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQyUlJXVVJXVWpCU1FWRklMMEpIYzNkaFdWcHVZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETlNlV0ZZVG05WlZ6VnlXVmhTYXdwWldGSm9Xa2M1Ymt3elRqRmpTRXBzWWxkVmRGb3lPVzVhTW5oc1kzazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWRXTkhNSFJqU0ZacENtSkhiSHBoUXpVMVlsZDRRV050Vm0xamVUa3dXVmRrZWt3eldYaE1ha0YxVGxSQk5VSm5iM0pDWjBWRlFWbFBMMDFCUlVKQ1EzUnZaRWhTZDJONmIzWUtURE5TZG1FeVZuVk1iVVpxWkVkc2RtSnVUWFZhTW13d1lVaFdhV1JZVG14amJVNTJZbTVTYkdKdVVYVlpNamwwVFVKVlIwTnBjMGRCVVZGQ1p6YzRkd3BCVVVsRlFqTktiR0pIVm1oak1sVjNUbWRaUzB0M1dVSkNRVWRFZG5wQlFrRjNVVzlOZW1oc1dXMVpOVTlVVVRCT1IxVjNUWHBPYVUxdFdYaE9WRlYzQ2xsNmJHaFpWMFpzV1ZkT2EwNXFTbXROUkVwb1RWUkthVmxVUVdSQ1oyOXlRbWRGUlVGWlR5OU5RVVZGUWtFNVQySXlVbXhNYlhCNlNVWkNhRmt5ZEdnS1dqSlZkMHgzV1V0TGQxbENRa0ZIUkhaNlFVSkNVVkZvWkVoS2NHTXlhR2hpYlhSb1pFZFNhR1JIUm10aU1tTjJZek5XZDJOdFZuUmFVekZ1WWpKa2JncGlSMVo2VFVJMFIwTnBjMGRCVVZGQ1p6YzRkMEZSV1VWRlNFcHNXbTVOZG1SSFJtNWplVGt5VFZNMGQweHFWWGRQZDFsTFMzZFpRa0pCUjBSMmVrRkNDa05CVVhSRVEzUnZaRWhTZDJONmIzWk1NMUoyWVRKV2RVeHRSbXBrUjJ4MlltNU5kVm95YkRCaFNGWnBaRmhPYkdOdFRuWmlibEpzWW01UmRWa3lPWFFLVFVoalIwTnBjMGRCVVZGQ1p6YzRkMEZSYTBWaFVYaHVZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETlNlV0ZZVG05WlZ6VnlXVmhTYXdwWldGSm9Xa2M1Ymt3elRqRmpTRXBzWWxkVmRGb3lPVzVhTW5oc1kzazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWRXTkhNSFJqU0ZacENtSkhiSHBoUXpVMVlsZDRRV050Vm0xamVUa3dXVmRrZWt3eldYaE1ha0YxVGxSQk5FSm5iM0pDWjBWRlFWbFBMMDFCUlV0Q1EyOU5TMFJOTkZwWFNtMEtUMVJyTUU1RVVteE5SRTE2V1dwS2JVMVVWVEZOUjAwMVdWZEdhRnBYUm1wYVJGbDVXa1JCZVZsVVJYbFpiVVYzU0ZGWlMwdDNXVUpDUVVkRWRucEJRZ3BEZDFGUVJFRXhibUZZVW05a1YwbDBZVWM1ZW1SSFZtdE5SVkZIUTJselIwRlJVVUpuTnpoM1FWRjNSVTVuZHpCaFNGSXdZMGhOTmt4NU9XNWhXRkp2Q21SWFNYVlpNamwwVEROU2VXRllUbTlaVnpWeVdWaFNhMWxZVW1oYVJ6bHVURE5PTVdOSVNteGlWMVYwV2pJNWJsb3llR3hqZWtFMFFtZHZja0puUlVVS1FWbFBMMDFCUlU1Q1EyOU5TMFJOTkZwWFNtMVBWR3N3VGtSU2JFMUVUWHBaYWtwdFRWUlZNVTFIVFRWWlYwWm9XbGRHYWxwRVdYbGFSRUY1V1ZSRmVRcFpiVVYzU1VGWlMwdDNXVUpDUVVkRWRucEJRa1JuVVZORVFrSjVXbGRhZWt3elVtaGFNMDEyWkdwRmRVMUROREZOUW10SFEybHpSMEZSVVVKbk56aDNDa0ZST0VWRGQzZEtUbXBOTWsxcWEzbE5SRUV5VFVSUlIwTnBjMGRCVVZGQ1p6YzRkMEZTUVVWS1ozZHJZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFVLV1RJNWRFd3pVbmxoV0U1dldWYzFjbGxZVW10WldGSm9Xa2M1YmsxQ1owZERhWE5IUVZGUlFtYzNPSGRCVWtWRlEyZDNTVTE2VFhoTmVrMTNUbnBOZHdwa2QxbExTM2RaUWtKQlIwUjJla0ZDUldkU2NFUkhaRzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmtTRXB3WXpKb2FHSnRkR2hrUjFKb0NtUkhSbXRpTW1OMll6TldkMk50Vm5SYVV6RnVZakprYm1KSFZucE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1qVjNZbE14ZDJSWFNuTUtZVmhPYjB4dWJIUmlSVUo1V2xkYWVrd3pVbWhhTTAxMlpHcEZkVTFETkRGTlJHZEhRMmx6UjBGUlVVSm5OemgzUVZKTlJVdG5kMjlOZW1oc1dXMVpOUXBQVkZFd1RrZFZkMDE2VG1sTmJWbDRUbFJWZDFsNmJHaFpWMFpzV1ZkT2EwNXFTbXROUkVwb1RWUkthVmxVUVZoQ1oyOXlRbWRGUlVGWlR5OU5RVVZWQ2tKQmEwMUNNMHBzWWtkV2FHTXlWWGRhZDFsTFMzZFpRa0pCUjBSMmVrRkNSbEZTV2tSR1pHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhZS1pFaEtjR015YUdoaWJYUm9aRWRTYUdSSFJtdGlNbU4yWXpOV2QyTnRWblJhVXpGdVlqSmtibUpIVm5wTU1rWnFaRWRzZG1KdVRYWmpibFoxWTNrNE1BcFBWR3N3VDFSak1VNTZhM2RNTWtZd1pFZFdkR05JVW5wTWVrVjNaMWx6UjBOcGMwZEJVVkZDTVc1clEwSkJTVVZtVVZJM1FVaHJRV1IzUkdSUVZFSnhDbmh6WTFKTmJVMWFTR2g1V2xwNlkwTnZhM0JsZFU0ME9ISm1LMGhwYmt0QlRIbHVkV3BuUVVGQldXZHNiakJSV1VGQlFVVkJkMEpKVFVWWlEwbFJRMm9LVUdGRmR6TlJhRVpXZGxKTFJ6WXJiV1ZZV2tKU1ozZFVObEpGY1ZwMVdWUTRZVkp3ZG5aU05IWjNTV2hCVFhweWVtSnRZMGRHYUhsNFIwSkxZV3h6UVFwa05rZDFaMGhRWldoVFpXdDZaak5ZZUZoRlpHcENja1pOUVc5SFEwTnhSMU5OTkRsQ1FVMUVRVEpuUVUxSFZVTk5RMGRoWmxSek9WWXZVMEpuYUZkUUNtSmplWFU0VUM4dk1VTnhjREpIVjFZelVVeEJRVEpSSzBKM1NucFZVU3RoYlhndmFsRjFkM1IwYW5sU1VHNVdNa1JuU1hoQlNVSXhSRlJvY2toWVVqY0tNSEp6TkdKSFRVeHlWVFpsYVhNNWNUaHJjV2htWjBGSWJHdzBibXR3UTJKWVZqRlZSbXM1TTJSQ01VTkdiemxTU1VRMWMzVkJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUT09Iiwic2lnIjoiVFVWUlEwbERTSGhWWjJSbVJtcFhja3hvTlRsSmRsaG1PRXQyVnpNeFN6bHRVVEJsTlZWaFlUVkJiV1F2YVhoVlFXbENaekZWZG1WUWMyRTJaR0V2YWpSbWIyWlZTR1pMV1VaVFoybENUeTgwY0hKeFFuWXJhV3N3ZFdSNVp6MDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMDQ3Mjg3NjhlYjBmNTEwNzc1YTkxNzNlYmJlMDVmMzM3MTMyMDg2MTRlYmVhMjZhYWQxNjk1M2UwYWJlN2JjNiJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjNmNWQ2M2MwOTM4N2Y2OTZhNDNiMWFiNDc0YzE5NzYxODg4NGY0NTIyZjllNWVkOGZiN2YxYzgzZjU5MGZiYjYifX19fQ=="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtLyU0MHRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlc0AxLjAuNSIsImRpZ2VzdCI6eyJzaGE1MTIiOiIxZTJlYmVjZTc1NzI1MDg3NmNkZTlkMGY2YzYzNmVkNmUwMDg4YTIzYTZjNDc3ZmUwY2QxYWZjYzExODAwYTViYTBjOTMyZjRhNTdhMTI1MzcwNjNkNDlkNzE3YmI3YWU3NmI4YTI5MzhiM2Q0OGU3ZjAyNjE3ZjY1NjRhZDkxOSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInByZWRpY2F0ZSI6eyJidWlsZFR5cGUiOiJodHRwczovL2dpdGh1Yi5jb20vbnBtL2NsaS9naGEvdjIiLCJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9naXRodWIuY29tL2FjdGlvbnMvcnVubmVyIn0sImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7InVyaSI6ImdpdCtodHRwczovL2dpdGh1Yi5jb20vdHJpc2hhbmthdGRhdGFkb2cvc3VwcmVtZS1nb2dnbGVzQHJlZnMvdGFncy92MS4wLjUiLCJkaWdlc3QiOnsic2hhMSI6IjM4ZWJmOTk0NDRlMDMzYjJmMTU1MGM5YWFhZWFjZDYyZDAyYTEyYmEifSwiZW50cnlQb2ludCI6Ii5naXRodWIvd29ya2Zsb3dzL25wbS1wdWJsaXNoLnltbCJ9LCJwYXJhbWV0ZXJzIjp7fSwiZW52aXJvbm1lbnQiOnsiR0lUSFVCX0VWRU5UX05BTUUiOiJyZWxlYXNlIiwiR0lUSFVCX1JFRiI6InJlZnMvdGFncy92MS4wLjUiLCJHSVRIVUJfUkVQT1NJVE9SWSI6InRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlcyIsIkdJVEhVQl9SRVBPU0lUT1JZX0lEIjoiNjM2MjkyMDA2IiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVJfSUQiOiIzMzEzMzA3MyIsIkdJVEhVQl9SVU5fQVRURU1QVCI6IjEiLCJHSVRIVUJfUlVOX0lEIjoiNDk5NDk3NTc5MCIsIkdJVEhVQl9TSEEiOiIzOGViZjk5NDQ0ZTAzM2IyZjE1NTBjOWFhYWVhY2Q2MmQwMmExMmJhIiwiR0lUSFVCX1dPUktGTE9XX1JFRiI6InRyaXNoYW5rYXRkYXRhZG9nL3N1cHJlbWUtZ29nZ2xlcy8uZ2l0aHViL3dvcmtmbG93cy9ucG0tcHVibGlzaC55bWxAcmVmcy90YWdzL3YxLjAuNSIsIkdJVEhVQl9XT1JLRkxPV19TSEEiOiIzOGViZjk5NDQ0ZTAzM2IyZjE1NTBjOWFhYWVhY2Q2MmQwMmExMmJhIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JZCI6IjQ5OTQ5NzU3OTAtMSIsImNvbXBsZXRlbmVzcyI6eyJwYXJhbWV0ZXJzIjpmYWxzZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS90cmlzaGFua2F0ZGF0YWRvZy9zdXByZW1lLWdvZ2dsZXNAcmVmcy90YWdzL3YxLjAuNSIsImRpZ2VzdCI6eyJzaGExIjoiMzhlYmY5OTQ0NGUwMzNiMmYxNTUwYzlhYWFlYWNkNjJkMDJhMTJiYSJ9fV19fQ==","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEQCICHxUgdfFjWrLh59IvXf8KvW31K9mQ0e5Uaa5Amd/ixUAiBg1UvePsa6da/j4fofUHfKYFSgiBO/4prqBv+ik0udyg==","keyid":""}]}}}]} \ No newline at end of file diff --git a/verifiers/verifier.go b/verifiers/verifier.go index c978d20a8..0015de1cd 100644 --- a/verifiers/verifier.go +++ b/verifiers/verifier.go @@ -66,14 +66,37 @@ func VerifyNpmPackage(ctx context.Context, attestations []byte, tarballHash string, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, + clientOpts ...options.ClientOpts, ) ([]byte, *utils.TrustedBuilderID, error) { + completeClientOpts, err := ensureCompleteClientOpts(clientOpts...) + if err != nil { + return nil, nil, err + } + verifier, err := getVerifier(builderOpts) if err != nil { return nil, nil, err } return verifier.VerifyNpmPackage(ctx, attestations, tarballHash, - provenanceOpts, builderOpts) + provenanceOpts, builderOpts, completeClientOpts) +} + +// ensureCompleteClientOpts returns a single options.ClientOpts, using the original if exactly one +// was provided from the variadic input, or creating a new one. +func ensureCompleteClientOpts(clientOpts ...options.ClientOpts) (*options.ClientOpts, error) { + switch len(clientOpts) { + case 0: + opts, err := options.NewDefaultClientOpts() + if err != nil { + return nil, err + } + return opts, nil + case 1: + opts := clientOpts[0] + return &opts, nil + } + return nil, serrors.ErrorInvalidClientOpts } // VerifyVSA verifies the VSA attestation. It returns the attestation base64-decoded from the envelope. diff --git a/verifiers/verifier_regression_test.go b/verifiers/verifier_regression_test.go new file mode 100644 index 000000000..5cf17735e --- /dev/null +++ b/verifiers/verifier_regression_test.go @@ -0,0 +1,137 @@ +//go:build regression + +package verifiers + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "hash" + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" + + sigstoreTUF "github.com/sigstore/sigstore-go/pkg/tuf" + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" + "github.com/slsa-framework/slsa-verifier/v2/options" +) + +const testDir = "./testdata" + +// Test_VerifyNpmPackage ensures that verifiers.VerifyNpmPackage works, +// borrowing only a few examples from the larger set of cases in main.Test_runVerifyGHAArtifactPath +func Test_VerifyNpmPackage(t *testing.T) { + sigstoreTUFClient, err := sigstoreTUF.New(sigstoreTUF.DefaultOptions().WithForceCache()) // fewer TUF refreshes for faster tests + if err != nil { + t.Fatal(err) + } + + t.Parallel() + + tests := []struct { + name string + artifact string + builderID string + source string + pkgVersion string + pkgName string + err error + }{ + { + name: "valid npm CLI builder", + artifact: "supreme-googles-cli-v02-tag.tgz", + source: "github.com/trishankatdatadog/supreme-goggles", + pkgVersion: "1.0.5", + pkgName: "@trishankatdatadog/supreme-goggles", + builderID: "https://github.com/actions/runner/github-hosted", + }, + { + name: "valid npm CLI builder mismatch source", + artifact: "supreme-googles-cli-v02-tag.tgz", + source: "github.com/trishankatdatadog/supreme-goggleS", + pkgVersion: "1.0.5", + pkgName: "@trishankatdatadog/supreme-goggles", + builderID: "https://github.com/actions/runner/github-hosted", + err: serrors.ErrorMismatchSource, + }, + { + name: "invalid signature provenance npm CLI", + artifact: "supreme-googles-cli-v02-tag-invalidsigprov.tgz", + source: "github.com/trishankatdatadog/supreme-goggles", + pkgName: "@trishankatdatadog/supreme-goggles", + builderID: "https://github.com/actions/runner/github-hosted", + err: serrors.ErrorInvalidSignature, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + artifactPath := filepath.Clean(filepath.Join(testDir, "npm", "gha", tt.artifact)) + attestationsPath := fmt.Sprintf("%s.json", artifactPath) + artifactHash, err := computeFileHash(artifactPath, sha256.New()) + if err != nil { + t.Fatal(err) + } + attestations, err := os.ReadFile(attestationsPath) + if err != nil { + t.Fatal(err) + } + provenanceOpts := &options.ProvenanceOpts{ + ExpectedSourceURI: tt.source, + ExpectedDigest: artifactHash, + ExpectedPackageName: &tt.pkgName, + ExpectedPackageVersion: &tt.pkgVersion, + } + builderOpts := &options.BuilderOpts{ + ExpectedID: &tt.builderID, + } + VerifyNpmPackage(context.Background(), attestations, artifactHash, provenanceOpts, builderOpts) + }) + + t.Run(tt.name+" - with sigstoreTUFClient", func(t *testing.T) { + t.Parallel() + + artifactPath := filepath.Clean(filepath.Join(testDir, "npm", "gha", tt.artifact)) + attestationsPath := fmt.Sprintf("%s.json", artifactPath) + artifactHash, err := computeFileHash(artifactPath, sha256.New()) + if err != nil { + t.Fatal(err) + } + attestaions, err := ioutil.ReadFile(attestationsPath) + if err != nil { + t.Fatal(err) + } + provenanceOpts := &options.ProvenanceOpts{ + ExpectedSourceURI: tt.source, + ExpectedDigest: artifactHash, + ExpectedPackageName: &tt.pkgName, + ExpectedPackageVersion: &tt.pkgVersion, + } + builderOpts := &options.BuilderOpts{ + ExpectedID: &tt.builderID, + } + clientOpts := options.ClientOpts{ + SigstoreTUFClient: sigstoreTUFClient, + } + VerifyNpmPackage(context.Background(), attestaions, artifactHash, provenanceOpts, builderOpts, clientOpts) + }) + } +} + +func computeFileHash(filePath string, h hash.Hash) (string, error) { + f, err := os.Open(filePath) + if err != nil { + return "", err + } + defer f.Close() + + if _, err := io.Copy(h, f); err != nil { + return "", err + } + return hex.EncodeToString(h.Sum(nil)), nil +} diff --git a/verifiers/verifier_test.go b/verifiers/verifier_test.go new file mode 100644 index 000000000..d93092386 --- /dev/null +++ b/verifiers/verifier_test.go @@ -0,0 +1,60 @@ +package verifiers + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" + "github.com/slsa-framework/slsa-verifier/v2/options" +) + +func Test_ensureCompleteClientOpts(t *testing.T) { + t.Parallel() + tests := []struct { + name string + opts *[]options.ClientOpts + err error + }{ + { + name: "success: no ClientOpts", + opts: &[]options.ClientOpts{}, + err: nil, + }, + { + name: "success: one ClientOpt", + opts: &[]options.ClientOpts{ + {}, + }, + err: nil, + }, + { + name: "failure: multiple ClientOpts", + opts: &[]options.ClientOpts{ + {}, + {}, + }, + err: serrors.ErrorInvalidClientOpts, + }, + } + + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + opts, err := ensureCompleteClientOpts(*tt.opts...) + if errorDiff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); errorDiff != "" { + t.Errorf("unexpected error (-want +got):\n%s", errorDiff) + } + + if err != nil && opts != nil { + t.Errorf("expected opts to be non-nil") + } + + if err == nil && opts == nil { + t.Errorf("expected opts to be nil") + } + }) + } +}