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: support builderID matching with or without semver for GCB #256

Merged
merged 20 commits into from
Sep 13, 2022
105 changes: 76 additions & 29 deletions cli/slsa-verifier/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"github.com/slsa-framework/slsa-verifier/cli/slsa-verifier/verify"
serrors "github.com/slsa-framework/slsa-verifier/errors"
"github.com/slsa-framework/slsa-verifier/verifiers/container"
"github.com/slsa-framework/slsa-verifier/verifiers/utils/container"
)

func errCmp(e1, e2 error) bool {
Expand Down Expand Up @@ -511,10 +511,14 @@ func Test_runVerifyArtifactPath(t *testing.T) {
return
}

if tt.outBuilderID != "" && outBuilderID != tt.outBuilderID {
t.Errorf(cmp.Diff(outBuilderID, tt.outBuilderID))
// Validate against test's expected builderID, if provided.
if tt.outBuilderID != "" {
if err := outBuilderID.Matches(tt.outBuilderID); err != nil {
t.Errorf(fmt.Sprintf("matches failed: %v", err))
}
}

// TODO: verify using Matches().
}
})
}
Expand Down Expand Up @@ -675,9 +679,14 @@ func Test_runVerifyGHAArtifactImage(t *testing.T) {
return
}

if tt.outBuilderID != "" && outBuilderID != tt.outBuilderID {
t.Errorf(cmp.Diff(outBuilderID, tt.outBuilderID))
// Validate against test's expected builderID, if provided.
if tt.outBuilderID != "" {
if err := outBuilderID.Matches(tt.outBuilderID); err != nil {
t.Errorf(fmt.Sprintf("matches failed: %v", err))
}
}

// TODO: verify using Matches().
}
})
}
Expand Down Expand Up @@ -729,6 +738,29 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
provenance: "gcloud-container-github.json",
source: "github.com/laurentsimon/gcb-tests",
},
{
name: "mismatch input builder version",
artifact: "gcloud-container-github",
provenance: "gcloud-container-github.json",
source: "github.com/laurentsimon/gcb-tests",
pBuilderID: pString(builder + "@v0.4"),
err: serrors.ErrorMismatchBuilderID,
},
{
name: "unsupported builder",
artifact: "gcloud-container-github",
provenance: "gcloud-container-github.json",
source: "github.com/laurentsimon/gcb-tests",
pBuilderID: pString(builder + "a"),
err: serrors.ErrorVerifierNotSupported,
},
{
name: "match output builder name",
artifact: "gcloud-container-github",
provenance: "gcloud-container-github.json",
source: "github.com/laurentsimon/gcb-tests",
outBuilderID: builder,
},
{
name: "invalid repo name",
artifact: "gcloud-container-github",
Expand Down Expand Up @@ -858,17 +890,20 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {

for _, v := range checkVersions {
semver := path.Base(v)
builderID := pString(builder + "@" + semver)
// For each test, we run 2 sub-tests:
// 1. With the the full builderID including the semver.
// 2. With only the name of the builder.
builderIDs := []string{builder + "@" + semver, builder}
provenance := filepath.Clean(filepath.Join(TEST_DIR, v, tt.provenance))
image := tt.artifact
var fn verify.ComputeDigestFn

// If builder ID is set, use it.
if tt.pBuilderID != nil {
if !tt.noversion {
panic("builderID set but not noversion option")
}
builderID = tt.pBuilderID
// if !tt.noversion {
// panic("builderID set but not noversion option")
// }
builderIDs = []string{*tt.pBuilderID}
}

// Select the right image according to the builder version we are testing.
Expand All @@ -889,30 +924,42 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
fn = localDigestComputeFn
}

cmd := verify.VerifyImageCommand{
SourceURI: tt.source,
SourceBranch: nil,
BuilderID: builderID,
SourceTag: nil,
SourceVersionTag: nil,
DigestFn: fn,
ProvenancePath: &provenance,
}
// We run the test for each builderID, in order to test
// a builderID provided by name and one containing both the name
// and semver.
for _, bid := range builderIDs {
cmd := verify.VerifyImageCommand{
SourceURI: tt.source,
SourceBranch: nil,
BuilderID: &bid,
SourceTag: nil,
SourceVersionTag: nil,
DigestFn: fn,
ProvenancePath: &provenance,
}

outBuilderID, err := cmd.Exec(context.Background(), []string{image})
outBuilderID, err := cmd.Exec(context.Background(), []string{image})

if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
}
if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
}

if err != nil {
return
}
if err != nil {
return
}

if tt.outBuilderID != "" && outBuilderID != tt.outBuilderID {
t.Errorf(cmp.Diff(outBuilderID, tt.outBuilderID))
}
// Validate against test's expected builderID, if provided.
if tt.outBuilderID != "" {
if err := outBuilderID.Matches(tt.outBuilderID); err != nil {
t.Errorf(fmt.Sprintf("matches failed: %v", err))
}
}

// Validate against builderID we generated automatically.
if err := outBuilderID.Matches(bid); err != nil {
t.Errorf(fmt.Sprintf("matches failed: %v", err))
}
}
}
})
}
Expand Down
9 changes: 5 additions & 4 deletions cli/slsa-verifier/verify/verify_artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/slsa-framework/slsa-verifier/options"
"github.com/slsa-framework/slsa-verifier/verifiers"
"github.com/slsa-framework/slsa-verifier/verifiers/utils"
)

// Note: nil branch, tag, version-tag and builder-id means we ignore them during verification.
Expand All @@ -38,10 +39,10 @@ type VerifyArtifactCommand struct {
PrintProvenance bool
}

func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (string, error) {
func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (*utils.BuilderID, error) {
artifactHash, err := getArtifactHash(artifacts[0])
if err != nil {
return "", err
return nil, err
}

provenanceOpts := &options.ProvenanceOpts{
Expand All @@ -59,12 +60,12 @@ func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (s

provenance, err := os.ReadFile(c.ProvenancePath)
if err != nil {
return "", err
return nil, err
}

verifiedProvenance, outBuilderID, err := verifiers.VerifyArtifact(ctx, provenance, artifactHash, provenanceOpts, builderOpts)
if err != nil {
return "", err
return nil, err
}

if c.PrintProvenance {
Expand Down
13 changes: 7 additions & 6 deletions cli/slsa-verifier/verify/verify_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import (

"github.com/slsa-framework/slsa-verifier/options"
"github.com/slsa-framework/slsa-verifier/verifiers"
"github.com/slsa-framework/slsa-verifier/verifiers/container"
"github.com/slsa-framework/slsa-verifier/verifiers/utils"
"github.com/slsa-framework/slsa-verifier/verifiers/utils/container"
)

type ComputeDigestFn func(string) (string, error)
Expand All @@ -40,20 +41,20 @@ type VerifyImageCommand struct {
DigestFn ComputeDigestFn
}

func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (string, error) {
func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (*utils.BuilderID, error) {
artifactImage := artifacts[0]
// Retrieve the image digest.
if c.DigestFn == nil {
c.DigestFn = container.GetImageDigest
}
digest, err := c.DigestFn(artifactImage)
if err != nil {
return "", err
return nil, err
}

// Verify that the reference is immutable.
if err := container.ValidateArtifactReference(artifactImage, digest); err != nil {
return "", err
return nil, err
}

provenanceOpts := &options.ProvenanceOpts{
Expand All @@ -73,13 +74,13 @@ func (c *VerifyImageCommand) Exec(ctx context.Context, artifacts []string) (stri
if c.ProvenancePath != nil {
provenance, err = os.ReadFile(*c.ProvenancePath)
if err != nil {
return "", err
return nil, err
}
}

verifiedProvenance, outBuilderID, err := verifiers.VerifyImage(ctx, artifacts[0], provenance, provenanceOpts, builderOpts)
if err != nil {
return "", err
return nil, err
}

if c.PrintProvenance {
Expand Down
2 changes: 1 addition & 1 deletion experimental/rest/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func verifyHandlerV1(r *http.Request) *v1Result {
results = results.withIntotoStatement(p)
}

return results.withBuilderID(builderID).withValidation(validationSuccess)
return results.withBuilderID(builderID.String()).withValidation(validationSuccess)
}

func queryFromString(content []byte) (*v1Query, error) {
Expand Down
7 changes: 4 additions & 3 deletions register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/slsa-framework/slsa-verifier/options"
"github.com/slsa-framework/slsa-verifier/verifiers/utils"
)

var SLSAVerifiers = make(map[string]SLSAVerifier)
Expand All @@ -12,21 +13,21 @@ type SLSAVerifier interface {
// IsAuthoritativeFor checks whether a verifier can
// verify provenance for a given builder identified by its
// `BuilderID`.
IsAuthoritativeFor(builderID string) bool
IsAuthoritativeFor(builderIDName string) bool

// VerifyArtifact verifies a provenance for a supplied artifact.
VerifyArtifact(ctx context.Context,
provenance []byte, artifactHash string,
provenanceOpts *options.ProvenanceOpts,
builderOpts *options.BuilderOpts,
) ([]byte, string, error)
) ([]byte, *utils.BuilderID, error)

// VerifyImage verifies a provenance for a supplied OCI image.
VerifyImage(ctx context.Context,
provenance []byte, artifactImage string,
provenanceOpts *options.ProvenanceOpts,
builderOpts *options.BuilderOpts,
) ([]byte, string, error)
) ([]byte, *utils.BuilderID, error)
}

func RegisterVerifier(name string, verifier SLSAVerifier) {
Expand Down
Loading