mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-08-19 11:36:49 +00:00
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:
co-authored by
Ian Lewis
parent
b9a0e6babf
commit
9aef8ff8aa
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"}]}
|
||||
+1
@@ -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"}]}
|
||||
Vendored
+1
@@ -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"}]}
|
||||
Vendored
+1
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user