Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: GCB tag and versioned-tag support for containers #540

Merged
merged 6 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 126 additions & 64 deletions cli/slsa-verifier/main_regression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/layout"

"github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier/verify"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
Expand Down Expand Up @@ -673,44 +671,13 @@ func Test_runVerifyGHAArtifactImage(t *testing.T) {
container.RunCosignImageVerification = func(ctx context.Context,
image string, co *cosign.CheckOpts,
) ([]oci.Signature, bool, error) {
return cosign.VerifyLocalImageAttestations(ctx, image, co)
}

// TODO: Is there a more uniform way of handling getting image digest for both
// remote and local images?
localDigestComputeFn := func(image string) (string, error) {
// This is copied from cosign's VerifyLocalImageAttestation code:
// https://github.com/sigstore/cosign/blob/fdceee4825dc5d56b130f3f431aab93137359e79/pkg/cosign/verify.go#L654
se, err := layout.SignedImageIndex(image)
if err != nil {
return "", err
}

var h v1.Hash
// Verify either an image index or image.
ii, err := se.SignedImageIndex(v1.Hash{})
if err != nil {
return "", err
key := "@sha256:"
i := strings.Index(image, key)
if i < 0 {
return nil, false, fmt.Errorf("cannot find '%v' in '%v'", key, image)
}
i, err := se.SignedImage(v1.Hash{})
if err != nil {
return "", err
}
switch {
case ii != nil:
h, err = ii.Digest()
if err != nil {
return "", err
}
case i != nil:
h, err = i.Digest()
if err != nil {
return "", err
}
default:
return "", errors.New("must verify either an image index or image")
}
return strings.TrimPrefix(h.String(), "sha256:"), nil
image = image[:i]
return cosign.VerifyLocalImageAttestations(ctx, image, co)
}

builder := "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml"
Expand Down Expand Up @@ -820,14 +787,20 @@ func Test_runVerifyGHAArtifactImage(t *testing.T) {
builderIDs = []*string{tt.pBuilderID}
}

// Compute the digest and append it to the image so that's it 'immutable'.
digest, err := localDigestCompute(image)
if err != nil {
panic(fmt.Sprintf("digest computation %v", err))
}
image = fmt.Sprintf("%v@sha256:%v", image, digest)

for _, bid := range builderIDs {
cmd := verify.VerifyImageCommand{
SourceURI: tt.source,
SourceBranch: tt.pbranch,
BuilderID: bid,
SourceTag: tt.ptag,
SourceVersionTag: tt.pversiontag,
DigestFn: localDigestComputeFn,
}

outBuilderID, err := cmd.Exec(context.Background(), []string{image})
Expand Down Expand Up @@ -863,35 +836,28 @@ func Test_runVerifyGHAArtifactImage(t *testing.T) {
}
}

func Test_runVerifyGCBArtifactImage(t *testing.T) {
t.Parallel()

// TODO: Is there a more uniform way of handling getting image digest for both
// remote and local images?
localDigestComputeFn := func(image string) (string, error) {
// This is copied from cosign's VerifyLocalImageAttestation code:
// https://github.com/sigstore/cosign/blob/fdceee4825dc5d56b130f3f431aab93137359e79/pkg/cosign/verify.go#L654
se, err := layout.SignedImageIndex(image)
if err != nil {
return "", err
}

h, err := se.Digest()
if err != nil {
return "", err
}

return strings.TrimPrefix(h.String(), "sha256:"), nil
func localDigestCompute(image string) (string, error) {
filename := image + ".digest"
digest, err := os.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("ReadFile fail: %w", err)
}
return strings.TrimPrefix(string(digest), "sha256:"), nil
}

func Test_runVerifyGCBArtifactImage(t *testing.T) {
t.Parallel()
builder := "https://cloudbuild.googleapis.com/GoogleHostedWorker"
tests := []struct {
name string
artifact string
artifactDigest map[string]string
noDigest bool
remote bool
provenance string
source string
ptag *string
pversiontag *string
pBuilderID *string
outBuilderID string
err error
Expand Down Expand Up @@ -1091,6 +1057,92 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
source: "github.com/laurentsimon/gcb-tests",
err: serrors.ErrorNoValidSignature,
},
{
name: "tag match",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
ptag: pString("v33.0.4"),
minversion: "v0.3",
},
{
name: "tag mismatch major",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
ptag: pString("v34.0.4"),
minversion: "v0.3",
err: serrors.ErrorMismatchTag,
},
{
name: "tag mismatch minor",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
ptag: pString("v33.1.4"),
minversion: "v0.3",
err: serrors.ErrorMismatchTag,
},
{
name: "tag mismatch patch",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
ptag: pString("v33.0.5"),
minversion: "v0.3",
err: serrors.ErrorMismatchTag,
},
{
name: "versioned tag match major",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v33"),
minversion: "v0.3",
},
{
name: "versioned tag match minor",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v33.0"),
minversion: "v0.3",
},
{
name: "versioned tag match patch",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v33.0.4"),
minversion: "v0.3",
},
{
name: "versioned tag mismatch patch",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v33.0.5"),
minversion: "v0.3",
err: serrors.ErrorMismatchVersionedTag,
},
{
name: "versioned tag mismatch minor",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v33.1"),
minversion: "v0.3",
err: serrors.ErrorMismatchVersionedTag,
},
{
name: "versioned tag mismatch major",
artifact: "gcloud-container-github-tag",
provenance: "gcloud-container-github-tag.json",
source: "github.com/slsa-framework/example-package",
pversiontag: pString("v35"),
minversion: "v0.3",
err: serrors.ErrorMismatchVersionedTag,
},
// TODO(388): verify the correct provenance is returned.
// This should also be done for all other entries in this test.
{
Expand Down Expand Up @@ -1146,6 +1198,7 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
artifact: "index.docker.io/laurentsimon/scorecard",
noversion: true,
remote: true,
noDigest: true,
source: "github.com/laurentsimon/gcb-tests",
provenance: "gcloud-container-github.json",
pBuilderID: pString(builder + "@v0.2"),
Expand All @@ -1170,7 +1223,7 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
builderIDs := []string{builder + "@" + semver, builder}
provenance := filepath.Clean(filepath.Join(TEST_DIR, v, tt.provenance))
image := tt.artifact
var fn verify.ComputeDigestFn
digestFn := container.GetImageDigest

// If builder ID is set, use it.
if tt.pBuilderID != nil {
Expand All @@ -1189,10 +1242,20 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
}
image = fmt.Sprintf("%s@sha256:%s", image, digest)
}

// If it is a local image, change the digest computation.
if !tt.remote {
image = filepath.Clean(filepath.Join(TEST_DIR, v, image))
fn = localDigestComputeFn
digestFn = localDigestCompute
}

if len(tt.artifactDigest) == 0 && !tt.noDigest {
// Compute the digest and append it to the image so that's it 'immutable'.
digest, err := digestFn(image)
if err != nil {
panic(fmt.Sprintf("digest computation %v", err))
}
image = fmt.Sprintf("%v@sha256:%v", image, digest)
}

// We run the test for each builderID, in order to test
Expand All @@ -1203,9 +1266,8 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
SourceURI: tt.source,
SourceBranch: nil,
BuilderID: &bid,
SourceTag: nil,
SourceVersionTag: nil,
DigestFn: fn,
SourceTag: tt.ptag,
SourceVersionTag: tt.pversiontag,
ProvenancePath: &provenance,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sha256:71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sha256:87db6d5226440e72f7134b71163df45282127a3f7b0600c946683cb2f43a70a9
Loading