Skip to content

Commit

Permalink
test: Add more ProvenanceFromEnvelope tests (#640)
Browse files Browse the repository at this point in the history
Fixes #573

---------

Signed-off-by: Ian Lewis <ianlewis@google.com>
  • Loading branch information
ianlewis committed Jun 26, 2023
1 parent f025c63 commit 90f4f23
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 80 deletions.
46 changes: 0 additions & 46 deletions verifiers/internal/gha/provenance_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package gha

import (
"os"
"testing"
"time"

Expand All @@ -12,7 +11,6 @@ import (
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"

serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
)

Expand Down Expand Up @@ -64,50 +62,6 @@ type testProvenanceV1 struct {

func (p *testProvenanceV1) Predicate() slsa1.ProvenancePredicate { return p.predicate }

func provenanceFromBytes(payload []byte) (iface.Provenance, error) {
env, err := EnvelopeFromBytes(payload)
if err != nil {
return nil, err
}
return slsaprovenance.ProvenanceFromEnvelope(env)
}

func Test_ProvenanceFromEnvelope(t *testing.T) {
t.Parallel()
tests := []struct {
name string
path string
expected error
}{
{
name: "invalid dsse: not SLSA predicate",
path: "./testdata/dsse-not-slsa.intoto.jsonl",
expected: serrors.ErrorInvalidDssePayload,
},
{
name: "slsa 1.0 invalid dsse: not SLSA predicate",
path: "./testdata/dsse-not-slsa-v1.intoto.jsonl",
expected: serrors.ErrorInvalidDssePayload,
},
// TODO(#573): add more compliance tests.
}
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 {
t.Fatalf("os.ReadFile: %v", err)
}
_, err = provenanceFromBytes(content)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected))
}
})
}
}

func Test_VerifyDigest(t *testing.T) {
t.Parallel()
tests := []struct {
Expand Down
19 changes: 9 additions & 10 deletions verifiers/internal/gha/slsaprovenance/slsaprovenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"

intoto "github.com/in-toto/in-toto-golang/in_toto"
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse"

Expand All @@ -27,31 +28,29 @@ var predicateTypeMap = map[string]provenanceConstructor{

// ProvenanceFromEnvelope returns a Provenance instance for the given DSSE Envelope.
func ProvenanceFromEnvelope(env *dsselib.Envelope) (iface.Provenance, error) {
if env.PayloadType != "application/vnd.in-toto+json" {
return nil, fmt.Errorf("%w: expected payload type 'application/vnd.in-toto+json', got '%s'",
serrors.ErrorInvalidDssePayload, env.PayloadType)
if env.PayloadType != intoto.PayloadType {
return nil, fmt.Errorf("%w: expected payload type %q, got '%s'",
serrors.ErrorInvalidDssePayload, intoto.PayloadType, env.PayloadType)
}
pyld, err := base64.StdEncoding.DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error())
}

// Get the predicateType, a required field.
pred := struct {
PredicateType string `json:"predicateType"`
}{}
// Load the in-toto attestation statement header.
pred := intoto.StatementHeader{}
if err := json.Unmarshal(pyld, &pred); err != nil {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error())
return nil, fmt.Errorf("%w: decoding json: %v", serrors.ErrorInvalidDssePayload, err)
}

// Load the appropriate structure and unmarshal.
// Verify the predicate type is one we can handle.
newProv, ok := predicateTypeMap[pred.PredicateType]
if !ok {
return nil, fmt.Errorf("%w: unexpected predicate type '%s'", serrors.ErrorInvalidDssePayload, pred.PredicateType)
}
prov, err := newProv(pyld)
if err != nil {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error())
return nil, fmt.Errorf("%w: %v", serrors.ErrorInvalidDssePayload, err)
}

return prov, nil
Expand Down
110 changes: 110 additions & 0 deletions verifiers/internal/gha/slsaprovenance/slsaprovenance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package slsaprovenance

import (
"encoding/base64"
"encoding/json"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
intoto "github.com/in-toto/in-toto-golang/in_toto"
intoto_slsav1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
"github.com/secure-systems-lab/go-securesystemslib/dsse"

serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
slsav1 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
)

func mustJSON(o any) string {
b, err := json.Marshal(o)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(b)
}

func Test_ProvenanceFromEnvelope(t *testing.T) {
t.Parallel()
tests := []struct {
name string
envelope *dsse.Envelope
path string
err error
}{
{
name: "valid dsse",
envelope: &dsse.Envelope{
PayloadType: intoto.PayloadType,
Payload: mustJSON(&slsav1.Attestation{
StatementHeader: intoto.StatementHeader{
PredicateType: intoto_slsav1.PredicateSLSAProvenance,
},
Predicate: intoto_slsav1.ProvenancePredicate{
BuildDefinition: intoto_slsav1.ProvenanceBuildDefinition{
BuildType: slsav1.BYOBBuildType,
},
},
}),
},
},
{
name: "invalid dsse: not SLSA predicate",
envelope: &dsse.Envelope{
PayloadType: intoto.PayloadType,
Payload: mustJSON(&intoto.StatementHeader{
// NOTE: Not a SLSA predicate type.
PredicateType: intoto.PredicateSPDX,
}),
},
err: serrors.ErrorInvalidDssePayload,
},
{
name: "invalid dsse: not base64",
envelope: &dsse.Envelope{
PayloadType: intoto.PayloadType,
// NOTE: Not valid base64.
Payload: "i&(*$(@&^&)))",
},
err: serrors.ErrorInvalidDssePayload,
},
{
name: "invalid dsse: not json",
envelope: &dsse.Envelope{
PayloadType: intoto.PayloadType,
// NOTE: Not valid JSON.
Payload: base64.StdEncoding.EncodeToString([]byte("{'not valid json'")),
},
err: serrors.ErrorInvalidDssePayload,
},
{
name: "invalid dsse: not in-toto",
envelope: &dsse.Envelope{
// NOTE: Not an in-toto attestation payload type,
PayloadType: "http://github.com/other/payload/type",
// NOTE: The rest of the payload should be valid.
Payload: mustJSON(&slsav1.Attestation{
StatementHeader: intoto.StatementHeader{
PredicateType: intoto_slsav1.PredicateSLSAProvenance,
},
Predicate: intoto_slsav1.ProvenancePredicate{
BuildDefinition: intoto_slsav1.ProvenanceBuildDefinition{
BuildType: slsav1.BYOBBuildType,
},
},
}),
},
err: 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()

_, err := ProvenanceFromEnvelope(tt.envelope)
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("unexpected error (-want +got):\n%s", diff)
}
})
}
}
2 changes: 1 addition & 1 deletion verifiers/internal/gha/slsaprovenance/v0.2/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
type provenanceV02 struct {
// upperEnv specifies if environment fields are in uppercase.
upperEnv bool
prov *intotoAttestation
prov *Attestation
}

// Predicate implements provenanceV02.Predicate.
Expand Down
6 changes: 3 additions & 3 deletions verifiers/internal/gha/slsaprovenance/v0.2/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ var (
byobDelegatorBuildType = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"
)

// intotoAttestation is a SLSA v0.2 in-toto attestation statement.
type intotoAttestation struct {
// Attestation is a SLSA v0.2 in-toto attestation statement.
type Attestation struct {
intoto.StatementHeader
Predicate slsa02.ProvenancePredicate `json:"predicate"`
}
Expand All @@ -46,7 +46,7 @@ func New(payload []byte) (iface.Provenance, error) {
dec := json.NewDecoder(bytes.NewReader(payload))
dec.DisallowUnknownFields()

a := &intotoAttestation{}
a := &Attestation{}
if err := dec.Decode(a); err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions verifiers/internal/gha/slsaprovenance/v1.0/byob.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import (
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
)

// byobBuildType is the base build type for BYOB delegated builders.
var byobBuildType = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"
// BYOBBuildType is the base build type for BYOB delegated builders.
var BYOBBuildType = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"

// BYOBProvenance is SLSA v1.0 provenance for the slsa-github-generator BYOB build type.
type BYOBProvenance struct {
prov *intotoAttestation
prov *Attestation
}

// Predicate implements ProvenanceV02.Predicate.
Expand Down
4 changes: 2 additions & 2 deletions verifiers/internal/gha/slsaprovenance/v1.0/container_based.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package v1

// containerBasedBuildType is the build type for the container-based builder and is based on BYOB.
var containerBasedBuildType = "https://slsa.dev/container-based-build/v0.1?draft"
// ContainerBasedBuildType is the build type for the container-based builder and is based on BYOB.
var ContainerBasedBuildType = "https://slsa.dev/container-based-build/v0.1?draft"

// ContainerBasedProvenance is provenance generated by the container-based builder.
type ContainerBasedProvenance struct {
Expand Down
9 changes: 5 additions & 4 deletions verifiers/internal/gha/slsaprovenance/v1.0/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
)

type intotoAttestation struct {
// Attestation is an in-toto SLSA v1.0 attestation statement.
type Attestation struct {
intoto.StatementHeader
Predicate slsa1.ProvenancePredicate `json:"predicate"`
}
Expand All @@ -30,17 +31,17 @@ func New(payload []byte) (iface.Provenance, error) {
dec := json.NewDecoder(bytes.NewReader(payload))
dec.DisallowUnknownFields()

a := &intotoAttestation{}
a := &Attestation{}
if err := dec.Decode(a); err != nil {
return nil, err
}

switch a.Predicate.BuildDefinition.BuildType {
case byobBuildType:
case BYOBBuildType:
return &BYOBProvenance{
prov: a,
}, nil
case containerBasedBuildType:
case ContainerBasedBuildType:
return &ContainerBasedProvenance{
BYOBProvenance: &BYOBProvenance{
prov: a,
Expand Down

This file was deleted.

10 changes: 0 additions & 10 deletions verifiers/internal/gha/testdata/dsse-not-slsa.intoto.jsonl

This file was deleted.

0 comments on commit 90f4f23

Please sign in to comment.