mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-02-14 17:49:58 +00:00
feat: Verification for when sha1 is specified in BYOB TRW (#641)
Fixes #600 --------- Signed-off-by: Ian Lewis <ianlewis@google.com> Signed-off-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com> Co-authored-by: laurentsimon <64505099+laurentsimon@users.noreply.github.com>
This commit is contained in:
@@ -9,6 +9,7 @@ var (
|
||||
ErrorMismatchPackageName = errors.New("package name does not match provenance")
|
||||
ErrorMismatchBuilderID = errors.New("builderID does not match provenance")
|
||||
ErrorInvalidBuilderID = errors.New("builderID is invalid")
|
||||
ErrorInvalidBuildType = errors.New("buildType is invalid")
|
||||
ErrorMismatchSource = errors.New("source used to generate the binary does not match provenance")
|
||||
ErrorMismatchWorkflowInputs = errors.New("workflow input does not match")
|
||||
ErrorMalformedURI = errors.New("URI is malformed")
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
"strings"
|
||||
|
||||
fulcio "github.com/sigstore/fulcio/pkg/certificate"
|
||||
|
||||
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/gha/slsaprovenance/common"
|
||||
ghacommon "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
)
|
||||
|
||||
@@ -27,18 +28,18 @@ var (
|
||||
)
|
||||
|
||||
var defaultArtifactTrustedReusableWorkflows = map[string]bool{
|
||||
common.GenericGeneratorBuilderID: true,
|
||||
common.GoBuilderID: true,
|
||||
common.ContainerBasedBuilderID: true,
|
||||
ghacommon.GenericGeneratorBuilderID: true,
|
||||
ghacommon.GoBuilderID: true,
|
||||
ghacommon.ContainerBasedBuilderID: true,
|
||||
}
|
||||
|
||||
var defaultContainerTrustedReusableWorkflows = map[string]bool{
|
||||
common.ContainerGeneratorBuilderID: true,
|
||||
ghacommon.ContainerGeneratorBuilderID: true,
|
||||
}
|
||||
|
||||
var defaultBYOBReusableWorkflows = map[string]bool{
|
||||
common.GenericDelegatorBuilderID: true,
|
||||
common.GenericLowPermsDelegatorBuilderID: true,
|
||||
ghacommon.GenericDelegatorBuilderID: true,
|
||||
ghacommon.GenericLowPermsDelegatorBuilderID: true,
|
||||
}
|
||||
|
||||
var JReleaserRepository = httpsGithubCom + jReleaserActionRepository
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"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/gha/slsaprovenance"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
@@ -68,6 +69,7 @@ func (b *BundleBytes) UnmarshalJSON(data []byte) error {
|
||||
type Npm struct {
|
||||
ctx context.Context
|
||||
root *TrustedRoot
|
||||
verifiedBuilderID *utils.TrustedBuilderID
|
||||
verifiedProvenanceAtt *SignedAttestation
|
||||
verifiedPublishAtt *SignedAttestation
|
||||
provenanceAttestation *attestation
|
||||
@@ -93,8 +95,9 @@ func NpmNew(ctx context.Context, root *TrustedRoot, attestationBytes []byte) (*N
|
||||
return nil, err
|
||||
}
|
||||
return &Npm{
|
||||
ctx: ctx,
|
||||
root: root,
|
||||
ctx: ctx,
|
||||
root: root,
|
||||
|
||||
provenanceAttestation: prov,
|
||||
publishAttestation: pub,
|
||||
}, nil
|
||||
@@ -251,7 +254,7 @@ func (n *Npm) verifyPackageName(name *string) error {
|
||||
}
|
||||
|
||||
// Verify subject name in provenance.
|
||||
if err := verifyProvenanceSubjectName(n.verifiedProvenanceAtt, *name); err != nil {
|
||||
if err := verifyProvenanceSubjectName(n.verifiedBuilderID, n.verifiedProvenanceAtt, *name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -274,7 +277,7 @@ func (n *Npm) verifyPackageVersion(version *string) error {
|
||||
}
|
||||
|
||||
// Verify subject version in provenance.
|
||||
if err := verifyProvenanceSubjectVersion(n.verifiedProvenanceAtt, *version); err != nil {
|
||||
if err := verifyProvenanceSubjectVersion(n.verifiedBuilderID, n.verifiedProvenanceAtt, *version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -291,6 +294,25 @@ func (n *Npm) verifyPackageVersion(version *string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Npm) verifyBuilderID(
|
||||
provenanceOpts *options.ProvenanceOpts,
|
||||
builderOpts *options.BuilderOpts,
|
||||
defaultBuilders map[string]bool,
|
||||
) (*utils.TrustedBuilderID, error) {
|
||||
// Verify certificate information.
|
||||
builder, err := verifyNpmEnvAndCert(
|
||||
n.ProvenanceEnvelope(),
|
||||
n.ProvenanceLeafCertificate(),
|
||||
provenanceOpts, builderOpts,
|
||||
defaultBuilders,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.verifiedBuilderID = builder
|
||||
return builder, err
|
||||
}
|
||||
|
||||
func verifyPublishPredicateVersion(att *SignedAttestation, expectedVersion string) error {
|
||||
_, version, err := getPublishPredicateData(att)
|
||||
if err != nil {
|
||||
@@ -336,8 +358,8 @@ func getPublishPredicateData(att *SignedAttestation) (string, string, error) {
|
||||
return statement.Predicate.Name, statement.Predicate.Version, nil
|
||||
}
|
||||
|
||||
func verifyProvenanceSubjectVersion(att *SignedAttestation, expectedVersion string) error {
|
||||
subject, err := getSubject(att)
|
||||
func verifyProvenanceSubjectVersion(b *utils.TrustedBuilderID, att *SignedAttestation, expectedVersion string) error {
|
||||
subject, err := getSubject(b, att)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -378,15 +400,15 @@ func verifyPublishSubjectName(att *SignedAttestation, expectedName string) error
|
||||
return verifyName(name, expectedName)
|
||||
}
|
||||
|
||||
func verifyProvenanceSubjectName(att *SignedAttestation, expectedName string) error {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(att.Envelope)
|
||||
func verifyProvenanceSubjectName(b *utils.TrustedBuilderID, att *SignedAttestation, expectedName string) error {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(b.Name(), att.Envelope)
|
||||
if err != nil {
|
||||
return nil
|
||||
return fmt.Errorf("reading provenance: %w", err)
|
||||
}
|
||||
|
||||
subjects, err := prov.Subjects()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w", serrors.ErrorInvalidDssePayload)
|
||||
return fmt.Errorf("%w: %w", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
if len(subjects) != 1 {
|
||||
return fmt.Errorf("%w: expected 1 subject, got %v", serrors.ErrorInvalidDssePayload, len(subjects))
|
||||
@@ -443,8 +465,8 @@ func getPackageNameAndVersion(name string) (string, string, error) {
|
||||
return pkgname, pkgtag, nil
|
||||
}
|
||||
|
||||
func getSubject(att *SignedAttestation) (string, error) {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(att.Envelope)
|
||||
func getSubject(b *utils.TrustedBuilderID, att *SignedAttestation) (string, error) {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(b.Name(), att.Envelope)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -2,17 +2,20 @@ package gha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
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/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
)
|
||||
|
||||
func Test_verifyName(t *testing.T) {
|
||||
@@ -81,7 +84,15 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJuYW1lIjogIkBsYXVyZW50c2ltb24vcHJvdmVuYW5jZS1ucG0tdGVzdCIsCiAgICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgICAicmVnaXN0cnkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmciCiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
|
||||
"predicate": {
|
||||
"name": "@laurentsimon/provenance-npm-test",
|
||||
"version": "1.0.0",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0.0",
|
||||
@@ -91,7 +102,15 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJuYW1lIjogIkBsYXVyZW50c2ltb24vcHJvdmVuYW5jZS1ucG0tdGVzdCIsCiAgICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgICAicmVnaXN0cnkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmciCiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
|
||||
"predicate": {
|
||||
"name": "@laurentsimon/provenance-npm-test",
|
||||
"version": "1.0.0",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0",
|
||||
@@ -102,7 +121,15 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJuYW1lIjogIkBsYXVyZW50c2ltb24vcHJvdmVuYW5jZS1ucG0tdGVzdCIsCiAgICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgICAicmVnaXN0cnkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmciCiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
|
||||
"predicate": {
|
||||
"name": "@laurentsimon/provenance-npm-test",
|
||||
"version": "1.0.0",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0.1",
|
||||
@@ -113,7 +140,15 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJuYW1lIjogIkBsYXVyZW50c2ltb24vcHJvdmVuYW5jZS1ucG0tdGVzdCIsCiAgICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgICAicmVnaXN0cnkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmciCiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
|
||||
"predicate": {
|
||||
"name": "@laurentsimon/provenance-npm-test",
|
||||
"version": "1.0.0",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.1.0",
|
||||
@@ -124,7 +159,15 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJwcmVkaWNhdGVUeXBlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJuYW1lIjogIkBsYXVyZW50c2ltb24vcHJvdmVuYW5jZS1ucG0tdGVzdCIsCiAgICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgICAicmVnaXN0cnkiOiAiaHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmciCiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
|
||||
"predicate": {
|
||||
"name": "@laurentsimon/provenance-npm-test",
|
||||
"version": "1.0.0",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "2.0.0",
|
||||
@@ -138,8 +181,8 @@ func Test_verifyPublishSubjectVersion(t *testing.T) {
|
||||
|
||||
err := verifyPublishSubjectVersion(tt.att, tt.version)
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -149,60 +192,151 @@ func Test_verifyProvenanceSubjectVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
att *SignedAttestation
|
||||
version string
|
||||
err error
|
||||
name string
|
||||
builderID string
|
||||
att *SignedAttestation
|
||||
version string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "correct version",
|
||||
name: "correct version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0.0",
|
||||
},
|
||||
{
|
||||
name: "incorrect subset version",
|
||||
name: "incorrect subset version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0",
|
||||
err: serrors.ErrorMismatchPackageVersion,
|
||||
},
|
||||
{
|
||||
name: "incorrect patch version",
|
||||
name: "incorrect patch version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.0.1",
|
||||
err: serrors.ErrorMismatchPackageVersion,
|
||||
},
|
||||
{
|
||||
name: "incorrect minor version",
|
||||
name: "incorrect minor version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "1.1.0",
|
||||
err: serrors.ErrorMismatchPackageVersion,
|
||||
},
|
||||
{
|
||||
name: "incorrect major version",
|
||||
name: "incorrect major version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
version: "2.0.0",
|
||||
@@ -213,11 +347,14 @@ func Test_verifyProvenanceSubjectVersion(t *testing.T) {
|
||||
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()
|
||||
builderID, err := utils.TrustedBuilderIDNew(tt.builderID, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err := verifyProvenanceSubjectVersion(tt.att, tt.version)
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
err = verifyProvenanceSubjectVersion(builderID, tt.att, tt.version)
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -439,49 +576,122 @@ func Test_verifyProvenanceSubjectName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
att *SignedAttestation
|
||||
subject string
|
||||
err error
|
||||
name string
|
||||
builderID string
|
||||
att *SignedAttestation
|
||||
subject string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "correct name",
|
||||
name: "correct name",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
subject: "@laurentsimon/provenance-npm-test",
|
||||
},
|
||||
{
|
||||
name: "incorrect name",
|
||||
name: "incorrect name",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
subject: "wrong name",
|
||||
err: serrors.ErrorMismatchPackageName,
|
||||
},
|
||||
{
|
||||
name: "incorrect scope",
|
||||
name: "incorrect scope",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
subject: "laurentsimon/provenance-npm-test",
|
||||
err: serrors.ErrorMismatchPackageName,
|
||||
},
|
||||
{
|
||||
name: "incorrect with version",
|
||||
name: "incorrect with version",
|
||||
builderID: common.NpmCLIHostedBuilderID,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
PayloadType: "application/vnd.in-toto+json",
|
||||
Payload: "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJwa2c6bnBtLyU0MGxhdXJlbnRzaW1vbi9wcm92ZW5hbmNlLW5wbS10ZXN0QDEuMC4wIiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogIjI5ZDE5ZjI2MjMzZjQ0NDEzMjg0MTJiMzRmZDczZWQxMDRlY2ZlZjYyZjE0MDk3ODkwY2NjZjc0NTViNTIxYjY1YzVhY2ZmODUxODQ5ZmFhODVjODUzOTVhYTIyZDQwMTQzNmYwMWYzYWZiNjFiMTljNzgwZTkwNmM4OGM3ZjIwIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGkvZ2hhQHYxIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9jbGlAOS41LjAiCiAgICB9CiAgfQp9Cg==",
|
||||
Payload: base64.StdEncoding.EncodeToString([]byte(`{
|
||||
"_type": "https://in-toto.io/Statement/v0.1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:npm/%40laurentsimon/provenance-npm-test@1.0.0",
|
||||
"digest": {
|
||||
"sha512": "29d19f26233f4441328412b34fd73ed104ecfef62f14097890cccf7455b521b65c5acff851849faa85c85395aa22d401436f01f3afb61b19c780e906c88c7f20"
|
||||
}
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
||||
"predicate": {
|
||||
"buildType": "https://github.com/npm/cli/gha@v1",
|
||||
"builder": {
|
||||
"id": "https://github.com/npm/cli@9.5.0"
|
||||
}
|
||||
}
|
||||
}`)),
|
||||
},
|
||||
},
|
||||
subject: "@laurentsimon/provenance-npm-test@1.0.0",
|
||||
@@ -492,11 +702,14 @@ func Test_verifyProvenanceSubjectName(t *testing.T) {
|
||||
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()
|
||||
builderID, err := utils.TrustedBuilderIDNew(tt.builderID, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err := verifyProvenanceSubjectName(tt.att, tt.subject)
|
||||
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
err = verifyProvenanceSubjectName(builderID, tt.att, tt.subject)
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -572,6 +785,11 @@ func Test_verifyPackageName(t *testing.T) {
|
||||
Envelope: env,
|
||||
}
|
||||
|
||||
npm.verifiedBuilderID, err = utils.TrustedBuilderIDNew(common.NpmCLIHostedBuilderID, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = npm.verifyPackageName(&tt.subject)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
@@ -642,6 +860,11 @@ func Test_verifyPackageVersion(t *testing.T) {
|
||||
Envelope: env,
|
||||
}
|
||||
|
||||
npm.verifiedBuilderID, err = utils.TrustedBuilderIDNew(common.NpmCLIHostedBuilderID, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
env, err = getEnvelopeFromBundleBytes(npm.publishAttestation.BundleBytes)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("getEnvelopeFromBundleBytes: %w", err))
|
||||
@@ -886,8 +1109,8 @@ func Test_NpmNew(t *testing.T) {
|
||||
}
|
||||
|
||||
_, err = NpmNew(ctx, trustedRoot, content)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func verifyBuilderIDLooseMatch(prov iface.Provenance, expectedBuilderID string)
|
||||
}
|
||||
|
||||
// Verify source URI in provenance statement.
|
||||
func verifySourceURI(prov iface.Provenance, expectedSourceURI string, allowNoMaterialRef bool) error {
|
||||
func verifySourceURI(prov iface.Provenance, expectedSourceURI string) error {
|
||||
source := utils.NormalizeGitURI(expectedSourceURI)
|
||||
|
||||
// We expect github.com URIs only.
|
||||
@@ -97,7 +97,7 @@ func verifySourceURI(prov iface.Provenance, expectedSourceURI string, allowNoMat
|
||||
return err
|
||||
}
|
||||
if triggerURI != source {
|
||||
return fmt.Errorf("%w: expected source '%s' in configSource.uri, got %q", serrors.ErrorMismatchSource,
|
||||
return fmt.Errorf("%w: expected trigger %q to match source-uri %q", serrors.ErrorMismatchSource,
|
||||
source, fullTriggerURI)
|
||||
}
|
||||
// We expect the trigger URI to always have a ref.
|
||||
@@ -116,22 +116,37 @@ func verifySourceURI(prov iface.Provenance, expectedSourceURI string, allowNoMat
|
||||
return err
|
||||
}
|
||||
if sourceURI != source {
|
||||
return fmt.Errorf("%w: expected source '%s' in material section, got %q", serrors.ErrorMismatchSource,
|
||||
source, fullSourceURI)
|
||||
return fmt.Errorf("%w: expected source %q to match source-uri %q", serrors.ErrorMismatchSource,
|
||||
fullSourceURI, source)
|
||||
}
|
||||
|
||||
buildType, err := prov.BuildType()
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking buildType: %v", err)
|
||||
}
|
||||
if sourceRef == "" {
|
||||
if allowNoMaterialRef {
|
||||
// NOTE: this is an exception for npm packages built before GA,
|
||||
// see https://github.com/slsa-framework/slsa-verifier/issues/492.
|
||||
// We don't need to compare the ref since materialSourceURI does not contain it.
|
||||
// NOTE: this is an exception for npm packages built before GA,
|
||||
// see https://github.com/slsa-framework/slsa-verifier/issues/492.
|
||||
// We don't need to compare the ref since materialSourceURI does not contain it.
|
||||
if buildType == common.NpmCLIBuildTypeV1 {
|
||||
return nil
|
||||
}
|
||||
// NOTE: BYOB builders can build from a different ref than the triggering ref.
|
||||
// This most often happens when a TRW makes a commit as part of the release process.
|
||||
// NOTE: Currently only building from a different git sha is supported
|
||||
// which means the sourceRef is empty. Building from an arbitrary ref
|
||||
// is currently not supported.
|
||||
if buildType == common.BYOBBuildTypeV0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%w: missing ref: %q", serrors.ErrorMalformedURI, fullSourceURI)
|
||||
}
|
||||
|
||||
// Last, verify that both fields match. We expect that the trigger URI and
|
||||
// the source URI match but the ref used to trigger the build and source ref
|
||||
// could be different.
|
||||
if fullTriggerURI != fullSourceURI {
|
||||
return fmt.Errorf("%w: material and config URIs do not match: %q != %q",
|
||||
return fmt.Errorf("%w: source and trigger URIs do not match: %q != %q",
|
||||
serrors.ErrorInvalidDssePayload,
|
||||
fullTriggerURI, fullSourceURI)
|
||||
}
|
||||
@@ -191,9 +206,9 @@ func VerifyProvenanceSignature(ctx context.Context, trustedRoot *TrustedRoot,
|
||||
|
||||
// VerifyNpmPackageProvenance verifies provenance for an npm package.
|
||||
func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentity,
|
||||
provenanceOpts *options.ProvenanceOpts, isTrustedBuilder bool,
|
||||
provenanceOpts *options.ProvenanceOpts, trustedBuilderID *utils.TrustedBuilderID, isTrustedBuilder bool,
|
||||
) error {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(env)
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(trustedBuilderID.Name(), env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -239,7 +254,7 @@ func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentit
|
||||
}
|
||||
|
||||
// Also, the GitHub context is not recorded for the default builder.
|
||||
if err := VerifyProvenanceCommonOptions(prov, provenanceOpts, true); err != nil {
|
||||
if err := VerifyProvenanceCommonOptions(prov, provenanceOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -271,9 +286,8 @@ func isValidDelegatorBuilderID(prov iface.Provenance) error {
|
||||
}
|
||||
|
||||
// VerifyProvenance verifies the provenance for the given DSSE envelope.
|
||||
func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceOpts, byob bool,
|
||||
) error {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(env)
|
||||
func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceOpts, trustedBuilderID *utils.TrustedBuilderID, byob bool) error {
|
||||
prov, err := slsaprovenance.ProvenanceFromEnvelope(trustedBuilderID.Name(), env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -295,15 +309,13 @@ func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceO
|
||||
}
|
||||
}
|
||||
|
||||
return VerifyProvenanceCommonOptions(prov, provenanceOpts, false)
|
||||
return VerifyProvenanceCommonOptions(prov, provenanceOpts)
|
||||
}
|
||||
|
||||
// VerifyProvenanceCommonOptions verifies the given provenance.
|
||||
func VerifyProvenanceCommonOptions(prov iface.Provenance, provenanceOpts *options.ProvenanceOpts,
|
||||
allowNoMaterialRef bool,
|
||||
) error {
|
||||
func VerifyProvenanceCommonOptions(prov iface.Provenance, provenanceOpts *options.ProvenanceOpts) error {
|
||||
// Verify source.
|
||||
if err := verifySourceURI(prov, provenanceOpts.ExpectedSourceURI, allowNoMaterialRef); err != nil {
|
||||
if err := verifySourceURI(prov, provenanceOpts.ExpectedSourceURI); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||
slsacommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
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/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
type testProvenance struct {
|
||||
builderID string
|
||||
buildType string
|
||||
sourceURI string
|
||||
triggerURI string
|
||||
subjects []intoto.Subject
|
||||
@@ -31,6 +33,7 @@ type testProvenance struct {
|
||||
}
|
||||
|
||||
func (p *testProvenance) BuilderID() (string, error) { return p.builderID, nil }
|
||||
func (p *testProvenance) BuildType() (string, error) { return p.buildType, nil }
|
||||
func (p *testProvenance) SourceURI() (string, error) { return p.sourceURI, nil }
|
||||
func (p *testProvenance) TriggerURI() (string, error) { return p.triggerURI, nil }
|
||||
func (p *testProvenance) Subjects() ([]intoto.Subject, error) { return p.subjects, nil }
|
||||
@@ -75,7 +78,7 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha1": "4506290e2e8feb1f34b27a044f7cc863c830ef6b",
|
||||
},
|
||||
},
|
||||
@@ -90,7 +93,7 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha1": "4506290e2e8feb1f34b27a044f7cc863c830ef6b",
|
||||
},
|
||||
},
|
||||
@@ -111,7 +114,7 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "0ae7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
@@ -125,7 +128,7 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "0ae7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
@@ -138,17 +141,17 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "03e7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha1": "4506290e2e8feb1f34b27a044f7cc863c830ef6b",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "0ae7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
@@ -161,17 +164,17 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "03e7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "0ae7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha1": "4506290e2e8feb1f34b27a044f7cc863c830ef6b",
|
||||
},
|
||||
},
|
||||
@@ -184,17 +187,17 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
prov: &testProvenance{
|
||||
subjects: []intoto.Subject{
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "03e7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha256": "0ae7e4fa71686538440012ee36a2634dbaa19df2dd16a466f52411fb348bbc4e",
|
||||
},
|
||||
},
|
||||
{
|
||||
Digest: common.DigestSet{
|
||||
Digest: slsacommon.DigestSet{
|
||||
"sha1": "4506290e2e8feb1f34b27a044f7cc863c830ef6b",
|
||||
},
|
||||
},
|
||||
@@ -220,6 +223,7 @@ func Test_verifySourceURI(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
provBuildType string
|
||||
provMaterialsURI string
|
||||
provTriggerURI string
|
||||
expectedSourceURI string
|
||||
@@ -283,14 +287,37 @@ func Test_verifySourceURI(t *testing.T) {
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
},
|
||||
{
|
||||
name: "match source no git no material ref",
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo",
|
||||
allowNoMaterialRef: true,
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
name: "match source no git no material ref (npm)",
|
||||
provBuildType: common.NpmCLIBuildTypeV1,
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo",
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
},
|
||||
{
|
||||
name: "match source no git no material ref ref not allowed",
|
||||
name: "mismatch source material ref (npm)",
|
||||
provBuildType: common.NpmCLIBuildTypeV1,
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo@v1.2.4",
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "match source no git no material ref (byob)",
|
||||
provBuildType: common.BYOBBuildTypeV0,
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo",
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
},
|
||||
{
|
||||
name: "mismatch source material ref (byob)",
|
||||
provBuildType: common.BYOBBuildTypeV0,
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo@v1.2.4",
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "match source no git no material ref",
|
||||
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
|
||||
provMaterialsURI: "git+https://github.com/some/repo",
|
||||
expectedSourceURI: "https://github.com/some/repo",
|
||||
@@ -358,11 +385,12 @@ func Test_verifySourceURI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &testProvenance{
|
||||
buildType: tt.provBuildType,
|
||||
sourceURI: tt.provMaterialsURI,
|
||||
triggerURI: tt.provTriggerURI,
|
||||
}
|
||||
|
||||
err := verifySourceURI(prov02, tt.expectedSourceURI, tt.allowNoMaterialRef)
|
||||
err := verifySourceURI(prov02, tt.expectedSourceURI)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
30
verifiers/internal/gha/slsaprovenance/common/buildtypes.go
Normal file
30
verifiers/internal/gha/slsaprovenance/common/buildtypes.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package common
|
||||
|
||||
var (
|
||||
// BYOBBuildTypeV0 is the base buildType for BYOB delegated builders.
|
||||
BYOBBuildTypeV0 = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"
|
||||
|
||||
// ContainerBasedBuildTypeV01Draft is the buildType for the container-based builder.
|
||||
ContainerBasedBuildTypeV01Draft = "https://slsa.dev/container-based-build/v0.1?draft"
|
||||
|
||||
// GoBuilderBuildTypeV1 is the buildType for the Go builder.
|
||||
GoBuilderBuildTypeV1 = "https://github.com/slsa-framework/slsa-github-generator/go@v1"
|
||||
|
||||
// GenericGeneratorBuildTypeV1 is the buildType for the generic generator.
|
||||
GenericGeneratorBuildTypeV1 = "https://github.com/slsa-framework/slsa-github-generator/generic@v1"
|
||||
|
||||
// ContainerGeneratorBuildTypeV1 is the buildType for the container generator.
|
||||
ContainerGeneratorBuildTypeV1 = "https://github.com/slsa-framework/slsa-github-generator/container@v1"
|
||||
|
||||
// NpmCLIBuildTypeV1 is the buildType for provenance generated by the npm cli.
|
||||
NpmCLIBuildTypeV1 = "https://github.com/npm/cli/gha@v1"
|
||||
)
|
||||
|
||||
// Legacy buildTypes.
|
||||
var (
|
||||
// LegacyGoBuilderBuildTypeV1 is a legacy Go builder buildType.
|
||||
LegacyGoBuilderBuildTypeV1 = "https://github.com/slsa-framework/slsa-github-generator-go@v1"
|
||||
|
||||
// LegacyBuilderBuildTypeV1 is a legacy generic build type for slsa-github-generator.
|
||||
LegacyBuilderBuildTypeV1 = "https://github.com/slsa-framework/slsa-github-generator@v1"
|
||||
)
|
||||
@@ -11,6 +11,9 @@ type Provenance interface {
|
||||
// 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) of the source material.
|
||||
SourceURI() (string, error)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"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"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
@@ -17,40 +16,43 @@ import (
|
||||
)
|
||||
|
||||
// provenanceConstructor creates a new Provenance instance for the given payload as a json Decoder.
|
||||
type provenanceConstructor func(payload []byte) (iface.Provenance, error)
|
||||
type provenanceConstructor func(builderID string, payload []byte) (iface.Provenance, error)
|
||||
|
||||
// predicateTypeMap stores the different provenance version types. It is a map of
|
||||
// predicate type -> ProvenanceConstructor.
|
||||
var predicateTypeMap = map[string]provenanceConstructor{
|
||||
common.ProvenanceV02Type: slsav02.New,
|
||||
slsa1.PredicateSLSAProvenance: slsav1.New,
|
||||
common.ProvenanceV02Type: slsav02.New,
|
||||
common.ProvenanceV1Type: slsav1.New,
|
||||
}
|
||||
|
||||
// ProvenanceFromEnvelope returns a Provenance instance for the given DSSE Envelope.
|
||||
func ProvenanceFromEnvelope(env *dsselib.Envelope) (iface.Provenance, error) {
|
||||
// ProvenanceFromEnvelope returns a Provenance instance for the given builder
|
||||
// ID and DSSE Envelope. The builder ID is retrieved from the signing certificate
|
||||
// rather than from the payload itself in order to support delegated builders.
|
||||
func ProvenanceFromEnvelope(builderID string, env *dsselib.Envelope) (iface.Provenance, error) {
|
||||
if env.PayloadType != intoto.PayloadType {
|
||||
return nil, fmt.Errorf("%w: expected payload type %q, got '%s'",
|
||||
return nil, fmt.Errorf("%w: expected payload type %q, got %q",
|
||||
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())
|
||||
return nil, fmt.Errorf("%w: %w", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
|
||||
// Load the in-toto attestation statement header.
|
||||
pred := intoto.StatementHeader{}
|
||||
if err := json.Unmarshal(pyld, &pred); err != nil {
|
||||
return nil, fmt.Errorf("%w: decoding json: %v", serrors.ErrorInvalidDssePayload, err)
|
||||
return nil, fmt.Errorf("%w: decoding json: %w", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
return nil, fmt.Errorf("%w: unexpected predicate type %q", serrors.ErrorInvalidDssePayload, pred.PredicateType)
|
||||
}
|
||||
prov, err := newProv(pyld)
|
||||
prov, err := newProv(builderID, pyld)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", serrors.ErrorInvalidDssePayload, err)
|
||||
return nil, fmt.Errorf("%w: %w", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
|
||||
return prov, nil
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
slsav1 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
|
||||
)
|
||||
|
||||
@@ -26,13 +27,15 @@ func mustJSON(o any) string {
|
||||
func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
envelope *dsse.Envelope
|
||||
path string
|
||||
err error
|
||||
name string
|
||||
builderID string
|
||||
envelope *dsse.Envelope
|
||||
path string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "valid dsse",
|
||||
name: "valid dsse",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
envelope: &dsse.Envelope{
|
||||
PayloadType: intoto.PayloadType,
|
||||
Payload: mustJSON(&slsav1.Attestation{
|
||||
@@ -48,7 +51,8 @@ func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid dsse: not SLSA predicate",
|
||||
name: "invalid dsse: not SLSA predicate",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
envelope: &dsse.Envelope{
|
||||
PayloadType: intoto.PayloadType,
|
||||
Payload: mustJSON(&intoto.StatementHeader{
|
||||
@@ -59,7 +63,8 @@ func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "invalid dsse: not base64",
|
||||
name: "invalid dsse: not base64",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
envelope: &dsse.Envelope{
|
||||
PayloadType: intoto.PayloadType,
|
||||
// NOTE: Not valid base64.
|
||||
@@ -68,7 +73,8 @@ func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "invalid dsse: not json",
|
||||
name: "invalid dsse: not json",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
envelope: &dsse.Envelope{
|
||||
PayloadType: intoto.PayloadType,
|
||||
// NOTE: Not valid JSON.
|
||||
@@ -77,7 +83,8 @@ func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "invalid dsse: not in-toto",
|
||||
name: "invalid dsse: not in-toto",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
envelope: &dsse.Envelope{
|
||||
// NOTE: Not an in-toto attestation payload type,
|
||||
PayloadType: "http://github.com/other/payload/type",
|
||||
@@ -101,7 +108,7 @@ func Test_ProvenanceFromEnvelope(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := ProvenanceFromEnvelope(tt.envelope)
|
||||
_, err := ProvenanceFromEnvelope(tt.builderID, tt.envelope)
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Errorf("unexpected error (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,16 @@ import (
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
func newLegacyBuilderProvenance(a *Attestation) iface.Provenance {
|
||||
return &provenanceV02{
|
||||
upperEnv: false,
|
||||
prov: a,
|
||||
}
|
||||
}
|
||||
|
||||
// provenanceV02 implements basic logic for SLSA v0.2 provenance.
|
||||
type provenanceV02 struct {
|
||||
// upperEnv specifies if environment fields are in uppercase.
|
||||
@@ -28,6 +36,11 @@ func (p *provenanceV02) BuilderID() (string, error) {
|
||||
return p.prov.Predicate.Builder.ID, nil
|
||||
}
|
||||
|
||||
// BuildType implements Provenance.BuildType.
|
||||
func (p *provenanceV02) BuildType() (string, error) {
|
||||
return p.prov.Predicate.BuildType, nil
|
||||
}
|
||||
|
||||
// SourceURI implements Provenance.SourceURI.
|
||||
func (p *provenanceV02) SourceURI() (string, error) {
|
||||
if len(p.prov.Predicate.Materials) == 0 {
|
||||
|
||||
91
verifiers/internal/gha/slsaprovenance/v0.2/byob.go
Normal file
91
verifiers/internal/gha/slsaprovenance/v0.2/byob.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package v02
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
)
|
||||
|
||||
// byobProvenance is SLSA v0.2 provenance created by a BYOB builder.
|
||||
type byobProvenance struct {
|
||||
*provenanceV02
|
||||
}
|
||||
|
||||
func newBYOBProvenance(att *Attestation) iface.Provenance {
|
||||
return &byobProvenance{
|
||||
provenanceV02: &provenanceV02{
|
||||
prov: att,
|
||||
upperEnv: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetBranch implements Provenance.GetBranch.
|
||||
func (p *byobProvenance) GetBranch() (string, error) {
|
||||
sourceURI, err := p.SourceURI()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading source uri: %w", err)
|
||||
}
|
||||
|
||||
// Returns the branch from the source URI if available.
|
||||
_, ref, err := utils.ParseGitURIAndRef(sourceURI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing source uri: %w", err)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
return "", fmt.Errorf("%w: unable to get ref for source %q",
|
||||
serrors.ErrorInvalidDssePayload, sourceURI)
|
||||
}
|
||||
|
||||
refType, _ := utils.ParseGitRef(ref)
|
||||
switch refType {
|
||||
case "heads": // branch.
|
||||
// NOTE: We return the full git ref.
|
||||
return ref, nil
|
||||
case "tags":
|
||||
// NOTE: If the ref type is a tag we want to try to parse out the branch from the tag.
|
||||
sysParams, ok := p.prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
return common.GetBranch(sysParams, true)
|
||||
default:
|
||||
return "", fmt.Errorf("%w: unknown ref type %q for ref %q",
|
||||
serrors.ErrorInvalidDssePayload, refType, ref)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTag implements Provenance.GetTag.
|
||||
func (p *byobProvenance) GetTag() (string, error) {
|
||||
sourceURI, err := p.SourceURI()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading source uri: %w", err)
|
||||
}
|
||||
|
||||
// Returns the branch from the source URI if available.
|
||||
_, ref, err := utils.ParseGitURIAndRef(sourceURI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing source uri: %w", err)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
return "", fmt.Errorf("%w: unable to get ref for source %q",
|
||||
serrors.ErrorInvalidDssePayload, sourceURI)
|
||||
}
|
||||
|
||||
refType, _ := utils.ParseGitRef(ref)
|
||||
switch refType {
|
||||
case "heads": // branch.
|
||||
return "", nil
|
||||
case "tags":
|
||||
// NOTE: We return the full git ref.
|
||||
return ref, nil
|
||||
default:
|
||||
return "", fmt.Errorf("%w: unknown ref type %q for ref %q",
|
||||
serrors.ErrorInvalidDssePayload, refType, ref)
|
||||
}
|
||||
}
|
||||
280
verifiers/internal/gha/slsaprovenance/v0.2/byob_test.go
Normal file
280
verifiers/internal/gha/slsaprovenance/v0.2/byob_test.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package v02
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsacommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
func Test_byobProvenance_GetBranch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
prov iface.Provenance
|
||||
branch string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty provenance",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri @ refs/heads/main",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
branch: "refs/heads/main",
|
||||
},
|
||||
{
|
||||
name: "environment GITHUB_REF @ refs/heads/main",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri @ refs/tags/v1.0.0",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_BASE_REF": "",
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
"GITHUB_EVENT_NAME": "push",
|
||||
"GITHUB_EVENT_PAYLOAD": map[string]any{
|
||||
"base_ref": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
branch: "",
|
||||
},
|
||||
{
|
||||
name: "environment GITHUB_REF @ ref/tags/v1.0.0",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_BASE_REF": "",
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
"GITHUB_EVENT_NAME": "push",
|
||||
"GITHUB_EVENT_PAYLOAD": map[string]any{
|
||||
"base_ref": nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri no ref",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
},
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tt := testCases[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
branch, err := tt.prov.GetBranch()
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := branch, tt.branch; got != want {
|
||||
t.Fatalf("unexpected branch, got: %q, want: %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_byobProvenance_GetTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
prov iface.Provenance
|
||||
tag string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty provenance",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri @ refs/heads/main",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
tag: "",
|
||||
},
|
||||
{
|
||||
name: "environment GITHUB_REF @ refs/heads/main",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri @ refs/tags/v1.0.0",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
tag: "refs/tags/v1.0.0",
|
||||
},
|
||||
{
|
||||
name: "environment GITHUB_REF @ ref/tags/v1.0.0",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "materials uri no ref",
|
||||
prov: newBYOBProvenance(
|
||||
&Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa02.ProvenancePredicate{
|
||||
Invocation: slsa02.ProvenanceInvocation{
|
||||
Environment: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
Materials: []slsacommon.ProvenanceMaterial{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tt := testCases[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tag, err := tt.prov.GetTag()
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := tag, tt.tag; got != want {
|
||||
t.Fatalf("unexpected tag, got: %q, want: %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,26 +7,12 @@ import (
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
var (
|
||||
goBuilderBuildType = "https://github.com/slsa-framework/slsa-github-generator/go@v1"
|
||||
|
||||
genericGeneratorBuildType = "https://github.com/slsa-framework/slsa-github-generator/generic@v1"
|
||||
containerGeneratorBuildType = "https://github.com/slsa-framework/slsa-github-generator/container@v1"
|
||||
npmCLIBuildType = "https://github.com/npm/cli/gha@v1"
|
||||
|
||||
// Legacy build types.
|
||||
legacyGoBuilderBuildType = "https://github.com/slsa-framework/slsa-github-generator-go@v1"
|
||||
legacyBuilderBuildType = "https://github.com/slsa-framework/slsa-github-generator@v1"
|
||||
|
||||
// byobBuildType is the base build type for BYOB delegated builders.
|
||||
byobDelegatorBuildType = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"
|
||||
)
|
||||
|
||||
// Attestation is a SLSA v0.2 in-toto attestation statement.
|
||||
type Attestation struct {
|
||||
intoto.StatementHeader
|
||||
@@ -38,8 +24,29 @@ type ProvenanceV02 interface {
|
||||
Predicate() slsa02.ProvenancePredicate
|
||||
}
|
||||
|
||||
type provFunc func(*Attestation) iface.Provenance
|
||||
|
||||
// buildTypeMap is a map of builder IDs to supported buildTypes.
|
||||
var buildTypeMap = map[string]map[string]provFunc{
|
||||
common.GenericDelegatorBuilderID: {common.BYOBBuildTypeV0: newBYOBProvenance},
|
||||
common.GenericLowPermsDelegatorBuilderID: {common.BYOBBuildTypeV0: newBYOBProvenance},
|
||||
|
||||
common.GoBuilderID: {
|
||||
common.GoBuilderBuildTypeV1: newLegacyBuilderProvenance,
|
||||
common.LegacyBuilderBuildTypeV1: newLegacyBuilderProvenance,
|
||||
common.LegacyGoBuilderBuildTypeV1: newLegacyBuilderProvenance,
|
||||
},
|
||||
|
||||
common.GenericGeneratorBuilderID: {common.GenericGeneratorBuildTypeV1: newLegacyBuilderProvenance},
|
||||
common.ContainerGeneratorBuilderID: {common.ContainerGeneratorBuildTypeV1: newLegacyBuilderProvenance},
|
||||
|
||||
common.NpmCLILegacyBuilderID: {common.NpmCLIBuildTypeV1: newLegacyBuilderProvenance},
|
||||
common.NpmCLIHostedBuilderID: {common.NpmCLIBuildTypeV1: newLegacyBuilderProvenance},
|
||||
// NOTE: we don't support Npm CLI on self-hosted.
|
||||
}
|
||||
|
||||
// New returns a new Provenance for the given json payload.
|
||||
func New(payload []byte) (iface.Provenance, error) {
|
||||
func New(builderID string, payload []byte) (iface.Provenance, error) {
|
||||
// Strict unmarshal.
|
||||
// NOTE: this supports extensions because they are
|
||||
// only used as part of interface{}-defined fields.
|
||||
@@ -51,23 +58,15 @@ func New(payload []byte) (iface.Provenance, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.Predicate.BuildType == byobDelegatorBuildType:
|
||||
return &provenanceV02{
|
||||
upperEnv: true,
|
||||
prov: a,
|
||||
}, nil
|
||||
case a.Predicate.BuildType == goBuilderBuildType ||
|
||||
a.Predicate.BuildType == genericGeneratorBuildType ||
|
||||
a.Predicate.BuildType == containerGeneratorBuildType ||
|
||||
a.Predicate.BuildType == npmCLIBuildType ||
|
||||
a.Predicate.BuildType == legacyBuilderBuildType ||
|
||||
a.Predicate.BuildType == legacyGoBuilderBuildType:
|
||||
return &provenanceV02{
|
||||
upperEnv: false,
|
||||
prov: a,
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: unknown buildType: %q", serrors.ErrorInvalidDssePayload, a.Predicate.BuildType)
|
||||
btMap, ok := buildTypeMap[builderID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q", serrors.ErrorInvalidBuilderID, builderID)
|
||||
}
|
||||
|
||||
pFunc, ok := btMap[a.Predicate.BuildType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q for builder ID %q", serrors.ErrorInvalidBuildType, a.Predicate.BuildType, builderID)
|
||||
}
|
||||
|
||||
return pFunc(a), nil
|
||||
}
|
||||
|
||||
188
verifiers/internal/gha/slsaprovenance/v1.0/base.go
Normal file
188
verifiers/internal/gha/slsaprovenance/v1.0/base.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
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/common"
|
||||
)
|
||||
|
||||
// provenanceV1 is a base implementation for SLSA v1.0 provenance.
|
||||
type provenanceV1 struct {
|
||||
prov *Attestation
|
||||
}
|
||||
|
||||
// Predicate implements ProvenanceV02.Predicate.
|
||||
func (p *provenanceV1) Predicate() slsa1.ProvenancePredicate {
|
||||
return p.prov.Predicate
|
||||
}
|
||||
|
||||
// BuilderID implements Provenance.BuilderID.
|
||||
func (p *provenanceV1) BuilderID() (string, error) {
|
||||
return p.prov.Predicate.RunDetails.Builder.ID, nil
|
||||
}
|
||||
|
||||
// BuildType implements Provenance.BuildType.
|
||||
func (p *provenanceV1) BuildType() (string, error) {
|
||||
return p.prov.Predicate.BuildDefinition.BuildType, nil
|
||||
}
|
||||
|
||||
// SourceURI implements Provenance.SourceURI.
|
||||
func (p *provenanceV1) SourceURI() (string, error) {
|
||||
// Use resolvedDependencies.
|
||||
if len(p.prov.Predicate.BuildDefinition.ResolvedDependencies) == 0 {
|
||||
return "", fmt.Errorf("%w: empty resovedDependencies", serrors.ErrorInvalidDssePayload)
|
||||
}
|
||||
// For now, we use the first resolvedDependency relying on a GHA builder-verifier contract.
|
||||
uri := p.prov.Predicate.BuildDefinition.ResolvedDependencies[0].URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
}
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (p *provenanceV1) builderTriggerInfo() (string, string, string, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
}
|
||||
|
||||
if _, exists := sysParams["GITHUB_WORKFLOW_REF"]; !exists {
|
||||
return "", "", "", fmt.Errorf("%w: GITHUB_WORKFLOW_REF", serrors.ErrorNotPresent)
|
||||
}
|
||||
|
||||
workflowRef, err := common.GetAsString(sysParams, "GITHUB_WORKFLOW_REF")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
parts := strings.Split(workflowRef, "@")
|
||||
if len(parts) != 2 {
|
||||
return "", "", "", fmt.Errorf("%w: ref: %s", serrors.ErrorInvalidFormat, workflowRef)
|
||||
}
|
||||
repoAndPath := parts[0]
|
||||
ref := parts[1]
|
||||
|
||||
parts = strings.Split(repoAndPath, "/")
|
||||
if len(parts) < 2 {
|
||||
return "", "", "", fmt.Errorf("%w: rep and path: %s", serrors.ErrorInvalidFormat, repoAndPath)
|
||||
}
|
||||
|
||||
repo := strings.Join(parts[:2], "/")
|
||||
path := strings.Join(parts[2:], "/")
|
||||
return fmt.Sprintf("git+https://github.com/%s", repo), ref, path, nil
|
||||
}
|
||||
|
||||
func (p *provenanceV1) triggerInfo() (string, string, string, error) {
|
||||
return p.builderTriggerInfo()
|
||||
}
|
||||
|
||||
// TriggerURI implements Provenance.TriggerURI.
|
||||
func (p *provenanceV1) TriggerURI() (string, error) {
|
||||
repository, ref, _, err := p.triggerInfo()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if repository == "" || ref == "" {
|
||||
return "", fmt.Errorf("%w: repository or ref is empty", serrors.ErrorMalformedURI)
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", repository, ref), nil
|
||||
}
|
||||
|
||||
// Subjects implements Provenance.Subjects.
|
||||
func (p *provenanceV1) Subjects() ([]intoto.Subject, error) {
|
||||
subj := p.prov.Subject
|
||||
if len(subj) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no subjects")
|
||||
}
|
||||
return subj, nil
|
||||
}
|
||||
|
||||
// GetBranch implements Provenance.GetBranch.
|
||||
func (p *provenanceV1) GetBranch() (string, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
}
|
||||
|
||||
return common.GetBranch(sysParams, true)
|
||||
}
|
||||
|
||||
// GetTag implements Provenance.GetTag.
|
||||
func (p *provenanceV1) GetTag() (string, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
|
||||
return common.GetTag(sysParams, true)
|
||||
}
|
||||
|
||||
// GetWorkflowInputs implements Provenance.GetWorkflowInputs.
|
||||
func (p *provenanceV1) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
return common.GetWorkflowInputs(sysParams, true)
|
||||
}
|
||||
|
||||
// GetBuildTriggerPath implements Provenance.GetBuildTriggerPath.
|
||||
func (p *provenanceV1) GetBuildTriggerPath() (string, error) {
|
||||
// TODO(#566): verify the ref and repo as well.
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.ExternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
|
||||
w, ok := sysParams["workflow"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "workflow parameters type")
|
||||
}
|
||||
|
||||
wMap, ok := w.(map[string]string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "workflow not a map")
|
||||
}
|
||||
|
||||
v, ok := wMap["path"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no path entry on workflow")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// GetBuildInvocationID implements Provenance.GetBuildInvocationID.
|
||||
func (p *provenanceV1) GetBuildInvocationID() (string, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.InvocationID, nil
|
||||
}
|
||||
|
||||
// GetBuildStartTime implements Provenance.GetBuildStartTime.
|
||||
func (p *provenanceV1) GetBuildStartTime() (*time.Time, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.StartedOn, nil
|
||||
}
|
||||
|
||||
// GetBuildFinishTime implements Provenance.GetBuildFinishTime.
|
||||
func (p *provenanceV1) GetBuildFinishTime() (*time.Time, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.FinishedOn, nil
|
||||
}
|
||||
|
||||
// GetNumberResolvedDependencies implements Provenance.GetNumberResolvedDependencies.
|
||||
func (p *provenanceV1) GetNumberResolvedDependencies() (int, error) {
|
||||
return len(p.prov.Predicate.BuildDefinition.ResolvedDependencies), nil
|
||||
}
|
||||
|
||||
// GetSystemParameters implements Provenance.GetSystemParameters.
|
||||
func (p *provenanceV1) GetSystemParameters() (map[string]any, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
|
||||
return sysParams, nil
|
||||
}
|
||||
@@ -2,14 +2,11 @@ package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
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/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
)
|
||||
|
||||
// BYOBBuildType is the base build type for BYOB delegated builders.
|
||||
@@ -17,224 +14,72 @@ var BYOBBuildType = "https://github.com/slsa-framework/slsa-github-generator/del
|
||||
|
||||
// BYOBProvenance is SLSA v1.0 provenance for the slsa-github-generator BYOB build type.
|
||||
type BYOBProvenance struct {
|
||||
prov *Attestation
|
||||
}
|
||||
|
||||
// Predicate implements ProvenanceV02.Predicate.
|
||||
func (p *BYOBProvenance) Predicate() slsa1.ProvenancePredicate {
|
||||
return p.prov.Predicate
|
||||
}
|
||||
|
||||
// BuilderID implements Provenance.BuilderID.
|
||||
func (p *BYOBProvenance) BuilderID() (string, error) {
|
||||
return p.prov.Predicate.RunDetails.Builder.ID, nil
|
||||
}
|
||||
|
||||
// SourceURI implements Provenance.SourceURI.
|
||||
func (p *BYOBProvenance) SourceURI() (string, error) {
|
||||
// Use resolvedDependencies.
|
||||
if len(p.prov.Predicate.BuildDefinition.ResolvedDependencies) == 0 {
|
||||
return "", fmt.Errorf("%w: empty resovedDependencies", serrors.ErrorInvalidDssePayload)
|
||||
}
|
||||
// For now, we use the first resolvedDependency relying on a GHA builder-verifier contract.
|
||||
uri := p.prov.Predicate.BuildDefinition.ResolvedDependencies[0].URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
}
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
// TODO(#613): Support for generators.
|
||||
//
|
||||
//nolint:unused
|
||||
func getValidateKey(m map[string]interface{}, key string) (string, error) {
|
||||
v, ok := m[key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: no %v found", serrors.ErrorInvalidFormat, key)
|
||||
}
|
||||
vv, ok := v.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: not a string %v", serrors.ErrorInvalidFormat, v)
|
||||
}
|
||||
if vv == "" {
|
||||
return "", fmt.Errorf("%w: empty %v", serrors.ErrorInvalidFormat, key)
|
||||
}
|
||||
return vv, nil
|
||||
}
|
||||
|
||||
// TODO(#613): Support for generators.
|
||||
//
|
||||
//nolint:unused
|
||||
func (p *BYOBProvenance) generatorTriggerInfo() (string, string, string, error) {
|
||||
// See https://github.com/slsa-framework/github-actions-buildtypes/blob/main/workflow/v1/example.json#L16-L19.
|
||||
extParams, ok := p.prov.Predicate.BuildDefinition.ExternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters type")
|
||||
}
|
||||
workflow, ok := extParams["workflow"]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters workflow")
|
||||
}
|
||||
workflowMap, ok := workflow.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("%w: %s, type %T", serrors.ErrorInvalidDssePayload, "not a map of interface{}", workflow)
|
||||
}
|
||||
ref, err := getValidateKey(workflowMap, "ref")
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("%w: %v", serrors.ErrorMalformedURI, err)
|
||||
}
|
||||
repository, err := getValidateKey(workflowMap, "repository")
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("%w: %v", serrors.ErrorMalformedURI, err)
|
||||
}
|
||||
path, err := getValidateKey(workflowMap, "path")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
return repository, ref, path, nil
|
||||
}
|
||||
|
||||
func (p *BYOBProvenance) builderTriggerInfo() (string, string, string, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
}
|
||||
|
||||
if _, exists := sysParams["GITHUB_WORKFLOW_REF"]; !exists {
|
||||
return "", "", "", fmt.Errorf("%w: GITHUB_WORKFLOW_REF", serrors.ErrorNotPresent)
|
||||
}
|
||||
|
||||
workflowRef, err := common.GetAsString(sysParams, "GITHUB_WORKFLOW_REF")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
parts := strings.Split(workflowRef, "@")
|
||||
if len(parts) != 2 {
|
||||
return "", "", "", fmt.Errorf("%w: ref: %s", serrors.ErrorInvalidFormat, workflowRef)
|
||||
}
|
||||
repoAndPath := parts[0]
|
||||
ref := parts[1]
|
||||
|
||||
parts = strings.Split(repoAndPath, "/")
|
||||
if len(parts) < 2 {
|
||||
return "", "", "", fmt.Errorf("%w: rep and path: %s", serrors.ErrorInvalidFormat, repoAndPath)
|
||||
}
|
||||
|
||||
repo := strings.Join(parts[:2], "/")
|
||||
path := strings.Join(parts[2:], "/")
|
||||
return fmt.Sprintf("git+https://github.com/%s", repo), ref, path, nil
|
||||
}
|
||||
|
||||
func (p *BYOBProvenance) triggerInfo() (string, string, string, error) {
|
||||
// TODO(#613): Support for generators.
|
||||
return p.builderTriggerInfo()
|
||||
}
|
||||
|
||||
// TriggerURI implements Provenance.TriggerURI.
|
||||
func (p *BYOBProvenance) TriggerURI() (string, error) {
|
||||
repository, ref, _, err := p.triggerInfo()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if repository == "" || ref == "" {
|
||||
return "", fmt.Errorf("%w: repository or ref is empty", serrors.ErrorMalformedURI)
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", repository, ref), nil
|
||||
}
|
||||
|
||||
// Subjects implements Provenance.Subjects.
|
||||
func (p *BYOBProvenance) Subjects() ([]intoto.Subject, error) {
|
||||
subj := p.prov.Subject
|
||||
if len(subj) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no subjects")
|
||||
}
|
||||
return subj, nil
|
||||
*provenanceV1
|
||||
}
|
||||
|
||||
// GetBranch implements Provenance.GetBranch.
|
||||
func (p *BYOBProvenance) GetBranch() (string, error) {
|
||||
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/472): Add GetBranch() support.
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
sourceURI, err := p.SourceURI()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading source uri: %w", err)
|
||||
}
|
||||
|
||||
return common.GetBranch(sysParams, true)
|
||||
// Returns the branch from the source URI if available.
|
||||
_, ref, err := utils.ParseGitURIAndRef(sourceURI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing source uri: %w", err)
|
||||
}
|
||||
|
||||
if ref == "" {
|
||||
return "", fmt.Errorf("%w: unable to get ref for source %q",
|
||||
serrors.ErrorInvalidDssePayload, sourceURI)
|
||||
}
|
||||
|
||||
refType, _ := utils.ParseGitRef(ref)
|
||||
switch refType {
|
||||
case "heads": // branch.
|
||||
// NOTE: We return the full git ref.
|
||||
return ref, nil
|
||||
case "tags":
|
||||
// NOTE: If the ref type is a tag we want to try to parse out the branch from the tag.
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
}
|
||||
return common.GetBranch(sysParams, true)
|
||||
default:
|
||||
return "", fmt.Errorf("%w: unknown ref type %q for ref %q",
|
||||
serrors.ErrorInvalidDssePayload, refType, ref)
|
||||
}
|
||||
}
|
||||
|
||||
// GetTag implements Provenance.GetTag.
|
||||
func (p *BYOBProvenance) GetTag() (string, error) {
|
||||
// Get the value from the internalParameters if there is no source URI.
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
sourceURI, err := p.SourceURI()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading source uri: %w", err)
|
||||
}
|
||||
|
||||
return common.GetTag(sysParams, true)
|
||||
}
|
||||
|
||||
// GetWorkflowInputs implements Provenance.GetWorkflowInputs.
|
||||
func (p *BYOBProvenance) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
return common.GetWorkflowInputs(sysParams, true)
|
||||
}
|
||||
|
||||
// GetBuildTriggerPath implements Provenance.GetBuildTriggerPath.
|
||||
func (p *BYOBProvenance) GetBuildTriggerPath() (string, error) {
|
||||
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/566):
|
||||
// verify the ref and repo as well.
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.ExternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
// Returns the branch from the source URI if available.
|
||||
_, ref, err := utils.ParseGitURIAndRef(sourceURI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing source uri: %w", err)
|
||||
}
|
||||
|
||||
w, ok := sysParams["workflow"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "workflow parameters type")
|
||||
if ref == "" {
|
||||
return "", fmt.Errorf("%w: unable to get ref for source %q",
|
||||
serrors.ErrorInvalidDssePayload, sourceURI)
|
||||
}
|
||||
|
||||
wMap, ok := w.(map[string]string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "workflow not a map")
|
||||
refType, _ := utils.ParseGitRef(ref)
|
||||
switch refType {
|
||||
case "heads": // branch.
|
||||
return "", nil
|
||||
case "tags":
|
||||
// NOTE: We return the full git ref.
|
||||
return ref, nil
|
||||
default:
|
||||
return "", fmt.Errorf("%w: unknown ref type %q for ref %q",
|
||||
serrors.ErrorInvalidDssePayload, refType, ref)
|
||||
}
|
||||
|
||||
v, ok := wMap["path"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no path entry on workflow")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// GetBuildInvocationID implements Provenance.GetBuildInvocationID.
|
||||
func (p *BYOBProvenance) GetBuildInvocationID() (string, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.InvocationID, nil
|
||||
}
|
||||
|
||||
// GetBuildStartTime implements Provenance.GetBuildStartTime.
|
||||
func (p *BYOBProvenance) GetBuildStartTime() (*time.Time, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.StartedOn, nil
|
||||
}
|
||||
|
||||
// GetBuildFinishTime implements Provenance.GetBuildFinishTime.
|
||||
func (p *BYOBProvenance) GetBuildFinishTime() (*time.Time, error) {
|
||||
return p.prov.Predicate.RunDetails.BuildMetadata.FinishedOn, nil
|
||||
}
|
||||
|
||||
// GetNumberResolvedDependencies implements Provenance.GetNumberResolvedDependencies.
|
||||
func (p *BYOBProvenance) GetNumberResolvedDependencies() (int, error) {
|
||||
return len(p.prov.Predicate.BuildDefinition.ResolvedDependencies), nil
|
||||
}
|
||||
|
||||
// GetSystemParameters implements Provenance.GetSystemParameters.
|
||||
func (p *BYOBProvenance) GetSystemParameters() (map[string]any, error) {
|
||||
sysParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
|
||||
return sysParams, nil
|
||||
}
|
||||
|
||||
279
verifiers/internal/gha/slsaprovenance/v1.0/byob_test.go
Normal file
279
verifiers/internal/gha/slsaprovenance/v1.0/byob_test.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
)
|
||||
|
||||
func Test_BYOBProvenance_GetBranch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
prov BYOBProvenance
|
||||
branch string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty provenance",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/heads/main",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
branch: "refs/heads/main",
|
||||
},
|
||||
{
|
||||
name: "internalParameters GITHUB_REF @ refs/heads/main",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/tags/v1.0.0",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_BASE_REF": "",
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
"GITHUB_EVENT_NAME": "push",
|
||||
"GITHUB_EVENT_PAYLOAD": map[string]any{
|
||||
"base_ref": nil,
|
||||
},
|
||||
},
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
branch: "",
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/heads/main no ref",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tt := testCases[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
branch, err := tt.prov.GetBranch()
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
if got, want := branch, tt.branch; got != want {
|
||||
t.Fatalf("unexpected branch, got: %q, want: %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_BYOBProvenance_GetTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
prov BYOBProvenance
|
||||
tag string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "empty provenance",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/heads/main",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tag: "",
|
||||
},
|
||||
{
|
||||
name: "internalParameters GITHUB_REF @ refs/heads/main",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "branch",
|
||||
"GITHUB_REF": "refs/heads/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/tags/v1.0.0",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes@refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tag: "refs/tags/v1.0.0",
|
||||
},
|
||||
{
|
||||
name: "internalParameters GITHUB_REF @ ref/tags/v1.0.0",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_REF_TYPE": "tag",
|
||||
"GITHUB_REF": "refs/tags/v1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "resolved dependency uri @ refs/tags/v1.0.0 no ref",
|
||||
prov: BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
StatementHeader: intoto.StatementHeader{},
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
ResolvedDependencies: []slsa1.ResourceDescriptor{
|
||||
{
|
||||
URI: "git+https://github.com/kubernetes/kubernetes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tt := testCases[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tag, err := tt.prov.GetTag()
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got, want := tag, tt.tag; got != want {
|
||||
t.Fatalf("unexpected tag, got: %q, want: %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package v1
|
||||
|
||||
// ContainerBasedBuildType is the build type for the container-based builder and is based on BYOB.
|
||||
// ContainerBasedBuildType is the build type for the container-based builder.
|
||||
var ContainerBasedBuildType = "https://slsa.dev/container-based-build/v0.1?draft"
|
||||
|
||||
// ContainerBasedProvenance is provenance generated by the container-based builder.
|
||||
type ContainerBasedProvenance struct {
|
||||
// NOTE: The Container-based builder is not based on BYOB framework but the
|
||||
// provenanece is identical and can be treated the same (for now).
|
||||
*BYOBProvenance
|
||||
*provenanceV1
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
@@ -23,8 +24,33 @@ type ProvenanceV1 interface {
|
||||
Predicate() slsa1.ProvenancePredicate
|
||||
}
|
||||
|
||||
type provFunc func(*Attestation) iface.Provenance
|
||||
|
||||
func newBYOB(a *Attestation) iface.Provenance {
|
||||
return &BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: a,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newContainerBased(a *Attestation) iface.Provenance {
|
||||
return &ContainerBasedProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: a,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// buildTypeMap is a map of builder IDs to supported buildTypes.
|
||||
var buildTypeMap = map[string]map[string]provFunc{
|
||||
common.GenericDelegatorBuilderID: {common.BYOBBuildTypeV0: newBYOB},
|
||||
common.GenericLowPermsDelegatorBuilderID: {common.BYOBBuildTypeV0: newBYOB},
|
||||
common.ContainerBasedBuilderID: {common.ContainerBasedBuildTypeV01Draft: newContainerBased},
|
||||
}
|
||||
|
||||
// New returns a new Provenance object based on the payload.
|
||||
func New(payload []byte) (iface.Provenance, error) {
|
||||
func New(builderID string, payload []byte) (iface.Provenance, error) {
|
||||
// Strict unmarshal.
|
||||
// NOTE: this supports extensions because they are
|
||||
// only used as part of interface{}-defined fields.
|
||||
@@ -33,21 +59,18 @@ func New(payload []byte) (iface.Provenance, error) {
|
||||
|
||||
a := &Attestation{}
|
||||
if err := dec.Decode(a); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%w: %w", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
|
||||
switch a.Predicate.BuildDefinition.BuildType {
|
||||
case BYOBBuildType:
|
||||
return &BYOBProvenance{
|
||||
prov: a,
|
||||
}, nil
|
||||
case ContainerBasedBuildType:
|
||||
return &ContainerBasedProvenance{
|
||||
BYOBProvenance: &BYOBProvenance{
|
||||
prov: a,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: unknown buildType: %q", serrors.ErrorInvalidDssePayload, a.Predicate.BuildDefinition.BuildType)
|
||||
btMap, ok := buildTypeMap[builderID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q", serrors.ErrorInvalidBuilderID, builderID)
|
||||
}
|
||||
|
||||
provFunc, ok := btMap[a.Predicate.BuildDefinition.BuildType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q for builder ID %q", serrors.ErrorInvalidBuildType, a.Predicate.BuildDefinition.BuildType, builderID)
|
||||
}
|
||||
|
||||
return provFunc(a), nil
|
||||
}
|
||||
|
||||
118
verifiers/internal/gha/slsaprovenance/v1.0/provenance_test.go
Normal file
118
verifiers/internal/gha/slsaprovenance/v1.0/provenance_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
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/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
func Test_New(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
builderID string
|
||||
payload string
|
||||
prov iface.Provenance
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "BYOB build type",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
payload: fmt.Sprintf(`{
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": %q
|
||||
}
|
||||
}
|
||||
}`, common.BYOBBuildTypeV0),
|
||||
prov: &BYOBProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
BuildType: common.BYOBBuildTypeV0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Container-based build type",
|
||||
builderID: common.ContainerBasedBuilderID,
|
||||
payload: fmt.Sprintf(`{
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": %q
|
||||
}
|
||||
}
|
||||
}`, common.ContainerBasedBuildTypeV01Draft),
|
||||
prov: &ContainerBasedProvenance{
|
||||
provenanceV1: &provenanceV1{
|
||||
prov: &Attestation{
|
||||
Predicate: slsa1.ProvenancePredicate{
|
||||
BuildDefinition: slsa1.ProvenanceBuildDefinition{
|
||||
BuildType: common.ContainerBasedBuildTypeV01Draft,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Unknown fields",
|
||||
payload: `{
|
||||
"predicate": {
|
||||
"unknown": "field",
|
||||
"buildDefinition": {
|
||||
"buildType": "foo"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
},
|
||||
{
|
||||
name: "Unknown builder ID",
|
||||
builderID: "unknown",
|
||||
payload: `{
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": "foo"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
err: serrors.ErrorInvalidBuilderID,
|
||||
},
|
||||
{
|
||||
name: "Unknown buildType",
|
||||
builderID: common.GenericDelegatorBuilderID,
|
||||
payload: `{
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": "foo"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
err: serrors.ErrorInvalidBuildType,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tt := testCases[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p, err := New(tt.builderID, []byte(tt.payload))
|
||||
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
||||
t.Fatalf("unexpected error (-want +got): \n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(tt.prov, p, cmp.AllowUnexported(provenanceV1{}, BYOBProvenance{}, ContainerBasedProvenance{})); diff != "" {
|
||||
t.Fatalf("unexpected result (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func verifyEnvAndCert(env *dsse.Envelope,
|
||||
}
|
||||
|
||||
// Verify the builder identity.
|
||||
builderID, byob, err := VerifyBuilderIdentity(workflowInfo, builderOpts, defaultBuilders)
|
||||
verifiedBuilderID, byob, err := VerifyBuilderIdentity(workflowInfo, builderOpts, defaultBuilders)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func verifyEnvAndCert(env *dsse.Envelope,
|
||||
|
||||
// Verify properties of the SLSA provenance.
|
||||
// Unpack and verify info in the provenance, including the subject Digest.
|
||||
provenanceOpts.ExpectedBuilderID = builderID.String()
|
||||
provenanceOpts.ExpectedBuilderID = verifiedBuilderID.String()
|
||||
// There is a corner-case to handle: if the verified builder ID from the cert
|
||||
// is a delegator builder, the user MUST provide an expected builder ID
|
||||
// and we MUST match it against the content of the provenance.
|
||||
@@ -79,7 +79,7 @@ func verifyEnvAndCert(env *dsse.Envelope,
|
||||
}
|
||||
provenanceOpts.ExpectedBuilderID = *builderOpts.ExpectedID
|
||||
}
|
||||
if err := VerifyProvenance(env, provenanceOpts, byob); err != nil {
|
||||
if err := VerifyProvenance(env, provenanceOpts, verifiedBuilderID, byob); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func verifyEnvAndCert(env *dsse.Envelope,
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return r, builderID, nil
|
||||
return r, verifiedBuilderID, nil
|
||||
}
|
||||
|
||||
func verifyNpmEnvAndCert(env *dsse.Envelope,
|
||||
@@ -178,7 +178,7 @@ func verifyNpmEnvAndCert(env *dsse.Envelope,
|
||||
return nil, fmt.Errorf("%w: re-usable workflow is GitHub-hosted", serrors.ErrorMismatchBuilderID)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: builder %q. Expected one of %q, %q", serrors.ErrorNotSupported, *builderOpts.ExpectedID,
|
||||
return nil, fmt.Errorf("%w: builder %v. Expected one of %v, %v", serrors.ErrorNotSupported, *builderOpts.ExpectedID,
|
||||
common.NpmCLISelfHostedBuilderID, common.NpmCLIHostedBuilderID)
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ func verifyNpmEnvAndCert(env *dsse.Envelope,
|
||||
|
||||
// Verify properties of the SLSA provenance.
|
||||
// Unpack and verify info in the provenance, including the Subject Digest.
|
||||
if err := VerifyNpmPackageProvenance(env, workflowInfo, provenanceOpts, isTrustedBuilder); err != nil {
|
||||
if err := VerifyNpmPackageProvenance(env, workflowInfo, provenanceOpts, trustedBuilderID, isTrustedBuilder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -330,6 +330,14 @@ func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context,
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Verify builder information.
|
||||
builder, err := npm.verifyBuilderID(
|
||||
provenanceOpts, builderOpts,
|
||||
defaultBYOBReusableWorkflows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Verify attestation headers.
|
||||
if err := npm.verifyIntotoHeaders(); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -346,15 +354,6 @@ func (v *GHAVerifier) VerifyNpmPackage(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// Verify certificate information.
|
||||
builder, err := verifyNpmEnvAndCert(npm.ProvenanceEnvelope(),
|
||||
npm.ProvenanceLeafCertificate(),
|
||||
provenanceOpts, builderOpts,
|
||||
defaultBYOBReusableWorkflows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
prov, err := npm.verifiedProvenanceBytes()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
Reference in New Issue
Block a user