diff --git a/verifiers/internal/gcb/intoto.go b/verifiers/internal/gcb/intoto.go deleted file mode 100644 index dccb68d6e..000000000 --- a/verifiers/internal/gcb/intoto.go +++ /dev/null @@ -1,74 +0,0 @@ -package gcb - -// NOTE: Copy of github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1 -// This holds an internal copy of in-toto-golang's structs for -// SLSA predicates to handle GCB's incompatibility with the -// published specification. -// Specifically, GCB provenance currently produces a string for -// ProvenancePredicate.Recipe.DefinedInMaterial rather than the SLSA compliant -// signed integer. Because of this, we comment out the field and do not unmarshal -// this in the Go struct. When comparing the envelope with the human-readable -// content, this field is ignored! -// GCB will later add compliant fields in the signed envelope, but NOT in the -// human-readable component. Either disregard comparison between human-readable -// summary and the signed envelope, or use this struct in comparison. - -import "time" - -const ( - // PredicateSLSAProvenance represents a build provenance for an artifact. - PredicateSLSAProvenance = "https://slsa.dev/provenance/v0.1" -) - -// ProvenancePredicate is the provenance predicate definition. -type ProvenancePredicate struct { - Builder ProvenanceBuilder `json:"builder"` - Recipe ProvenanceRecipe `json:"recipe"` - Metadata *ProvenanceMetadata `json:"metadata,omitempty"` - Materials []ProvenanceMaterial `json:"materials,omitempty"` -} - -// ProvenanceBuilder idenfifies the entity that executed the build steps. -type ProvenanceBuilder struct { - ID string `json:"id"` -} - -// ProvenanceRecipe describes the actions performed by the builder. -type ProvenanceRecipe struct { - Type string `json:"type"` - // DefinedInMaterial can be sent as the null pointer to indicate that - // the value is not present. - // DefinedInMaterial *int `json:"definedInMaterial,omitempty"` - EntryPoint string `json:"entryPoint"` - Arguments interface{} `json:"arguments,omitempty"` - Environment interface{} `json:"environment,omitempty"` -} - -// ProvenanceMetadata contains metadata for the built artifact. -type ProvenanceMetadata struct { - // Use pointer to make sure that the abscense of a time is not - // encoded as the Epoch time. - BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` - BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` - Completeness ProvenanceComplete `json:"completeness"` - Reproducible bool `json:"reproducible"` -} - -// ProvenanceMaterial defines the materials used to build an artifact. -type ProvenanceMaterial struct { - URI string `json:"uri"` - Digest DigestSet `json:"digest,omitempty"` -} - -// ProvenanceComplete indicates whether the claims in build/recipe are complete. -// For in depth information refer to the specifictaion: -// https://github.com/in-toto/attestation/blob/v0.1.0/spec/predicates/provenance.md -type ProvenanceComplete struct { - Arguments bool `json:"arguments"` - Environment bool `json:"environment"` - Materials bool `json:"materials"` -} - -// DigestSet contains a set of digests. It is represented as a map from -// algorithm name to lowercase hex-encoded value. -type DigestSet map[string]string diff --git a/verifiers/internal/gcb/provenance.go b/verifiers/internal/gcb/provenance.go index 38feb2911..ae6b08663 100644 --- a/verifiers/internal/gcb/provenance.go +++ b/verifiers/internal/gcb/provenance.go @@ -3,7 +3,6 @@ package gcb import ( "crypto/sha256" "encoding/json" - "errors" "fmt" "os" "reflect" @@ -11,13 +10,15 @@ import ( "strings" "github.com/google/go-cmp/cmp" - intoto "github.com/in-toto/in-toto-golang/in_toto" dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse" serrors "github.com/slsa-framework/slsa-verifier/v2/errors" "github.com/slsa-framework/slsa-verifier/v2/options" "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/keys" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/common" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/iface" + v01 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/v0.1" "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" ) @@ -28,25 +29,9 @@ var GCBBuilderIDs = []string{ var regionalKeyRegex = regexp.MustCompile(`^projects\/verified-builder\/locations\/(.*)\/keyRings\/attestor\/cryptoKeys\/builtByGCB\/cryptoKeyVersions\/1$`) -var errorSubstitutionError = errors.New("GCB substitution variable error") - -type v01IntotoStatement struct { - intoto.StatementHeader - Predicate ProvenancePredicate `json:"predicate"` -} - -// The GCB provenance contains a human-readable version of the intoto -// statement, but it is not compliant with the standard. It uses `slsaProvenance` -// instead of `predicate`. For backward compatibility, this has not been fixed -// by the GCB team. -type v01GCBIntotoStatement struct { - intoto.StatementHeader - SlsaProvenance ProvenancePredicate `json:"slsaProvenance"` -} - type provenance struct { Build struct { - UnverifiedTextIntotoStatement v01GCBIntotoStatement `json:"intotoStatement"` + UnverifiedTextIntotoStatementV01 v01.GCBIntotoTextStatement `json:"intotoStatement"` } `json:"build"` Kind string `json:"kind"` ResourceURI string `json:"resourceUri"` @@ -66,9 +51,9 @@ type gloudProvenance struct { } type Provenance struct { - gcloudProv *gloudProvenance - verifiedProvenance *provenance - verifiedIntotoStatement *v01IntotoStatement + gcloudProv *gloudProvenance + verifiedProvenance *provenance + verifiedStatement iface.Provenance } func ProvenanceFromBytes(payload []byte) (*Provenance, error) { @@ -85,8 +70,7 @@ func ProvenanceFromBytes(payload []byte) (*Provenance, error) { func (p *Provenance) isVerified() error { // Check that the signature is verified. - if p.verifiedIntotoStatement == nil || - p.verifiedProvenance == nil { + if p.verifiedStatement == nil || p.verifiedProvenance == nil { return serrors.ErrorNoValidSignature } return nil @@ -96,7 +80,7 @@ func (p *Provenance) GetVerifiedIntotoStatement() ([]byte, error) { if err := p.isVerified(); err != nil { return nil, err } - d, err := json.Marshal(p.verifiedIntotoStatement) + d, err := json.Marshal(p.verifiedStatement) if err != nil { return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error()) } @@ -166,43 +150,32 @@ func (p *Provenance) VerifyTextProvenance() error { return err } - // Note: there is an additional field `metadata.buildInvocationId` which - // is not part of the specs but is present. This field is currently ignored during comparison. - unverifiedTextIntotoStatement := v01IntotoStatement{ - StatementHeader: p.verifiedProvenance.Build.UnverifiedTextIntotoStatement.StatementHeader, - Predicate: p.verifiedProvenance.Build.UnverifiedTextIntotoStatement.SlsaProvenance, + statement := p.verifiedStatement + predicateType, err := statement.PredicateType() + if err != nil { + return err + } + + var unverifiedTextIntotoStatement interface{} + switch predicateType { + case v01.PredicateSLSAProvenance: + // NOTE: there is an additional field `metadata.buildInvocationId` which + // is not part of the specs but is present. This field is currently ignored during comparison. + unverifiedTextIntotoStatement = &v01.Provenance{ + StatementHeader: p.verifiedProvenance.Build.UnverifiedTextIntotoStatementV01.StatementHeader, + Pred: p.verifiedProvenance.Build.UnverifiedTextIntotoStatementV01.SlsaProvenance, + } + default: + return fmt.Errorf("%w: unknown %v type", serrors.ErrorInvalidFormat, predicateType) } // Note: DeepEqual() has problem with time comparisons: https://github.com/onsi/gomega/issues/264 // but this should not affect us since both times are supposed to have the same string and // they are both taken from a string representation. // We do not use cmp.Equal() because it *can* panic and is intended for unit tests only. - if !reflect.DeepEqual(unverifiedTextIntotoStatement, *p.verifiedIntotoStatement) { + if !reflect.DeepEqual(unverifiedTextIntotoStatement, p.verifiedStatement) { return fmt.Errorf("%w: diff '%s'", serrors.ErrorMismatchIntoto, - cmp.Diff(unverifiedTextIntotoStatement, *p.verifiedIntotoStatement)) - } - - return nil -} - -// VerifyIntotoHeaders verifies the headers are intoto format and the expected -// slsa predicate. -func (p *Provenance) VerifyIntotoHeaders() error { - if err := p.isVerified(); err != nil { - return err - } - - statement := p.verifiedIntotoStatement - // https://in-toto.io/Statement/v0.1 - if statement.StatementHeader.Type != intoto.StatementInTotoV01 { - return fmt.Errorf("%w: expected statement header type '%s', got '%s'", - serrors.ErrorInvalidDssePayload, intoto.StatementInTotoV01, statement.StatementHeader.Type) - } - - // https://slsa.dev/provenance/v0.1 - if statement.StatementHeader.PredicateType != PredicateSLSAProvenance { - return fmt.Errorf("%w: expected statement predicate type '%s', got '%s'", - serrors.ErrorInvalidDssePayload, PredicateSLSAProvenance, statement.StatementHeader.PredicateType) + cmp.Diff(unverifiedTextIntotoStatement, p.verifiedStatement)) } return nil @@ -217,18 +190,20 @@ func isValidBuilderID(id string) error { return serrors.ErrorInvalidBuilderID } -func validateRecipeType(builderID utils.TrustedBuilderID, recipeType string) error { +func validateBuildType(builderID utils.TrustedBuilderID, buildType string) error { var err error v := builderID.Version() switch v { + // NOTE: buildType is called recipeType in v0.1 specification. + // Builders with version <= v0.3 use v0.1 specification. case "v0.2": // In this version, the recipe type should be the same as // the builder ID. - if builderID.String() == recipeType { + if builderID.String() == buildType { return nil } err = fmt.Errorf("%w: expected '%s', got '%s'", - serrors.ErrorInvalidRecipe, builderID.String(), recipeType) + serrors.ErrorInvalidRecipe, builderID.String(), buildType) case "v0.3": // In this version, two recipe types are allowed, depending how the @@ -239,12 +214,12 @@ func validateRecipeType(builderID utils.TrustedBuilderID, recipeType string) err "https://cloudbuild.googleapis.com/CloudBuildSteps@", } for _, r := range recipes { - if strings.HasPrefix(recipeType, r) { + if strings.HasPrefix(buildType, r) { return nil } } err = fmt.Errorf("%w: expected on of '%s', got '%s'", - serrors.ErrorInvalidRecipe, strings.Join(recipes, ","), recipeType) + serrors.ErrorInvalidRecipe, strings.Join(recipes, ","), buildType) default: err = fmt.Errorf("%w: version '%s'", serrors.ErrorInvalidBuilderID, v) @@ -262,8 +237,11 @@ func (p *Provenance) VerifyBuilder(builderOpts *options.BuilderOpts) (*utils.Tru return nil, err } - statement := p.verifiedIntotoStatement - predicateBuilderID := statement.Predicate.Builder.ID + statement := p.verifiedStatement + predicateBuilderID, err := statement.BuilderID() + if err != nil { + return nil, err + } // Sanity check the builderID. if err := isValidBuilderID(predicateBuilderID); err != nil { @@ -283,26 +261,38 @@ func (p *Provenance) VerifyBuilder(builderOpts *options.BuilderOpts) (*utils.Tru } // Valiate the recipe type. - if err := validateRecipeType(*provBuilderID, statement.Predicate.Recipe.Type); err != nil { + buildType, err := statement.BuildType() + if err != nil { return nil, err } - - // Validate the recipe argument type. - expectedType := "type.googleapis.com/google.devtools.cloudbuild.v1.Build" - args, ok := statement.Predicate.Recipe.Arguments.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("%w: recipe arguments is not a map", serrors.ErrorInvalidDssePayload) + if err := validateBuildType(*provBuilderID, buildType); err != nil { + return nil, err } - ts, err := getAsString(args, "@type") + + // Validate the recipe argument type for v0.2 provenance only. + predicate, err := statement.Predicate() if err != nil { return nil, err } + switch v := predicate.(type) { + case v01.ProvenancePredicate: + expectedType := "type.googleapis.com/google.devtools.cloudbuild.v1.Build" + args, ok := v.Recipe.Arguments.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("%w: recipe arguments is not a map", serrors.ErrorInvalidDssePayload) + } + ts, err := getAsString(args, "@type") + if err != nil { + return nil, err + } - if ts != expectedType { - return nil, fmt.Errorf("%w: expected '%s', got '%s'", serrors.ErrorMismatchBuilderID, - expectedType, ts) + if ts != expectedType { + return nil, fmt.Errorf("%w: expected '%s', got '%s'", serrors.ErrorMismatchBuilderID, + expectedType, ts) + } + default: + return nil, fmt.Errorf("%w: unknown type %v", serrors.ErrorInvalidFormat, v) } - return provBuilderID, nil } @@ -324,8 +314,12 @@ func (p *Provenance) VerifySubjectDigest(expectedHash string) error { return err } - statement := p.verifiedIntotoStatement - for _, subject := range statement.StatementHeader.Subject { + statement := p.verifiedStatement + subjects, err := statement.Subjects() + if err != nil { + return err + } + for _, subject := range subjects { digestSet := subject.Digest hash, exists := digestSet["sha256"] if !exists { @@ -346,12 +340,11 @@ func (p *Provenance) VerifySourceURI(expectedSourceURI string, builderID utils.T return err } - statement := p.verifiedIntotoStatement - materials := statement.Predicate.Materials - if len(materials) == 0 { - return fmt.Errorf("%w: no materials", serrors.ErrorInvalidDssePayload) + statement := p.verifiedStatement + uri, err := statement.SourceURI() + if err != nil { + return err } - uri := materials[0].URI // NOTE: the material URI did not contain 'git+' for GCB versions <= v0.3. // A change occurred sometimes in v0.3 witout version bump. // Versions >= 0.3 contain the prefix (https://github.com/slsa-framework/slsa-verifier/pull/519). @@ -370,7 +363,6 @@ func (p *Provenance) VerifySourceURI(expectedSourceURI string, builderID utils.T `https://cloud.google.com/build/docs/automating-builds/github/build-repos-from-github`) } - var err error v := builderID.Version() switch v { case "v0.2": @@ -428,7 +420,7 @@ func (p *Provenance) getTag() (string, error) { return "", err } - statement := p.verifiedIntotoStatement + statement := p.verifiedStatement provenanceTag, err := getSubstitutionsField(statement, "TAG_NAME") if err != nil { return "", err @@ -437,32 +429,20 @@ func (p *Provenance) getTag() (string, error) { return provenanceTag, nil } -func getSubstitutionsField(statement *v01IntotoStatement, name string) (string, error) { - arguments := statement.Predicate.Recipe.Arguments - - argsMap, ok := arguments.(map[string]interface{}) - if !ok { - return "", fmt.Errorf("%w: cannot cast arguments as map", errorSubstitutionError) - } - - substitutions, ok := argsMap["substitutions"] - if !ok { - return "", fmt.Errorf("%w: no 'substitutions' field", errorSubstitutionError) - } - - m, ok := substitutions.(map[string]interface{}) - if !ok { - return "", fmt.Errorf("%w: cannot convert substitutions to a map", errorSubstitutionError) +func getSubstitutionsField(statement iface.Provenance, name string) (string, error) { + sysParams, err := statement.GetSystemParameters() + if err != nil { + return "", err } - value, ok := m[name] + value, ok := sysParams[name] if !ok { - return "", fmt.Errorf("%w: no entry '%v' in substitution map", errorSubstitutionError, name) + return "", fmt.Errorf("%w: no entry '%v' in substitution map", common.ErrSubstitution, name) } valueStr, ok := value.(string) if !ok { - return "", fmt.Errorf("%w: value '%v' is not a string", errorSubstitutionError, value) + return "", fmt.Errorf("%w: value '%v' is not a string", common.ErrSubstitution, value) } return valueStr, nil @@ -534,11 +514,14 @@ func (p *Provenance) verifySignatures(prov *provenance) error { continue } - var statement v01IntotoStatement - if err := json.Unmarshal(payload, &statement); err != nil { - return fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error()) + // TODO(#683): try v1.0 verification. + // We can use the text.SlsaprovenanceV01 field to dis-ambiguate. + stmt, err := v01.New(payload) + if err != nil { + errs = append(errs, err) + continue } - p.verifiedIntotoStatement = &statement + p.verifiedStatement = stmt p.verifiedProvenance = prov fmt.Fprintf(os.Stderr, "Verification succeeded with region key '%s'\n", region) return nil diff --git a/verifiers/internal/gcb/provenance_test.go b/verifiers/internal/gcb/provenance_test.go index d35e7bbe0..908b2e860 100644 --- a/verifiers/internal/gcb/provenance_test.go +++ b/verifiers/internal/gcb/provenance_test.go @@ -12,6 +12,8 @@ import ( serrors "github.com/slsa-framework/slsa-verifier/v2/errors" "github.com/slsa-framework/slsa-verifier/v2/options" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/common" + v01 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/v0.1" "github.com/slsa-framework/slsa-verifier/v2/verifiers/utils" ) @@ -20,72 +22,19 @@ import ( // expect this statement to be populated; and this is done only // after the signature is verified. func setStatement(gcb *Provenance) error { - var statement v01IntotoStatement payload, err := utils.PayloadFromEnvelope(&gcb.gcloudProv.ProvenanceSummary.Provenance[0].Envelope) if err != nil { return fmt.Errorf("payloadFromEnvelope: %w", err) } - if err := json.Unmarshal(payload, &statement); err != nil { - return fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error()) + stmt, err := v01.New(payload) + if err != nil { + return fmt.Errorf("v01.New: %w", err) } - gcb.verifiedIntotoStatement = &statement + gcb.verifiedStatement = stmt gcb.verifiedProvenance = &gcb.gcloudProv.ProvenanceSummary.Provenance[0] return nil } -func Test_VerifyIntotoHeaders(t *testing.T) { - t.Parallel() - tests := []struct { - name string - path string - expected error - }{ - { - name: "valid gcb provenance", - path: "./testdata/gcloud-container-github.json", - }, - { - name: "valid gcb provenance gcs", - path: "./testdata/gcloud-container-gcs.json", - }, - { - name: "invalid intoto header", - path: "./testdata/gcloud-container-invalid-intotoheader.json", - expected: serrors.ErrorInvalidDssePayload, - }, - { - name: "invalid provenance header", - path: "./testdata/gcloud-container-invalid-slsaheader.json", - expected: serrors.ErrorInvalidDssePayload, - }, - } - 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() - - content, err := os.ReadFile(tt.path) - if err != nil { - panic(fmt.Errorf("os.ReadFile: %w", err)) - } - - prov, err := ProvenanceFromBytes(content) - if err != nil { - panic(fmt.Errorf("ProvenanceFromBytes: %w", err)) - } - - if err := setStatement(prov); err != nil { - panic(fmt.Errorf("setStatement: %w", err)) - } - - err = prov.VerifyIntotoHeaders() - if !cmp.Equal(err, tt.expected, cmpopts.EquateErrors()) { - t.Errorf(cmp.Diff(err, tt.expected, cmpopts.EquateErrors())) - } - }) - } -} - func Test_VerifyBuilder(t *testing.T) { t.Parallel() tests := []struct { @@ -243,7 +192,7 @@ func Test_VerifyBuilder(t *testing.T) { } } -func Test_validateRecipeType(t *testing.T) { +func Test_validateBuildType(t *testing.T) { t.Parallel() tests := []struct { name string @@ -303,7 +252,7 @@ func Test_validateRecipeType(t *testing.T) { if err != nil { panic(fmt.Errorf("BuilderIDNew: %w", err)) } - err = validateRecipeType(*builderID, tt.recipeType) + err = validateBuildType(*builderID, tt.recipeType) if !cmp.Equal(err, tt.expected, cmpopts.EquateErrors()) { t.Errorf(cmp.Diff(err, tt.expected, cmpopts.EquateErrors())) } @@ -856,7 +805,7 @@ func Test_VerifyTextProvenance(t *testing.T) { } // Alter fields. - cpy, err := json.Marshal(prov.verifiedProvenance.Build.UnverifiedTextIntotoStatement) + cpy, err := json.Marshal(prov.verifiedProvenance.Build.UnverifiedTextIntotoStatementV01) if err != nil { panic(err) } @@ -893,7 +842,7 @@ func Test_VerifyTextProvenance(t *testing.T) { patch[i] += 1 } - if err = json.Unmarshal(patch, &prov.verifiedProvenance.Build.UnverifiedTextIntotoStatement); err != nil { + if err = json.Unmarshal(patch, &prov.verifiedProvenance.Build.UnverifiedTextIntotoStatementV01); err != nil { // If we updated a character that makes a non-string field invalid, like Time, unmarshaling will fail, // so we ignore the error. i += 1 @@ -995,19 +944,19 @@ func Test_getSubstitutionsField(t *testing.T) { name: "no substitutions field", path: "./testdata/gcloud-container-github.json", field: "TAG_NAME", - err: errorSubstitutionError, + err: common.ErrSubstitution, }, { name: "tag not present", path: "./testdata/gcloud-container-tag-notpresent.json", field: "TAG_NAME", - err: errorSubstitutionError, + err: common.ErrSubstitution, }, { name: "tag not string", path: "./testdata/gcloud-container-tag-notstring.json", field: "TAG_NAME", - err: errorSubstitutionError, + err: common.ErrSubstitution, }, } for _, tt := range tests { @@ -1029,7 +978,7 @@ func Test_getSubstitutionsField(t *testing.T) { panic(fmt.Errorf("setStatement: %w", err)) } - value, err := getSubstitutionsField(prov.verifiedIntotoStatement, tt.field) + value, err := getSubstitutionsField(prov.verifiedStatement, tt.field) if !cmp.Equal(err, tt.err, cmpopts.EquateErrors()) { t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors())) } diff --git a/verifiers/internal/gcb/slsaprovenance/common/error.go b/verifiers/internal/gcb/slsaprovenance/common/error.go new file mode 100644 index 000000000..9cdade162 --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/common/error.go @@ -0,0 +1,5 @@ +package common + +import "errors" + +var ErrSubstitution = errors.New("GCB substitution variable error") diff --git a/verifiers/internal/gcb/slsaprovenance/iface/provenance.go b/verifiers/internal/gcb/slsaprovenance/iface/provenance.go new file mode 100644 index 000000000..9f784190f --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/iface/provenance.go @@ -0,0 +1,32 @@ +package iface + +import ( + intoto "github.com/in-toto/in-toto-golang/in_toto" +) + +// Provenance represents provenance for a predicate type and build type. +type Provenance interface { + // Predicate returns the predicate. + Predicate() (interface{}, error) + + // PredicateType returns the predicate type. + PredicateType() (string, error) + + // Header returns the statement header. + Header() (intoto.StatementHeader, error) + + // BuilderID returns the builder id in the predicate. + BuilderID() (string, error) + + // BuildType returns the buildType. + BuildType() (string, error) + + // SourceURI is the full URI (including tag). + SourceURI() (string, error) + + // Subject is the list of intoto subjects in the provenance. + Subjects() ([]intoto.Subject, error) + + // Get system pararmeters. + GetSystemParameters() (map[string]any, error) +} diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/provenance.go b/verifiers/internal/gcb/slsaprovenance/v0.1/provenance.go new file mode 100644 index 000000000..12a2d7abb --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/provenance.go @@ -0,0 +1,185 @@ +package v01 + +// NOTE: Copy of github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1 +// This holds an internal copy of in-toto-golang's structs for +// SLSA predicates to handle GCB's incompatibility with the +// published specification. +// Specifically, GCB provenance currently produces a string for +// ProvenancePredicate.Recipe.DefinedInMaterial rather than the SLSA compliant +// signed integer. Because of this, we comment out the field and do not unmarshal +// this in the Go struct. When comparing the envelope with the human-readable +// content, this field is ignored! +// GCB will later add compliant fields in the signed envelope, but NOT in the +// human-readable component. Either disregard comparison between human-readable +// summary and the signed envelope, or use this struct in comparison. + +import ( + "encoding/json" + "fmt" + "time" + + intoto "github.com/in-toto/in-toto-golang/in_toto" + intotov01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1" + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/common" + "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gcb/slsaprovenance/iface" +) + +const ( + // PredicateSLSAProvenance represents a build provenance for an artifact. + PredicateSLSAProvenance = intotov01.PredicateSLSAProvenance + // StatementInToto is the statement type for v0.1. + statementInToto = intoto.StatementInTotoV01 +) + +// ProvenancePredicate is the provenance predicate definition. +type ProvenancePredicate struct { + Builder ProvenanceBuilder `json:"builder"` + Recipe ProvenanceRecipe `json:"recipe"` + Metadata *ProvenanceMetadata `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} + +// ProvenanceBuilder idenfifies the entity that executed the build steps. +type ProvenanceBuilder struct { + ID string `json:"id"` +} + +// ProvenanceRecipe describes the actions performed by the builder. +type ProvenanceRecipe struct { + Type string `json:"type"` + // DefinedInMaterial can be sent as the null pointer to indicate that + // the value is not present. + // DefinedInMaterial *int `json:"definedInMaterial,omitempty"` + EntryPoint string `json:"entryPoint"` + Arguments interface{} `json:"arguments,omitempty"` + Environment interface{} `json:"environment,omitempty"` +} + +// ProvenanceMetadata contains metadata for the built artifact. +type ProvenanceMetadata struct { + // Use pointer to make sure that the abscense of a time is not + // encoded as the Epoch time. + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness ProvenanceComplete `json:"completeness"` + Reproducible bool `json:"reproducible"` +} + +// ProvenanceMaterial defines the materials used to build an artifact. +type ProvenanceMaterial struct { + URI string `json:"uri"` + Digest DigestSet `json:"digest,omitempty"` +} + +// ProvenanceComplete indicates whether the claims in build/recipe are complete. +// For in depth information refer to the specifictaion: +// https://github.com/in-toto/attestation/blob/v0.1.0/spec/predicates/provenance.md +type ProvenanceComplete struct { + Arguments bool `json:"arguments"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` +} + +// DigestSet contains a set of digests. It is represented as a map from +// algorithm name to lowercase hex-encoded value. +type DigestSet map[string]string + +// The GCB provenance contains a human-readable version of the intoto +// statement, but it is not compliant with the standard. It uses `slsaProvenance` +// instead of `predicate`. For backward compatibility, this has not been fixed +// by the GCB team. +type GCBIntotoTextStatement struct { + intoto.StatementHeader + SlsaProvenance ProvenancePredicate `json:"slsaProvenance"` +} + +// Provenance is GCB provenance. +type Provenance struct { + intoto.StatementHeader + Pred ProvenancePredicate `json:"predicate"` +} + +func New(payload []byte) (iface.Provenance, error) { + var provenance Provenance + if err := json.Unmarshal(payload, &provenance); err != nil { + return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error()) + } + + // Validate the intoto type. + if provenance.StatementHeader.Type != statementInToto { + return nil, fmt.Errorf("%w: expected statement header type '%s', got '%s'", + serrors.ErrorInvalidDssePayload, statementInToto, provenance.StatementHeader.Type) + } + + // Validate the predicate type. + if provenance.StatementHeader.PredicateType != PredicateSLSAProvenance { + return nil, fmt.Errorf("%w: expected statement predicate type '%s', got '%s'", + serrors.ErrorInvalidDssePayload, PredicateSLSAProvenance, provenance.StatementHeader.PredicateType) + } + return &provenance, nil +} + +func (p *Provenance) Predicate() (interface{}, error) { + return p.Pred, nil +} + +func (p *Provenance) PredicateType() (string, error) { + return p.StatementHeader.PredicateType, nil +} + +func (p *Provenance) Header() (intoto.StatementHeader, error) { + return p.StatementHeader, nil +} + +// BuilderID implements Statement.BuilderID. +func (p *Provenance) BuilderID() (string, error) { + return p.Pred.Builder.ID, nil +} + +// BuildType implements Statement.BuildType. +func (p *Provenance) BuildType() (string, error) { + return p.Pred.Recipe.Type, nil +} + +// BuildType implements Statement.GetSystemParameters. +func (p *Provenance) GetSystemParameters() (map[string]any, error) { + arguments := p.Pred.Recipe.Arguments + argsMap, ok := arguments.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("%w: cannot cast arguments as map", common.ErrSubstitution) + } + + substitutions, ok := argsMap["substitutions"] + if !ok { + return nil, fmt.Errorf("%w: no 'substitutions' field", common.ErrSubstitution) + } + + m, ok := substitutions.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("%w: cannot convert substitutions to a map", common.ErrSubstitution) + } + return m, nil +} + +// SourceURI implements Statement.SourceURI. +func (p *Provenance) SourceURI() (string, error) { + if len(p.Pred.Materials) == 0 { + return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no material") + } + uri := p.Pred.Materials[0].URI + if uri == "" { + return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI) + } + + return uri, nil +} + +// Subjects implements Statement.Subjects. +func (p *Provenance) Subjects() ([]intoto.Subject, error) { + subj := p.StatementHeader.Subject + if len(subj) == 0 { + return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no subjects") + } + return subj, nil +} diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/provenance_test.go b/verifiers/internal/gcb/slsaprovenance/v0.1/provenance_test.go new file mode 100644 index 000000000..ae1028e6f --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/provenance_test.go @@ -0,0 +1,56 @@ +package v01 + +import ( + "fmt" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + serrors "github.com/slsa-framework/slsa-verifier/v2/errors" +) + +func Test_New(t *testing.T) { + t.Parallel() + tests := []struct { + name string + path string + expected error + }{ + { + name: "valid gcb provenance", + path: "./testdata/gcloud-container-github.json", + }, + { + name: "valid gcb provenance gcs", + path: "./testdata/gcloud-container-gcs.json", + }, + { + name: "invalid intoto header", + path: "./testdata/gcloud-container-invalid-intotoheader.json", + expected: serrors.ErrorInvalidDssePayload, + }, + { + name: "invalid provenance header", + path: "./testdata/gcloud-container-invalid-slsaheader.json", + expected: serrors.ErrorInvalidDssePayload, + }, + } + 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() + + content, err := os.ReadFile(tt.path) + if err != nil { + panic(fmt.Errorf("os.ReadFile: %w", err)) + } + fmt.Println(string(content)) + _, err = New(content) + if !cmp.Equal(err, tt.expected, cmpopts.EquateErrors()) { + t.Errorf(cmp.Diff(err, tt.expected, cmpopts.EquateErrors())) + } + }) + } +} diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-gcs.json b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-gcs.json new file mode 100644 index 000000000..2709bee7b --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-gcs.json @@ -0,0 +1 @@ +{"_type":"https://in-toto.io/Statement/v0.1","predicate":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.3"},"materials":[{"digest":{"md5":"6e9c2c03099262d534519d106fe04b08"},"uri":"gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729"}],"metadata":{"buildFinishedOn":"2022-10-07T17:57:16.595464Z","buildInvocationId":"565456b1-0394-4c71-8c18-79f9057483df","buildStartedOn":"2022-10-07T17:56:01.814040892Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"565456b1-0394-4c71-8c18-79f9057483df","name":"projects/171719165453/locations/us-central1/builds/565456b1-0394-4c71-8c18-79f9057483df","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"requestedVerifyOption":"VERIFIED"},"sourceProvenance":{"fileHashes":{"gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729":{"fileHash":[{"type":"MD5","value":"bpwsAwmSYtU0UZ0Qb+BLCA=="}]}},"resolvedStorageSource":{"bucket":"damith-sds_cloudbuild","generation":"1665165361152729","object":"source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz"}},"steps":[{"args":["-c","docker build -t us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart .\ndocker push us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart\n"],"entrypoint":"/bin/bash","id":"Build and Push Container Image: Backend","name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-10-07T17:56:08.614390837Z","startTime":"2022-10-07T17:56:08.610302991Z"},"status":"SUCCESS","timing":{"endTime":"2022-10-07T17:57:15.136042514Z","startTime":"2022-10-07T17:56:08.610302991Z"}}],"substitutions":{"_BACKEND_IMAGE":"us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart"}},"definedInMaterial":"-1","type":"https://cloudbuild.googleapis.com/CloudBuildSteps@v0.1"}},"predicateType":"https://slsa.dev/provenance/v0.1","slsaProvenance":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.3"},"materials":[{"digest":{"md5":"6e9c2c03099262d534519d106fe04b08"},"uri":"gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729"}],"metadata":{"buildFinishedOn":"2022-10-07T17:57:16.595464Z","buildInvocationId":"565456b1-0394-4c71-8c18-79f9057483df","buildStartedOn":"2022-10-07T17:56:01.814040892Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"565456b1-0394-4c71-8c18-79f9057483df","name":"projects/171719165453/locations/us-central1/builds/565456b1-0394-4c71-8c18-79f9057483df","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"requestedVerifyOption":"VERIFIED"},"sourceProvenance":{"fileHashes":{"gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729":{"fileHash":[{"type":"MD5","value":"bpwsAwmSYtU0UZ0Qb+BLCA=="}]}},"resolvedStorageSource":{"bucket":"damith-sds_cloudbuild","generation":"1665165361152729","object":"source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz"}},"steps":[{"args":["-c","docker build -t us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart .\ndocker push us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart\n"],"entrypoint":"/bin/bash","id":"Build and Push Container Image: Backend","name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-10-07T17:56:08.614390837Z","startTime":"2022-10-07T17:56:08.610302991Z"},"status":"SUCCESS","timing":{"endTime":"2022-10-07T17:57:15.136042514Z","startTime":"2022-10-07T17:56:08.610302991Z"}}],"substitutions":{"_BACKEND_IMAGE":"us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart"}},"definedInMaterial":"-1","type":"https://cloudbuild.googleapis.com/CloudBuildSteps@v0.1"}},"subject":[{"digest":{"sha256":"9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe"},"name":"https://us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart"}]} \ No newline at end of file diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-github.json b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-github.json new file mode 100644 index 000000000..ebd6d9c3b --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-github.json @@ -0,0 +1 @@ +{"_type":"https://in-toto.io/Statement/v0.1","predicate":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"predicateType":"https://slsa.dev/provenance/v0.1","slsaProvenance":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"subject":[{"digest":{"sha256":"1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd"},"name":"https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14"}]} \ No newline at end of file diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-intotoheader.json b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-intotoheader.json new file mode 100644 index 000000000..b00fbed1f --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-intotoheader.json @@ -0,0 +1 @@ +{"_type":"https://in-toto.io/Statement/v0.2","predicate":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"predicateType":"https://slsa.dev/provenance/v0.1","slsaProvenance":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"subject":[{"digest":{"sha256":"1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd"},"name":"https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14"}]} \ No newline at end of file diff --git a/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-slsaheader.json b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-slsaheader.json new file mode 100644 index 000000000..5b56bcb08 --- /dev/null +++ b/verifiers/internal/gcb/slsaprovenance/v0.1/testdata/gcloud-container-invalid-slsaheader.json @@ -0,0 +1 @@ +{"_type":"https://in-toto.io/Statement/v0.1","predicate":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"predicateType":"https://slsa.dev/provenance/v0.2","slsaProvenance":{"builder":{"id":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"},"materials":[{"uri":"https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"}],"metadata":{"buildFinishedOn":"2022-08-15T22:43:34.366498Z","buildInvocationId":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","buildStartedOn":"2022-08-15T22:43:18.700638187Z"},"recipe":{"arguments":{"@type":"type.googleapis.com/google.devtools.cloudbuild.v1.Build","id":"b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b","options":{"dynamicSubstitutions":true,"logging":"LEGACY","pool":{},"substitutionOption":"ALLOW_LOOSE"},"sourceProvenance":{},"steps":[{"args":["build","-t","us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14","."],"name":"gcr.io/cloud-builders/docker","pullTiming":{"endTime":"2022-08-15T22:43:21.662016533Z","startTime":"2022-08-15T22:43:21.657262492Z"},"status":"SUCCESS","timing":{"endTime":"2022-08-15T22:43:27.056377441Z","startTime":"2022-08-15T22:43:21.657262492Z"}}]},"entryPoint":"cloudbuild.yaml","type":"https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"}},"subject":[{"digest":{"sha256":"1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd"},"name":"https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14"}]} \ No newline at end of file diff --git a/verifiers/internal/gcb/testdata/gcloud-container-invalid-intotoheader.json b/verifiers/internal/gcb/testdata/gcloud-container-invalid-intotoheader.json deleted file mode 100644 index bd2a38ebb..000000000 --- a/verifiers/internal/gcb/testdata/gcloud-container-invalid-intotoheader.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "image_summary": { - "digest": "sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "fully_qualified_digest": "us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "registry": "us-west2-docker.pkg.dev", - "repository": "quickstart-docker-repo" - }, - "provenance_summary": { - "provenance": [ - { - "build": { - "intotoStatement": { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v0.1", - "slsaProvenance": { - "builder": { - "id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2" - }, - "materials": [ - { - "uri": "https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e" - } - ], - "metadata": { - "buildFinishedOn": "2022-08-15T22:43:34.366498Z", - "buildInvocationId": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "buildStartedOn": "2022-08-15T22:43:18.700638187Z" - }, - "recipe": { - "arguments": { - "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.Build", - "id": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "options": { - "dynamicSubstitutions": true, - "logging": "LEGACY", - "pool": {}, - "substitutionOption": "ALLOW_LOOSE" - }, - "sourceProvenance": {}, - "steps": [ - { - "args": [ - "build", - "-t", - "us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14", - "." - ], - "name": "gcr.io/cloud-builders/docker", - "pullTiming": { - "endTime": "2022-08-15T22:43:21.662016533Z", - "startTime": "2022-08-15T22:43:21.657262492Z" - }, - "status": "SUCCESS", - "timing": { - "endTime": "2022-08-15T22:43:27.056377441Z", - "startTime": "2022-08-15T22:43:21.657262492Z" - } - } - ] - }, - "entryPoint": "cloudbuild.yaml", - "type": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2" - } - }, - "subject": [ - { - "digest": { - "sha256": "1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd" - }, - "name": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14" - } - ] - } - }, - "createTime": "2022-08-15T22:43:35.649016Z", - "envelope": { - "payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjIiLAogICJwcmVkaWNhdGUiOiB7CiAgICAiYnVpbGRlciI6IHsKICAgICAgImlkIjogImh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9Hb29nbGVIb3N0ZWRXb3JrZXJAdjAuMiIKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgInVyaSI6ICJodHRwczovL2dpdGh1Yi5jb20vbGF1cmVudHNpbW9uL2djYi10ZXN0cy9jb21taXQvZmJiYjk4NzY1ZTg1YWQ0NjQzMDJkYzU5Nzc5NjgxMDRkMzZlNDU1ZSIKICAgICAgfQogICAgXSwKICAgICJtZXRhZGF0YSI6IHsKICAgICAgImJ1aWxkRmluaXNoZWRPbiI6ICIyMDIyLTA4LTE1VDIyOjQzOjM0LjM2NjQ5OFoiLAogICAgICAiYnVpbGRJbnZvY2F0aW9uSWQiOiAiYjZlMDUyYTctNWFhNC00MWJmLWE1NmItOWJjNGU0ZjMwNThiIiwKICAgICAgImJ1aWxkU3RhcnRlZE9uIjogIjIwMjItMDgtMTVUMjI6NDM6MTguNzAwNjM4MTg3WiIKICAgIH0sCiAgICAicmVjaXBlIjogewogICAgICAiYXJndW1lbnRzIjogewogICAgICAgICJAdHlwZSI6ICJ0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5kZXZ0b29scy5jbG91ZGJ1aWxkLnYxLkJ1aWxkIiwKICAgICAgICAiaWQiOiAiYjZlMDUyYTctNWFhNC00MWJmLWE1NmItOWJjNGU0ZjMwNThiIiwKICAgICAgICAib3B0aW9ucyI6IHsKICAgICAgICAgICJkeW5hbWljU3Vic3RpdHV0aW9ucyI6IHRydWUsCiAgICAgICAgICAibG9nZ2luZyI6ICJMRUdBQ1kiLAogICAgICAgICAgInBvb2wiOiB7fSwKICAgICAgICAgICJzdWJzdGl0dXRpb25PcHRpb24iOiAiQUxMT1dfTE9PU0UiCiAgICAgICAgfSwKICAgICAgICAic291cmNlUHJvdmVuYW5jZSI6IHt9LAogICAgICAgICJzdGVwcyI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgImFyZ3MiOiBbCiAgICAgICAgICAgICAgImJ1aWxkIiwKICAgICAgICAgICAgICAiLXQiLAogICAgICAgICAgICAgICJ1cy13ZXN0Mi1kb2NrZXIucGtnLmRldi9nb3NzdC1zY2FyZS1zYW5kYm94L3F1aWNrc3RhcnQtZG9ja2VyLXJlcG8vcXVpY2tzdGFydC1pbWFnZTp2MTQiLAogICAgICAgICAgICAgICIuIgogICAgICAgICAgICBdLAogICAgICAgICAgICAibmFtZSI6ICJnY3IuaW8vY2xvdWQtYnVpbGRlcnMvZG9ja2VyIiwKICAgICAgICAgICAgInB1bGxUaW1pbmciOiB7CiAgICAgICAgICAgICAgImVuZFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyMS42NjIwMTY1MzNaIiwKICAgICAgICAgICAgICAic3RhcnRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiIKICAgICAgICAgICAgfSwKICAgICAgICAgICAgInN0YXR1cyI6ICJTVUNDRVNTIiwKICAgICAgICAgICAgInRpbWluZyI6IHsKICAgICAgICAgICAgICAiZW5kVGltZSI6ICIyMDIyLTA4LTE1VDIyOjQzOjI3LjA1NjM3NzQ0MVoiLAogICAgICAgICAgICAgICJzdGFydFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyMS42NTcyNjI0OTJaIgogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9LAogICAgICAiZW50cnlQb2ludCI6ICJjbG91ZGJ1aWxkLnlhbWwiLAogICAgICAidHlwZSI6ICJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjIiCiAgICB9CiAgfSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMSIsCiAgInNsc2FQcm92ZW5hbmNlIjogewogICAgImJ1aWxkZXIiOiB7CiAgICAgICJpZCI6ICJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjIiCiAgICB9LAogICAgIm1hdGVyaWFscyI6IFsKICAgICAgewogICAgICAgICJ1cmkiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9nY2ItdGVzdHMvY29tbWl0L2ZiYmI5ODc2NWU4NWFkNDY0MzAyZGM1OTc3OTY4MTA0ZDM2ZTQ1NWUiCiAgICAgIH0KICAgIF0sCiAgICAibWV0YWRhdGEiOiB7CiAgICAgICJidWlsZEZpbmlzaGVkT24iOiAiMjAyMi0wOC0xNVQyMjo0MzozNC4zNjY0OThaIiwKICAgICAgImJ1aWxkSW52b2NhdGlvbklkIjogImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsCiAgICAgICJidWlsZFN0YXJ0ZWRPbiI6ICIyMDIyLTA4LTE1VDIyOjQzOjE4LjcwMDYzODE4N1oiCiAgICB9LAogICAgInJlY2lwZSI6IHsKICAgICAgImFyZ3VtZW50cyI6IHsKICAgICAgICAiQHR5cGUiOiAidHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuZGV2dG9vbHMuY2xvdWRidWlsZC52MS5CdWlsZCIsCiAgICAgICAgImlkIjogImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsCiAgICAgICAgIm9wdGlvbnMiOiB7CiAgICAgICAgICAiZHluYW1pY1N1YnN0aXR1dGlvbnMiOiB0cnVlLAogICAgICAgICAgImxvZ2dpbmciOiAiTEVHQUNZIiwKICAgICAgICAgICJwb29sIjoge30sCiAgICAgICAgICAic3Vic3RpdHV0aW9uT3B0aW9uIjogIkFMTE9XX0xPT1NFIgogICAgICAgIH0sCiAgICAgICAgInNvdXJjZVByb3ZlbmFuY2UiOiB7fSwKICAgICAgICAic3RlcHMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJhcmdzIjogWwogICAgICAgICAgICAgICJidWlsZCIsCiAgICAgICAgICAgICAgIi10IiwKICAgICAgICAgICAgICAidXMtd2VzdDItZG9ja2VyLnBrZy5kZXYvZ29zc3Qtc2NhcmUtc2FuZGJveC9xdWlja3N0YXJ0LWRvY2tlci1yZXBvL3F1aWNrc3RhcnQtaW1hZ2U6djE0IiwKICAgICAgICAgICAgICAiLiIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm5hbWUiOiAiZ2NyLmlvL2Nsb3VkLWJ1aWxkZXJzL2RvY2tlciIsCiAgICAgICAgICAgICJwdWxsVGltaW5nIjogewogICAgICAgICAgICAgICJlbmRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjYyMDE2NTMzWiIsCiAgICAgICAgICAgICAgInN0YXJ0VGltZSI6ICIyMDIyLTA4LTE1VDIyOjQzOjIxLjY1NzI2MjQ5MloiCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJzdGF0dXMiOiAiU1VDQ0VTUyIsCiAgICAgICAgICAgICJ0aW1pbmciOiB7CiAgICAgICAgICAgICAgImVuZFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyNy4wNTYzNzc0NDFaIiwKICAgICAgICAgICAgICAic3RhcnRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIF0KICAgICAgfSwKICAgICAgImVudHJ5UG9pbnQiOiAiY2xvdWRidWlsZC55YW1sIiwKICAgICAgInR5cGUiOiAiaHR0cHM6Ly9jbG91ZGJ1aWxkLmdvb2dsZWFwaXMuY29tL0dvb2dsZUhvc3RlZFdvcmtlckB2MC4yIgogICAgfQogIH0sCiAgInN1YmplY3QiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIxYTAzM2IwMDJmODllZDJiOGVhNzMzMTYyNDk3ZmI3MGYxYTQwNDlhN2Y4NjAyZDZhMzM2ODJiNGFkOTkyMWZkIgogICAgICB9LAogICAgICAibmFtZSI6ICJodHRwczovL3VzLXdlc3QyLWRvY2tlci5wa2cuZGV2L2dvc3N0LXNjYXJlLXNhbmRib3gvcXVpY2tzdGFydC1kb2NrZXItcmVwby9xdWlja3N0YXJ0LWltYWdlOnYxNCIKICAgIH0KICBdCn0K", - "payloadType": "application/vnd.in-toto+json", - "signatures": [ - { - "keyid": "projects/verified-builder/locations/global/keyRings/attestor/cryptoKeys/builtByGCB/cryptoKeyVersions/1", - "sig": "MEYCIQD-0xUsdkYnsmKnQL_ndEvXknLfn82zsG-hGyYUd4aYsAIhAP4KSCxN2VPNc-dvfrQIGduMUNmAiHxLttdezqdrSf3F" - } - ] - }, - "kind": "BUILD", - "name": "projects/gosst-scare-sandbox/occurrences/8ce06798-f94d-4772-a224-04e473163790", - "noteName": "projects/verified-builder/notes/intoto_b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "resourceUri": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "updateTime": "2022-08-15T22:43:35.649016Z" - } - ] - } -} \ No newline at end of file diff --git a/verifiers/internal/gcb/testdata/gcloud-container-invalid-slsaheader.json b/verifiers/internal/gcb/testdata/gcloud-container-invalid-slsaheader.json deleted file mode 100644 index ad783b4b9..000000000 --- a/verifiers/internal/gcb/testdata/gcloud-container-invalid-slsaheader.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "image_summary": { - "digest": "sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "fully_qualified_digest": "us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "registry": "us-west2-docker.pkg.dev", - "repository": "quickstart-docker-repo" - }, - "provenance_summary": { - "provenance": [ - { - "build": { - "intotoStatement": { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v0.1", - "slsaProvenance": { - "builder": { - "id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2" - }, - "materials": [ - { - "uri": "https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e" - } - ], - "metadata": { - "buildFinishedOn": "2022-08-15T22:43:34.366498Z", - "buildInvocationId": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "buildStartedOn": "2022-08-15T22:43:18.700638187Z" - }, - "recipe": { - "arguments": { - "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.Build", - "id": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "options": { - "dynamicSubstitutions": true, - "logging": "LEGACY", - "pool": {}, - "substitutionOption": "ALLOW_LOOSE" - }, - "sourceProvenance": {}, - "steps": [ - { - "args": [ - "build", - "-t", - "us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14", - "." - ], - "name": "gcr.io/cloud-builders/docker", - "pullTiming": { - "endTime": "2022-08-15T22:43:21.662016533Z", - "startTime": "2022-08-15T22:43:21.657262492Z" - }, - "status": "SUCCESS", - "timing": { - "endTime": "2022-08-15T22:43:27.056377441Z", - "startTime": "2022-08-15T22:43:21.657262492Z" - } - } - ] - }, - "entryPoint": "cloudbuild.yaml", - "type": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2" - } - }, - "subject": [ - { - "digest": { - "sha256": "1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd" - }, - "name": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14" - } - ] - } - }, - "createTime": "2022-08-15T22:43:35.649016Z", - "envelope": { - "payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGUiOiB7CiAgICAiYnVpbGRlciI6IHsKICAgICAgImlkIjogImh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9Hb29nbGVIb3N0ZWRXb3JrZXJAdjAuMiIKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgInVyaSI6ICJodHRwczovL2dpdGh1Yi5jb20vbGF1cmVudHNpbW9uL2djYi10ZXN0cy9jb21taXQvZmJiYjk4NzY1ZTg1YWQ0NjQzMDJkYzU5Nzc5NjgxMDRkMzZlNDU1ZSIKICAgICAgfQogICAgXSwKICAgICJtZXRhZGF0YSI6IHsKICAgICAgImJ1aWxkRmluaXNoZWRPbiI6ICIyMDIyLTA4LTE1VDIyOjQzOjM0LjM2NjQ5OFoiLAogICAgICAiYnVpbGRJbnZvY2F0aW9uSWQiOiAiYjZlMDUyYTctNWFhNC00MWJmLWE1NmItOWJjNGU0ZjMwNThiIiwKICAgICAgImJ1aWxkU3RhcnRlZE9uIjogIjIwMjItMDgtMTVUMjI6NDM6MTguNzAwNjM4MTg3WiIKICAgIH0sCiAgICAicmVjaXBlIjogewogICAgICAiYXJndW1lbnRzIjogewogICAgICAgICJAdHlwZSI6ICJ0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5kZXZ0b29scy5jbG91ZGJ1aWxkLnYxLkJ1aWxkIiwKICAgICAgICAiaWQiOiAiYjZlMDUyYTctNWFhNC00MWJmLWE1NmItOWJjNGU0ZjMwNThiIiwKICAgICAgICAib3B0aW9ucyI6IHsKICAgICAgICAgICJkeW5hbWljU3Vic3RpdHV0aW9ucyI6IHRydWUsCiAgICAgICAgICAibG9nZ2luZyI6ICJMRUdBQ1kiLAogICAgICAgICAgInBvb2wiOiB7fSwKICAgICAgICAgICJzdWJzdGl0dXRpb25PcHRpb24iOiAiQUxMT1dfTE9PU0UiCiAgICAgICAgfSwKICAgICAgICAic291cmNlUHJvdmVuYW5jZSI6IHt9LAogICAgICAgICJzdGVwcyI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgImFyZ3MiOiBbCiAgICAgICAgICAgICAgImJ1aWxkIiwKICAgICAgICAgICAgICAiLXQiLAogICAgICAgICAgICAgICJ1cy13ZXN0Mi1kb2NrZXIucGtnLmRldi9nb3NzdC1zY2FyZS1zYW5kYm94L3F1aWNrc3RhcnQtZG9ja2VyLXJlcG8vcXVpY2tzdGFydC1pbWFnZTp2MTQiLAogICAgICAgICAgICAgICIuIgogICAgICAgICAgICBdLAogICAgICAgICAgICAibmFtZSI6ICJnY3IuaW8vY2xvdWQtYnVpbGRlcnMvZG9ja2VyIiwKICAgICAgICAgICAgInB1bGxUaW1pbmciOiB7CiAgICAgICAgICAgICAgImVuZFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyMS42NjIwMTY1MzNaIiwKICAgICAgICAgICAgICAic3RhcnRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiIKICAgICAgICAgICAgfSwKICAgICAgICAgICAgInN0YXR1cyI6ICJTVUNDRVNTIiwKICAgICAgICAgICAgInRpbWluZyI6IHsKICAgICAgICAgICAgICAiZW5kVGltZSI6ICIyMDIyLTA4LTE1VDIyOjQzOjI3LjA1NjM3NzQ0MVoiLAogICAgICAgICAgICAgICJzdGFydFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyMS42NTcyNjI0OTJaIgogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9LAogICAgICAiZW50cnlQb2ludCI6ICJjbG91ZGJ1aWxkLnlhbWwiLAogICAgICAidHlwZSI6ICJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjIiCiAgICB9CiAgfSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInNsc2FQcm92ZW5hbmNlIjogewogICAgImJ1aWxkZXIiOiB7CiAgICAgICJpZCI6ICJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjIiCiAgICB9LAogICAgIm1hdGVyaWFscyI6IFsKICAgICAgewogICAgICAgICJ1cmkiOiAiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9nY2ItdGVzdHMvY29tbWl0L2ZiYmI5ODc2NWU4NWFkNDY0MzAyZGM1OTc3OTY4MTA0ZDM2ZTQ1NWUiCiAgICAgIH0KICAgIF0sCiAgICAibWV0YWRhdGEiOiB7CiAgICAgICJidWlsZEZpbmlzaGVkT24iOiAiMjAyMi0wOC0xNVQyMjo0MzozNC4zNjY0OThaIiwKICAgICAgImJ1aWxkSW52b2NhdGlvbklkIjogImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsCiAgICAgICJidWlsZFN0YXJ0ZWRPbiI6ICIyMDIyLTA4LTE1VDIyOjQzOjE4LjcwMDYzODE4N1oiCiAgICB9LAogICAgInJlY2lwZSI6IHsKICAgICAgImFyZ3VtZW50cyI6IHsKICAgICAgICAiQHR5cGUiOiAidHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuZGV2dG9vbHMuY2xvdWRidWlsZC52MS5CdWlsZCIsCiAgICAgICAgImlkIjogImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsCiAgICAgICAgIm9wdGlvbnMiOiB7CiAgICAgICAgICAiZHluYW1pY1N1YnN0aXR1dGlvbnMiOiB0cnVlLAogICAgICAgICAgImxvZ2dpbmciOiAiTEVHQUNZIiwKICAgICAgICAgICJwb29sIjoge30sCiAgICAgICAgICAic3Vic3RpdHV0aW9uT3B0aW9uIjogIkFMTE9XX0xPT1NFIgogICAgICAgIH0sCiAgICAgICAgInNvdXJjZVByb3ZlbmFuY2UiOiB7fSwKICAgICAgICAic3RlcHMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJhcmdzIjogWwogICAgICAgICAgICAgICJidWlsZCIsCiAgICAgICAgICAgICAgIi10IiwKICAgICAgICAgICAgICAidXMtd2VzdDItZG9ja2VyLnBrZy5kZXYvZ29zc3Qtc2NhcmUtc2FuZGJveC9xdWlja3N0YXJ0LWRvY2tlci1yZXBvL3F1aWNrc3RhcnQtaW1hZ2U6djE0IiwKICAgICAgICAgICAgICAiLiIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgIm5hbWUiOiAiZ2NyLmlvL2Nsb3VkLWJ1aWxkZXJzL2RvY2tlciIsCiAgICAgICAgICAgICJwdWxsVGltaW5nIjogewogICAgICAgICAgICAgICJlbmRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjYyMDE2NTMzWiIsCiAgICAgICAgICAgICAgInN0YXJ0VGltZSI6ICIyMDIyLTA4LTE1VDIyOjQzOjIxLjY1NzI2MjQ5MloiCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJzdGF0dXMiOiAiU1VDQ0VTUyIsCiAgICAgICAgICAgICJ0aW1pbmciOiB7CiAgICAgICAgICAgICAgImVuZFRpbWUiOiAiMjAyMi0wOC0xNVQyMjo0MzoyNy4wNTYzNzc0NDFaIiwKICAgICAgICAgICAgICAic3RhcnRUaW1lIjogIjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiIKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIF0KICAgICAgfSwKICAgICAgImVudHJ5UG9pbnQiOiAiY2xvdWRidWlsZC55YW1sIiwKICAgICAgInR5cGUiOiAiaHR0cHM6Ly9jbG91ZGJ1aWxkLmdvb2dsZWFwaXMuY29tL0dvb2dsZUhvc3RlZFdvcmtlckB2MC4yIgogICAgfQogIH0sCiAgInN1YmplY3QiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgInNoYTI1NiI6ICIxYTAzM2IwMDJmODllZDJiOGVhNzMzMTYyNDk3ZmI3MGYxYTQwNDlhN2Y4NjAyZDZhMzM2ODJiNGFkOTkyMWZkIgogICAgICB9LAogICAgICAibmFtZSI6ICJodHRwczovL3VzLXdlc3QyLWRvY2tlci5wa2cuZGV2L2dvc3N0LXNjYXJlLXNhbmRib3gvcXVpY2tzdGFydC1kb2NrZXItcmVwby9xdWlja3N0YXJ0LWltYWdlOnYxNCIKICAgIH0KICBdCn0K", - "payloadType": "application/vnd.in-toto+json", - "signatures": [ - { - "keyid": "projects/verified-builder/locations/global/keyRings/attestor/cryptoKeys/builtByGCB/cryptoKeyVersions/1", - "sig": "MEYCIQD-0xUsdkYnsmKnQL_ndEvXknLfn82zsG-hGyYUd4aYsAIhAP4KSCxN2VPNc-dvfrQIGduMUNmAiHxLttdezqdrSf3F" - } - ] - }, - "kind": "BUILD", - "name": "projects/gosst-scare-sandbox/occurrences/8ce06798-f94d-4772-a224-04e473163790", - "noteName": "projects/verified-builder/notes/intoto_b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b", - "resourceUri": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", - "updateTime": "2022-08-15T22:43:35.649016Z" - } - ] - } - } \ No newline at end of file diff --git a/verifiers/internal/gcb/verifier.go b/verifiers/internal/gcb/verifier.go index 04f31e6bb..511065009 100644 --- a/verifiers/internal/gcb/verifier.go +++ b/verifiers/internal/gcb/verifier.go @@ -64,11 +64,6 @@ func (v *GCBVerifier) VerifyImage(ctx context.Context, return nil, nil, err } - // Verify intoto header. - if err := prov.VerifyIntotoHeaders(); err != nil { - return nil, nil, err - } - // Verify the builder. builderID, err := prov.VerifyBuilder(builderOpts) if err != nil {