feat: GCB refactor for v1.0 support (#682)

In anticipation for GCB's v1.0 support, this PR re-factors the code to
look similar to GHA's code

---------

Signed-off-by: laurentsimon <laurentsimon@google.com>
Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>
Co-authored-by: Ian Lewis <ianlewis@google.com>
This commit is contained in:
laurentsimon
2023-08-15 18:15:49 +00:00
committed by GitHub
co-authored by Ian Lewis
parent b9a0e6babf
commit 9aef8ff8aa
14 changed files with 388 additions and 441 deletions
-74
View File
@@ -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
+92 -109
View File
@@ -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 {
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)
}
ts, err := getAsString(args, "@type")
buildType, err := statement.BuildType()
if err != nil {
return nil, err
}
if ts != expectedType {
return nil, fmt.Errorf("%w: expected '%s', got '%s'", serrors.ErrorMismatchBuilderID,
expectedType, ts)
if err := validateBuildType(*provBuilderID, buildType); err != nil {
return nil, err
}
// 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)
}
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)
func getSubstitutionsField(statement iface.Provenance, name string) (string, error) {
sysParams, err := statement.GetSystemParameters()
if err != nil {
return "", err
}
substitutions, ok := argsMap["substitutions"]
value, ok := sysParams[name]
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)
}
value, ok := m[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
+14 -65
View File
@@ -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()))
}
@@ -0,0 +1,5 @@
package common
import "errors"
var ErrSubstitution = errors.New("GCB substitution variable error")
@@ -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)
}
@@ -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
}
@@ -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()))
}
})
}
}
@@ -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"}]}
@@ -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"}]}
@@ -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"}]}
@@ -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"}]}
@@ -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"
}
]
}
}
@@ -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"
}
]
}
}
-5
View File
@@ -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 {