mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-08-18 11:06:21 +00:00
feat: Verify provenance by build type (#632)
Fixes #473 Updates handling of provenance by providing implementations based on [buildType](https://slsa.dev/provenance/v1#buildType) since this determines how to interpret parameters and dependencies. This is done because we need a way to interpret parameters not just based on the predicateType. The 3 major build types with format differences are: - non-BYOB SLSA v0.2 - BYOB SLSA v0.2 - BYOB SLSA v1.0 --------- Signed-off-by: Ian Lewis <ianlewis@google.com>
This commit is contained in:
@@ -16,6 +16,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"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
|
||||
)
|
||||
|
||||
@@ -114,7 +115,7 @@ func extractAttestations(attestations []attestation) (*attestation, *attestation
|
||||
for i := range attestations {
|
||||
att := attestations[i]
|
||||
// Provenance type verification.
|
||||
if att.PredicateType == slsaprovenance.ProvenanceV02Type {
|
||||
if att.PredicateType == common.ProvenanceV02Type {
|
||||
provenanceAttestation = &att
|
||||
}
|
||||
// Publish type verification.
|
||||
@@ -196,7 +197,7 @@ func (n *Npm) verifyPublishAttesttationSignature() error {
|
||||
|
||||
func (n *Npm) verifyIntotoHeaders() error {
|
||||
if err := verifyIntotoTypes(n.verifiedProvenanceAtt,
|
||||
slsaprovenance.ProvenanceV02Type, intoto.PayloadType, false); err != nil {
|
||||
common.ProvenanceV02Type, intoto.PayloadType, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := verifyIntotoTypes(n.verifiedPublishAtt,
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
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"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
)
|
||||
|
||||
func Test_verifyName(t *testing.T) {
|
||||
@@ -671,7 +671,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "prov correct",
|
||||
predicateType: slsaprovenance.ProvenanceV02Type,
|
||||
predicateType: common.ProvenanceV02Type,
|
||||
payloadType: intoto.PayloadType,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
@@ -682,7 +682,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "prov mismatch payload type",
|
||||
predicateType: slsaprovenance.ProvenanceV02Type,
|
||||
predicateType: common.ProvenanceV02Type,
|
||||
payloadType: intoto.PayloadType,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
@@ -694,7 +694,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "prov mismatch predicate type",
|
||||
predicateType: slsaprovenance.ProvenanceV02Type + "a",
|
||||
predicateType: common.ProvenanceV02Type + "a",
|
||||
payloadType: intoto.PayloadType,
|
||||
att: &SignedAttestation{
|
||||
Envelope: &dsselib.Envelope{
|
||||
|
||||
@@ -16,11 +16,9 @@ import (
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/options"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
|
||||
"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"
|
||||
|
||||
// Load provenance types.
|
||||
|
||||
_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
|
||||
)
|
||||
|
||||
// SignedAttestation contains a signed DSSE envelope
|
||||
@@ -34,6 +32,7 @@ type SignedAttestation struct {
|
||||
RekorEntry *models.LogEntryAnon
|
||||
}
|
||||
|
||||
// EnvelopeFromBytes reads a DSSE envelope from the given payload.
|
||||
func EnvelopeFromBytes(payload []byte) (env *dsselib.Envelope, err error) {
|
||||
env = &dsselib.Envelope{}
|
||||
err = json.Unmarshal(payload, env)
|
||||
@@ -43,7 +42,7 @@ func EnvelopeFromBytes(payload []byte) (env *dsselib.Envelope, err error) {
|
||||
// Verify Builder ID in provenance statement.
|
||||
// This function does an exact comparison, and expects expectedBuilderID to be the full
|
||||
// `name@refs/tags/<name>`.
|
||||
func verifyBuilderIDExactMatch(prov slsaprovenance.Provenance, expectedBuilderID string) error {
|
||||
func verifyBuilderIDExactMatch(prov iface.Provenance, expectedBuilderID string) error {
|
||||
id, err := prov.BuilderID()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,7 +61,7 @@ func verifyBuilderIDExactMatch(prov slsaprovenance.Provenance, expectedBuilderID
|
||||
// Verify Builder ID in provenance statement.
|
||||
// This function verifies the names match. If the expected builder ID contains a version,
|
||||
// it also verifies the versions match.
|
||||
func verifyBuilderIDLooseMatch(prov slsaprovenance.Provenance, expectedBuilderID string) error {
|
||||
func verifyBuilderIDLooseMatch(prov iface.Provenance, expectedBuilderID string) error {
|
||||
id, err := prov.BuilderID()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -91,7 +90,7 @@ func asURI(s string) string {
|
||||
}
|
||||
|
||||
// Verify source URI in provenance statement.
|
||||
func verifySourceURI(prov slsaprovenance.Provenance, expectedSourceURI string, allowNoMaterialRef bool) error {
|
||||
func verifySourceURI(prov iface.Provenance, expectedSourceURI string, allowNoMaterialRef bool) error {
|
||||
source := asURI(expectedSourceURI)
|
||||
|
||||
// We expect github.com URIs only.
|
||||
@@ -172,7 +171,7 @@ func sourceFromURI(uri string, allowNoRef bool) (string, error) {
|
||||
}
|
||||
|
||||
// Verify Subject Digest from the provenance statement.
|
||||
func verifyDigest(prov slsaprovenance.Provenance, expectedHash string) error {
|
||||
func verifyDigest(prov iface.Provenance, expectedHash string) error {
|
||||
subjects, err := prov.Subjects()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -221,6 +220,7 @@ func VerifyProvenanceSignature(ctx context.Context, trustedRoot *TrustedRoot,
|
||||
provenance, rClient, trustedRoot)
|
||||
}
|
||||
|
||||
// VerifyNpmPackageProvenance verifies provenance for an npm package.
|
||||
func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentity,
|
||||
provenanceOpts *options.ProvenanceOpts, isTrustedBuilder bool,
|
||||
) error {
|
||||
@@ -282,7 +282,7 @@ func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentit
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidDelegatorBuilderID(prov slsaprovenance.Provenance) error {
|
||||
func isValidDelegatorBuilderID(prov iface.Provenance) error {
|
||||
// Verify the TRW was referenced at a proper tag by the user.
|
||||
id, err := prov.BuilderID()
|
||||
if err != nil {
|
||||
@@ -295,6 +295,7 @@ func isValidDelegatorBuilderID(prov slsaprovenance.Provenance) error {
|
||||
return utils.IsValidBuilderTag(parts[1], false)
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -322,7 +323,8 @@ func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceO
|
||||
return VerifyProvenanceCommonOptions(prov, provenanceOpts, false)
|
||||
}
|
||||
|
||||
func VerifyProvenanceCommonOptions(prov slsaprovenance.Provenance, provenanceOpts *options.ProvenanceOpts,
|
||||
// VerifyProvenanceCommonOptions verifies the given provenance.
|
||||
func VerifyProvenanceCommonOptions(prov iface.Provenance, provenanceOpts *options.ProvenanceOpts,
|
||||
allowNoMaterialRef bool,
|
||||
) error {
|
||||
// Verify source.
|
||||
@@ -366,7 +368,9 @@ func VerifyProvenanceCommonOptions(prov slsaprovenance.Provenance, provenanceOpt
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]string) error {
|
||||
// VerifyWorkflowInputs verifies that the workflow inputs in the provenance
|
||||
// match the expected values.
|
||||
func VerifyWorkflowInputs(prov iface.Provenance, inputs map[string]string) error {
|
||||
pyldInputs, err := prov.GetWorkflowInputs()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -374,7 +378,7 @@ func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]stri
|
||||
|
||||
// Verify all inputs.
|
||||
for k, v := range inputs {
|
||||
value, err := slsaprovenance.GetAsString(pyldInputs, k)
|
||||
value, err := common.GetAsString(pyldInputs, k)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: cannot retrieve value of '%s'", serrors.ErrorMismatchWorkflowInputs, k)
|
||||
}
|
||||
@@ -390,7 +394,7 @@ func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]stri
|
||||
|
||||
// VerifyBranch verifies that the source branch in the provenance matches the
|
||||
// expected value.
|
||||
func VerifyBranch(prov slsaprovenance.Provenance, expectedBranch string) error {
|
||||
func VerifyBranch(prov iface.Provenance, expectedBranch string) error {
|
||||
ref, err := prov.GetBranch()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -410,7 +414,7 @@ func VerifyBranch(prov slsaprovenance.Provenance, expectedBranch string) error {
|
||||
|
||||
// VerifyTag verifies that the source tag in the provenance matches the
|
||||
// expected value.
|
||||
func VerifyTag(prov slsaprovenance.Provenance, expectedTag string) error {
|
||||
func VerifyTag(prov iface.Provenance, expectedTag string) error {
|
||||
ref, err := prov.GetTag()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -430,7 +434,7 @@ func VerifyTag(prov slsaprovenance.Provenance, expectedTag string) error {
|
||||
|
||||
// VerifyVersionedTag verifies that the source tag in the provenance matches the
|
||||
// expected semver value.
|
||||
func VerifyVersionedTag(prov slsaprovenance.Provenance, expectedTag string) error {
|
||||
func VerifyVersionedTag(prov iface.Provenance, expectedTag string) error {
|
||||
// Retrieve, validate and canonicalize the provenance tag.
|
||||
// Note: prerelease is validated as part of patch validation
|
||||
// and must be equal. Build is discarded as per https://semver.org/:
|
||||
|
||||
@@ -6,14 +6,12 @@ import (
|
||||
"strings"
|
||||
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
|
||||
|
||||
// Load provenance types.
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
slsav02 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v0.2"
|
||||
_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
|
||||
)
|
||||
|
||||
func verifyProvenanceMatchesCertificate(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
|
||||
func verifyProvenanceMatchesCertificate(prov iface.Provenance, workflow *WorkflowIdentity) error {
|
||||
// See the generation at https://github.com/npm/cli/blob/latest/workspaces/libnpmpublish/lib/provenance.js.
|
||||
// Verify systemParameters.
|
||||
if err := verifySystemParameters(prov, workflow); err != nil {
|
||||
@@ -65,7 +63,7 @@ func verifyProvenanceMatchesCertificate(prov slsaprovenance.Provenance, workflow
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySubjectDigestName(prov slsaprovenance.Provenance, digestName string) error {
|
||||
func verifySubjectDigestName(prov iface.Provenance, digestName string) error {
|
||||
subjects, err := prov.Subjects()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -84,7 +82,7 @@ func verifySubjectDigestName(prov slsaprovenance.Provenance, digestName string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBuildConfig(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
|
||||
func verifyBuildConfig(prov iface.Provenance, workflow *WorkflowIdentity) error {
|
||||
triggerPath, err := prov.GetBuildTriggerPath()
|
||||
if err != nil {
|
||||
// If the field is not available in the provenance,
|
||||
@@ -98,7 +96,7 @@ func verifyBuildConfig(prov slsaprovenance.Provenance, workflow *WorkflowIdentit
|
||||
return equalCertificateValue(workflow.BuildConfigPath, triggerPath, "trigger workflow")
|
||||
}
|
||||
|
||||
func verifyResolvedDependencies(prov slsaprovenance.Provenance) error {
|
||||
func verifyResolvedDependencies(prov iface.Provenance) error {
|
||||
n, err := prov.GetNumberResolvedDependencies()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -110,7 +108,7 @@ func verifyResolvedDependencies(prov slsaprovenance.Provenance) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
|
||||
func verifyMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {
|
||||
if err := verifyCommonMetadata(prov, workflow); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +123,7 @@ func verifyMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity)
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyCommonMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
|
||||
func verifyCommonMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {
|
||||
// Verify build invocation ID.
|
||||
invocationID, err := prov.GetBuildInvocationID()
|
||||
if err != nil {
|
||||
@@ -169,7 +167,7 @@ func verifyCommonMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIden
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyV02Metadata(prov slsaprovenance.Provenance) error {
|
||||
func verifyV02Metadata(prov iface.Provenance) error {
|
||||
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
|
||||
/*
|
||||
v0.2:
|
||||
@@ -181,21 +179,23 @@ func verifyV02Metadata(prov slsaprovenance.Provenance) error {
|
||||
},
|
||||
"reproducible": false
|
||||
*/
|
||||
prov02, ok := prov.(*slsav02.ProvenanceV02)
|
||||
prov02, ok := prov.(slsav02.ProvenanceV02)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if prov02.Predicate.Metadata == nil {
|
||||
predicate := prov02.Predicate()
|
||||
|
||||
if predicate.Metadata == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if prov02.Predicate.Metadata.Reproducible {
|
||||
if predicate.Metadata.Reproducible {
|
||||
return fmt.Errorf("%w: reproducible: %v",
|
||||
serrors.ErrorNonVerifiableClaim,
|
||||
prov02.Predicate.Metadata.Reproducible)
|
||||
predicate.Metadata.Reproducible)
|
||||
}
|
||||
|
||||
completeness := prov02.Predicate.Metadata.Completeness
|
||||
completeness := predicate.Metadata.Completeness
|
||||
if completeness.Parameters || completeness.Materials ||
|
||||
completeness.Environment {
|
||||
return fmt.Errorf("%w: completeness: %v",
|
||||
@@ -205,44 +205,47 @@ func verifyV02Metadata(prov slsaprovenance.Provenance) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyV02Parameters(prov slsaprovenance.Provenance) error {
|
||||
func verifyV02Parameters(prov iface.Provenance) error {
|
||||
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
|
||||
prov02, ok := prov.(*slsav02.ProvenanceV02)
|
||||
prov02, ok := prov.(slsav02.ProvenanceV02)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if prov02.Predicate.Invocation.Parameters == nil {
|
||||
predicate := prov02.Predicate()
|
||||
|
||||
if predicate.Invocation.Parameters == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := prov02.Predicate.Invocation.Parameters.(map[string]any)
|
||||
m, ok := predicate.Invocation.Parameters.(map[string]any)
|
||||
if !ok || len(m) > 0 {
|
||||
return fmt.Errorf("%w: parameters: %v",
|
||||
serrors.ErrorNonVerifiableClaim, prov02.Predicate.Invocation.Parameters)
|
||||
serrors.ErrorNonVerifiableClaim, predicate.Invocation.Parameters)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyV02BuildConfig(prov slsaprovenance.Provenance) error {
|
||||
func verifyV02BuildConfig(prov iface.Provenance) error {
|
||||
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
|
||||
prov02, ok := prov.(*slsav02.ProvenanceV02)
|
||||
prov02, ok := prov.(slsav02.ProvenanceV02)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
predicate := prov02.Predicate()
|
||||
|
||||
if prov02.Predicate.BuildConfig == nil {
|
||||
if predicate.BuildConfig == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := prov02.Predicate.BuildConfig.(map[string]any)
|
||||
m, ok := predicate.BuildConfig.(map[string]any)
|
||||
if !ok || len(m) > 0 {
|
||||
return fmt.Errorf("%w: buildConfig: %v",
|
||||
serrors.ErrorNonVerifiableClaim, prov02.Predicate.BuildConfig)
|
||||
serrors.ErrorNonVerifiableClaim, predicate.BuildConfig)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySystemParameters(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
|
||||
func verifySystemParameters(prov iface.Provenance, workflow *WorkflowIdentity) error {
|
||||
/*
|
||||
"environment": {
|
||||
"GITHUB_EVENT_NAME": "workflow_dispatch",
|
||||
@@ -338,7 +341,7 @@ func getRunIDs(workflow *WorkflowIdentity) (string, string, error) {
|
||||
|
||||
func verifySystemRun(params map[string]any, workflow *WorkflowIdentity) error {
|
||||
// Verify only if the values are provided in the provenance.
|
||||
if !slsaprovenance.Exists(params, "GITHUB_RUN_ID") && !slsaprovenance.Exists(params, "GITHUB_RUN_ATTEMPT") {
|
||||
if !common.Exists(params, "GITHUB_RUN_ID") && !common.Exists(params, "GITHUB_RUN_ATTEMPT") {
|
||||
return nil
|
||||
}
|
||||
// The certificate contains runID as '4757060009/attempts/1'.
|
||||
@@ -364,11 +367,11 @@ func verifySystemRun(params map[string]any, workflow *WorkflowIdentity) error {
|
||||
|
||||
func verifySystemParameter(params map[string]any, name string, certValue *string) error {
|
||||
// If the provenance does not contain an env variable.
|
||||
if !slsaprovenance.Exists(params, name) {
|
||||
if !common.Exists(params, name) {
|
||||
return nil
|
||||
}
|
||||
// Provenance contains the field, we must verify it.
|
||||
provValue, err := slsaprovenance.GetAsString(params, name)
|
||||
provValue, err := common.GetAsString(params, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package gha
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -9,10 +8,7 @@ import (
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
intotocommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||
intotov02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
intotov1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
slsav02 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v0.2"
|
||||
slsav10 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
|
||||
)
|
||||
|
||||
func Test_verifySubjectDigestName(t *testing.T) {
|
||||
@@ -48,7 +44,7 @@ func Test_verifySubjectDigestName(t *testing.T) {
|
||||
{
|
||||
name: "invalid no subjects",
|
||||
digestName: "sha256",
|
||||
err: serrors.ErrorInvalidDssePayload,
|
||||
err: serrors.ErrorNonVerifiableClaim,
|
||||
},
|
||||
{
|
||||
name: "wrong digest",
|
||||
@@ -66,25 +62,10 @@ func Test_verifySubjectDigestName(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
Subject: tt.subject,
|
||||
},
|
||||
},
|
||||
prov := &testProvenance{
|
||||
subjects: tt.subject,
|
||||
}
|
||||
err := verifySubjectDigestName(prov02, tt.digestName)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
Subject: tt.subject,
|
||||
},
|
||||
}
|
||||
err = verifySubjectDigestName(prov1, tt.digestName)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifySubjectDigestName(prov, tt.digestName); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -125,33 +106,10 @@ func Test_verifyBuildConfig(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
Predicate: intotov02.ProvenancePredicate{
|
||||
Invocation: intotov02.ProvenanceInvocation{
|
||||
ConfigSource: intotov02.ConfigSource{
|
||||
EntryPoint: tt.path,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
prov := &testProvenance{
|
||||
buildTriggerPath: tt.path,
|
||||
}
|
||||
err := verifyBuildConfig(prov02, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{
|
||||
Predicate: intotov1.ProvenancePredicate{
|
||||
BuildDefinition: intotov1.ProvenanceBuildDefinition{
|
||||
InternalParameters: map[string]interface{}{
|
||||
"GITHUB_WORKFLOW_REF": fmt.Sprintf("some/repo/%s@some-ref", tt.path),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = verifyBuildConfig(prov1, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifyBuildConfig(prov, &tt.workflow); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -186,29 +144,10 @@ func Test_verifyResolvedDependencies(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
Predicate: intotov02.ProvenancePredicate{},
|
||||
},
|
||||
prov := &testProvenance{
|
||||
noResolvedDeps: tt.n,
|
||||
}
|
||||
if tt.n > 0 {
|
||||
prov02.Predicate.Materials = make([]intotocommon.ProvenanceMaterial, tt.n)
|
||||
}
|
||||
err := verifyResolvedDependencies(prov02)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{
|
||||
Predicate: intotov1.ProvenancePredicate{
|
||||
BuildDefinition: intotov1.ProvenanceBuildDefinition{},
|
||||
},
|
||||
}
|
||||
if tt.n > 0 {
|
||||
prov1.Predicate.BuildDefinition.ResolvedDependencies = make([]intotov1.ResourceDescriptor, tt.n)
|
||||
}
|
||||
err = verifyResolvedDependencies(prov1)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifyResolvedDependencies(prov); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -296,45 +235,19 @@ func Test_verifyCommonMetadata(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()
|
||||
metadata := tt.metadata || tt.invocationID != nil || tt.startTime != nil ||
|
||||
tt.endTime != nil
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
Predicate: intotov02.ProvenancePredicate{},
|
||||
},
|
||||
}
|
||||
if metadata {
|
||||
prov02.Predicate.Metadata = &intotov02.ProvenanceMetadata{}
|
||||
}
|
||||
|
||||
prov := &testProvenance{}
|
||||
if tt.invocationID != nil {
|
||||
prov02.Predicate.Metadata.BuildInvocationID = *tt.invocationID
|
||||
prov.buildInvocationID = *tt.invocationID
|
||||
}
|
||||
if tt.startTime != nil {
|
||||
prov02.Predicate.Metadata.BuildStartedOn = tt.startTime
|
||||
prov.buildStartTime = tt.startTime
|
||||
}
|
||||
if tt.endTime != nil {
|
||||
prov02.Predicate.Metadata.BuildFinishedOn = tt.endTime
|
||||
prov.buildFinishTime = tt.endTime
|
||||
}
|
||||
|
||||
err := verifyCommonMetadata(prov02, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{}
|
||||
|
||||
if tt.invocationID != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.InvocationID = *tt.invocationID
|
||||
}
|
||||
if tt.startTime != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.StartedOn = tt.startTime
|
||||
}
|
||||
if tt.endTime != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.FinishedOn = tt.endTime
|
||||
}
|
||||
|
||||
err = verifyCommonMetadata(prov1, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifyCommonMetadata(prov, &tt.workflow); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -382,15 +295,12 @@ func Test_verifyV02Metadata(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()
|
||||
|
||||
metadata := tt.metadata || tt.reproducible || tt.parameters ||
|
||||
tt.environment || tt.materials
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{},
|
||||
}
|
||||
prov02 := &testProvenanceV02{}
|
||||
if metadata {
|
||||
prov02.Predicate.Metadata = &intotov02.ProvenanceMetadata{
|
||||
prov02.predicate.Metadata = &intotov02.ProvenanceMetadata{
|
||||
Completeness: intotov02.ProvenanceComplete{
|
||||
Parameters: tt.parameters,
|
||||
Materials: tt.materials,
|
||||
@@ -399,8 +309,7 @@ func Test_verifyV02Metadata(t *testing.T) {
|
||||
Reproducible: tt.reproducible,
|
||||
}
|
||||
}
|
||||
err := verifyV02Metadata(prov02)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifyV02Metadata(prov02); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -441,11 +350,9 @@ func Test_verifyV02Parameters(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{},
|
||||
}
|
||||
prov02 := &testProvenanceV02{}
|
||||
if tt.present || len(tt.value) > 0 {
|
||||
prov02.Predicate.Invocation.Parameters = tt.value
|
||||
prov02.predicate.Invocation.Parameters = tt.value
|
||||
}
|
||||
err := verifyV02Parameters(prov02)
|
||||
if !errCmp(err, tt.err) {
|
||||
@@ -489,11 +396,9 @@ func Test_verifyV02BuildConfig(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{},
|
||||
}
|
||||
prov02 := &testProvenanceV02{}
|
||||
if tt.present || len(tt.value) > 0 {
|
||||
prov02.Predicate.BuildConfig = tt.value
|
||||
prov02.predicate.BuildConfig = tt.value
|
||||
}
|
||||
err := verifyV02BuildConfig(prov02)
|
||||
if !errCmp(err, tt.err) {
|
||||
@@ -616,52 +521,43 @@ func Test_verifyMetadata(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
metadata := tt.metadata || tt.invocationID != nil || tt.startTime != nil ||
|
||||
tt.endTime != nil || tt.reproducible || tt.parameters ||
|
||||
tt.environment || tt.materials
|
||||
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{},
|
||||
}
|
||||
if metadata {
|
||||
prov02.Predicate.Metadata = &intotov02.ProvenanceMetadata{
|
||||
Completeness: intotov02.ProvenanceComplete{
|
||||
Parameters: tt.parameters,
|
||||
Materials: tt.materials,
|
||||
Environment: tt.environment,
|
||||
},
|
||||
Reproducible: tt.reproducible,
|
||||
}
|
||||
if tt.invocationID != nil {
|
||||
prov02.Predicate.Metadata.BuildInvocationID = *tt.invocationID
|
||||
}
|
||||
if tt.startTime != nil {
|
||||
prov02.Predicate.Metadata.BuildStartedOn = tt.startTime
|
||||
}
|
||||
if tt.endTime != nil {
|
||||
prov02.Predicate.Metadata.BuildFinishedOn = tt.endTime
|
||||
}
|
||||
}
|
||||
errV02 := verifyMetadata(prov02, &tt.workflow)
|
||||
if !errCmp(errV02, tt.errV02) {
|
||||
t.Errorf(cmp.Diff(errV02, tt.errV02))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{}
|
||||
|
||||
prov02 := &testProvenanceV02{}
|
||||
if tt.invocationID != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.InvocationID = *tt.invocationID
|
||||
prov02.buildInvocationID = *tt.invocationID
|
||||
}
|
||||
if tt.startTime != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.StartedOn = tt.startTime
|
||||
prov02.buildStartTime = tt.startTime
|
||||
}
|
||||
if tt.endTime != nil {
|
||||
prov1.Predicate.RunDetails.BuildMetadata.FinishedOn = tt.endTime
|
||||
prov02.buildFinishTime = tt.endTime
|
||||
}
|
||||
|
||||
errV01 := verifyMetadata(prov1, &tt.workflow)
|
||||
if !errCmp(errV01, tt.errV01) {
|
||||
t.Errorf(cmp.Diff(errV01, tt.errV01))
|
||||
prov02.predicate.Metadata = &intotov02.ProvenanceMetadata{
|
||||
Completeness: intotov02.ProvenanceComplete{
|
||||
Parameters: tt.parameters,
|
||||
Materials: tt.materials,
|
||||
Environment: tt.environment,
|
||||
},
|
||||
Reproducible: tt.reproducible,
|
||||
}
|
||||
|
||||
if err := verifyMetadata(prov02, &tt.workflow); !errCmp(err, tt.errV02) {
|
||||
t.Errorf(cmp.Diff(err, tt.errV02))
|
||||
}
|
||||
|
||||
prov1 := &testProvenanceV1{}
|
||||
if tt.invocationID != nil {
|
||||
prov1.buildInvocationID = *tt.invocationID
|
||||
}
|
||||
if tt.startTime != nil {
|
||||
prov1.buildStartTime = tt.startTime
|
||||
}
|
||||
if tt.endTime != nil {
|
||||
prov1.buildFinishTime = tt.endTime
|
||||
}
|
||||
|
||||
if err := verifyMetadata(prov1, &tt.workflow); !errCmp(err, tt.errV01) {
|
||||
t.Errorf(cmp.Diff(err, tt.errV01))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1038,30 +934,12 @@ func Test_verifySystemParameters(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()
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
Predicate: intotov02.ProvenancePredicate{
|
||||
Invocation: intotov02.ProvenanceInvocation{
|
||||
Environment: tt.environment,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
prov := &testProvenance{
|
||||
systemParameters: tt.environment,
|
||||
}
|
||||
|
||||
err := verifySystemParameters(prov02, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{
|
||||
Predicate: intotov1.ProvenancePredicate{
|
||||
BuildDefinition: intotov1.ProvenanceBuildDefinition{
|
||||
InternalParameters: tt.environment,
|
||||
},
|
||||
},
|
||||
}
|
||||
err = verifySystemParameters(prov1, &tt.workflow)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifySystemParameters(prov, &tt.workflow); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
@@ -1127,6 +1005,11 @@ func Test_verifyProvenanceMatchesCertificate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "unknown field",
|
||||
subject: []intoto.Subject{
|
||||
{
|
||||
Digest: intotocommon.DigestSet{"sha512": "abcd"},
|
||||
},
|
||||
},
|
||||
environment: map[string]interface{}{
|
||||
"SOMETHING": "workflow_dispatch",
|
||||
},
|
||||
@@ -1201,48 +1084,15 @@ func Test_verifyProvenanceMatchesCertificate(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()
|
||||
prov02 := &slsav02.ProvenanceV02{
|
||||
&intoto.ProvenanceStatement{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
Subject: tt.subject,
|
||||
},
|
||||
Predicate: intotov02.ProvenancePredicate{
|
||||
Invocation: intotov02.ProvenanceInvocation{
|
||||
Environment: tt.environment,
|
||||
ConfigSource: intotov02.ConfigSource{
|
||||
EntryPoint: tt.workflowTriggerPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
prov := &testProvenance{
|
||||
subjects: tt.subject,
|
||||
noResolvedDeps: tt.numberResolvedDependencies,
|
||||
buildTriggerPath: tt.workflowTriggerPath,
|
||||
systemParameters: tt.environment,
|
||||
}
|
||||
|
||||
if tt.numberResolvedDependencies > 0 {
|
||||
prov02.Predicate.Materials = make([]intotocommon.ProvenanceMaterial, tt.numberResolvedDependencies)
|
||||
}
|
||||
|
||||
err := verifyProvenanceMatchesCertificate(prov02, &tt.certificateIdentity)
|
||||
if !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
|
||||
prov1 := &slsav10.ProvenanceV1{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
Subject: tt.subject,
|
||||
},
|
||||
Predicate: intotov1.ProvenancePredicate{
|
||||
BuildDefinition: intotov1.ProvenanceBuildDefinition{
|
||||
InternalParameters: tt.environment,
|
||||
// TODO(#566): verify fields for v1.0 provenance.
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if tt.numberResolvedDependencies > 0 {
|
||||
prov1.Predicate.BuildDefinition.ResolvedDependencies = make([]intotov1.ResourceDescriptor, tt.numberResolvedDependencies)
|
||||
}
|
||||
err = verifyProvenanceMatchesCertificate(prov1, &tt.certificateIdentity)
|
||||
if !errCmp(err, tt.err) {
|
||||
if err := verifyProvenanceMatchesCertificate(prov, &tt.certificateIdentity); !errCmp(err, tt.err) {
|
||||
t.Errorf(cmp.Diff(err, tt.err))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,9 +8,12 @@ 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"
|
||||
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"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
type testProvenance struct {
|
||||
@@ -47,7 +50,21 @@ func (p *testProvenance) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
return p.workflowInputs, nil
|
||||
}
|
||||
|
||||
func provenanceFromBytes(payload []byte) (slsaprovenance.Provenance, error) {
|
||||
type testProvenanceV02 struct {
|
||||
testProvenance
|
||||
predicate slsa02.ProvenancePredicate
|
||||
}
|
||||
|
||||
func (p *testProvenanceV02) Predicate() slsa02.ProvenancePredicate { return p.predicate }
|
||||
|
||||
type testProvenanceV1 struct {
|
||||
testProvenance
|
||||
predicate slsa1.ProvenancePredicate
|
||||
}
|
||||
|
||||
func (p *testProvenanceV1) Predicate() slsa1.ProvenancePredicate { return p.predicate }
|
||||
|
||||
func provenanceFromBytes(payload []byte) (iface.Provenance, error) {
|
||||
env, err := EnvelopeFromBytes(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -95,7 +112,7 @@ func Test_VerifyDigest(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prov slsaprovenance.Provenance
|
||||
prov iface.Provenance
|
||||
artifactHash string
|
||||
expected error
|
||||
}{
|
||||
@@ -507,7 +524,7 @@ func Test_VerifyBranch(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prov slsaprovenance.Provenance
|
||||
prov iface.Provenance
|
||||
branch string
|
||||
expected error
|
||||
}{
|
||||
@@ -574,7 +591,7 @@ func Test_VerifyWorkflowInputs(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prov slsaprovenance.Provenance
|
||||
prov iface.Provenance
|
||||
inputs map[string]string
|
||||
expected error
|
||||
}{
|
||||
@@ -696,7 +713,7 @@ func Test_VerifyTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prov slsaprovenance.Provenance
|
||||
prov iface.Provenance
|
||||
tag string
|
||||
expected error
|
||||
}{
|
||||
@@ -763,7 +780,7 @@ func Test_VerifyVersionedTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prov slsaprovenance.Provenance
|
||||
prov iface.Provenance
|
||||
tag string
|
||||
expected error
|
||||
}{
|
||||
|
||||
+29
-25
@@ -1,18 +1,22 @@
|
||||
package slsaprovenance
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
|
||||
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProvenanceV02Type is the SLSA v0.2 predicate type.
|
||||
ProvenanceV02Type = "https://slsa.dev/provenance/v0.2"
|
||||
)
|
||||
|
||||
// GetWorkflowInputs gets the workflow inputs from the GitHub environment map
|
||||
// and converts the keys to the necessary casing depending on predicate type.
|
||||
func GetWorkflowInputs(environment map[string]any, predicateType string) (map[string]any, error) {
|
||||
func GetWorkflowInputs(environment map[string]any, upperEnv bool) (map[string]any, error) {
|
||||
// Verify it's a workflow_dispatch trigger.
|
||||
eventKey, err := convertKey("github_event_name", predicateType)
|
||||
eventKey, err := convertKey("github_event_name", upperEnv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s",
|
||||
serrors.ErrorMismatchWorkflowInputs, err)
|
||||
@@ -26,7 +30,7 @@ func GetWorkflowInputs(environment map[string]any, predicateType string) (map[st
|
||||
serrors.ErrorMismatchWorkflowInputs, triggerName)
|
||||
}
|
||||
|
||||
payload, err := GetEventPayload(environment, predicateType)
|
||||
payload, err := GetEventPayload(environment, upperEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -45,8 +49,8 @@ func GetWorkflowInputs(environment map[string]any, predicateType string) (map[st
|
||||
|
||||
// GetEventPayload retrieves the GitHub event payload from the environment map
|
||||
// that contains the GitHub context payload.
|
||||
func GetEventPayload(environment map[string]any, predicateType string) (map[string]any, error) {
|
||||
eventPayloadKey, err := convertKey("github_event_payload", predicateType)
|
||||
func GetEventPayload(environment map[string]any, upperEnv bool) (map[string]any, error) {
|
||||
eventPayloadKey, err := convertKey("github_event_payload", upperEnv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -63,15 +67,11 @@ func GetEventPayload(environment map[string]any, predicateType string) (map[stri
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func convertKey(key, predicateType string) (string, error) {
|
||||
switch predicateType {
|
||||
case slsa1.PredicateSLSAProvenance:
|
||||
func convertKey(key string, upperEnv bool) (string, error) {
|
||||
if upperEnv {
|
||||
return strings.ToUpper(key), nil
|
||||
case ProvenanceV02Type:
|
||||
return key, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unrecognized predicate type %s", predicateType)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func getAsAny(environment map[string]any, field string) (any, error) {
|
||||
@@ -83,8 +83,8 @@ func getAsAny(environment map[string]any, field string) (any, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func getBranchForTag(environment map[string]any, predicateType string) (string, error) {
|
||||
baseRefKey, err := convertKey("github_base_ref", predicateType)
|
||||
func getBranchForTag(environment map[string]any, upperEnv bool) (string, error) {
|
||||
baseRefKey, err := convertKey("github_base_ref", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func getBranchForTag(environment map[string]any, predicateType string) (string,
|
||||
}
|
||||
|
||||
// Look at the event payload instead.
|
||||
environmentKey, err := convertKey("github_event_name", predicateType)
|
||||
environmentKey, err := convertKey("github_event_name", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func getBranchForTag(environment map[string]any, predicateType string) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
payload, err := GetEventPayload(environment, predicateType)
|
||||
payload, err := GetEventPayload(environment, upperEnv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -154,8 +154,9 @@ func getBranchForTag(environment map[string]any, predicateType string) (string,
|
||||
}
|
||||
}
|
||||
|
||||
func GetTag(environment map[string]any, predicateType string) (string, error) {
|
||||
refTypeKey, err := convertKey("github_ref_type", predicateType)
|
||||
// GetTag returns the triggering event's tag based on the given environment.
|
||||
func GetTag(environment map[string]any, upperEnv bool) (string, error) {
|
||||
refTypeKey, err := convertKey("github_ref_type", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -169,7 +170,7 @@ func GetTag(environment map[string]any, predicateType string) (string, error) {
|
||||
case "branch":
|
||||
return "", nil
|
||||
case "tag":
|
||||
refKey, err := convertKey("github_ref", predicateType)
|
||||
refKey, err := convertKey("github_ref", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -180,8 +181,9 @@ func GetTag(environment map[string]any, predicateType string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func GetBranch(environment map[string]any, predicateType string) (string, error) {
|
||||
refTypeKey, err := convertKey("github_ref_type", predicateType)
|
||||
// GetBranch returns the triggering event's branch based on the given environment.
|
||||
func GetBranch(environment map[string]any, upperEnv bool) (string, error) {
|
||||
refTypeKey, err := convertKey("github_ref_type", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
@@ -193,24 +195,26 @@ func GetBranch(environment map[string]any, predicateType string) (string, error)
|
||||
|
||||
switch refType {
|
||||
case "branch":
|
||||
refKey, err := convertKey("github_ref", predicateType)
|
||||
refKey, err := convertKey("github_ref", upperEnv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
|
||||
}
|
||||
return GetAsString(environment, refKey)
|
||||
case "tag":
|
||||
return getBranchForTag(environment, predicateType)
|
||||
return getBranchForTag(environment, upperEnv)
|
||||
default:
|
||||
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
|
||||
"unknown ref type", refType)
|
||||
}
|
||||
}
|
||||
|
||||
// Exists returns true if the given key exists in the environment.
|
||||
func Exists(environment map[string]any, field string) bool {
|
||||
_, ok := environment[field]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetAsString returns the value in the given environment as a string.
|
||||
func GetAsString(environment map[string]any, field string) (string, error) {
|
||||
value, ok := environment[field]
|
||||
if !ok {
|
||||
@@ -0,0 +1,50 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
)
|
||||
|
||||
// Provenance represents provenance for a predicate type and build type.
|
||||
type Provenance interface {
|
||||
// BuilderID returns the builder id in the predicate.
|
||||
BuilderID() (string, error)
|
||||
|
||||
// SourceURI is the full URI (including tag) of the source material.
|
||||
SourceURI() (string, error)
|
||||
|
||||
// TriggerURI is the full URI (including tag) of the configuration / trigger.
|
||||
TriggerURI() (string, error)
|
||||
|
||||
// Subject is the list of intoto subjects in the provenance.
|
||||
Subjects() ([]intoto.Subject, error)
|
||||
|
||||
// GetBranch retrieves the branch name of the source from the provenance.
|
||||
GetBranch() (string, error)
|
||||
|
||||
// GetTag retrieves the tag of the source from the provenance.
|
||||
GetTag() (string, error)
|
||||
|
||||
// Get workflow trigger path.
|
||||
GetBuildTriggerPath() (string, error)
|
||||
|
||||
// Get system pararmeters.
|
||||
GetSystemParameters() (map[string]any, error)
|
||||
|
||||
// Get build invocation ID.
|
||||
GetBuildInvocationID() (string, error)
|
||||
|
||||
// Get build start time.
|
||||
GetBuildStartTime() (*time.Time, error)
|
||||
|
||||
// Get build finish time.
|
||||
GetBuildFinishTime() (*time.Time, error)
|
||||
|
||||
// Get number of resolved dependencies.
|
||||
GetNumberResolvedDependencies() (int, error)
|
||||
|
||||
// GetWorkflowInputs retrieves the inputs from the provenance. Only succeeds for event
|
||||
// relevant event types (workflow_inputs).
|
||||
GetWorkflowInputs() (map[string]interface{}, error)
|
||||
}
|
||||
@@ -1,69 +1,32 @@
|
||||
package slsaprovenance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
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"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
slsav02 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v0.2"
|
||||
slsav1 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
|
||||
)
|
||||
|
||||
const (
|
||||
ProvenanceV02Type = "https://slsa.dev/provenance/v0.2"
|
||||
)
|
||||
// provenanceConstructor creates a new Provenance instance for the given payload as a json Decoder.
|
||||
type provenanceConstructor func(payload []byte) (iface.Provenance, error)
|
||||
|
||||
type Provenance interface {
|
||||
// BuilderID returns the builder id in the predicate.
|
||||
BuilderID() (string, error)
|
||||
|
||||
// SourceURI is the full URI (including tag) of the source material.
|
||||
SourceURI() (string, error)
|
||||
|
||||
// TriggerURI is the full URI (including tag) of the configuration / trigger.
|
||||
TriggerURI() (string, error)
|
||||
|
||||
// Subject is the list of intoto subjects in the provenance.
|
||||
Subjects() ([]intoto.Subject, error)
|
||||
|
||||
// GetBranch retrieves the branch name of the source from the provenance.
|
||||
GetBranch() (string, error)
|
||||
|
||||
// GetTag retrieves the tag of the source from the provenance.
|
||||
GetTag() (string, error)
|
||||
|
||||
// Get workflow trigger path.
|
||||
GetBuildTriggerPath() (string, error)
|
||||
|
||||
// Get system pararmeters.
|
||||
GetSystemParameters() (map[string]any, error)
|
||||
|
||||
// Get build invocation ID.
|
||||
GetBuildInvocationID() (string, error)
|
||||
|
||||
// Get build start time.
|
||||
GetBuildStartTime() (*time.Time, error)
|
||||
|
||||
// Get build finish time.
|
||||
GetBuildFinishTime() (*time.Time, error)
|
||||
|
||||
// Get number of resolved dependencies.
|
||||
GetNumberResolvedDependencies() (int, error)
|
||||
|
||||
// GetWorkflowInputs retrieves the inputs from the provenance. Only succeeds for event
|
||||
// relevant event types (workflow_inputs).
|
||||
GetWorkflowInputs() (map[string]interface{}, 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,
|
||||
}
|
||||
|
||||
// ProvenanceMap stores the different provenance version types.
|
||||
var ProvenanceMap sync.Map
|
||||
|
||||
// Provenance interface that each type may implement.
|
||||
func ProvenanceFromEnvelope(env *dsselib.Envelope) (Provenance, error) {
|
||||
// ProvenanceFromEnvelope returns a Provenance instance for the given DSSE Envelope.
|
||||
func ProvenanceFromEnvelope(env *dsselib.Envelope) (iface.Provenance, error) {
|
||||
if env.PayloadType != "application/vnd.in-toto+json" {
|
||||
return nil, fmt.Errorf("%w: expected payload type 'application/vnd.in-toto+json', got '%s'",
|
||||
serrors.ErrorInvalidDssePayload, env.PayloadType)
|
||||
@@ -82,19 +45,14 @@ func ProvenanceFromEnvelope(env *dsselib.Envelope) (Provenance, error) {
|
||||
}
|
||||
|
||||
// Load the appropriate structure and unmarshal.
|
||||
ptype, ok := ProvenanceMap.Load(pred.PredicateType)
|
||||
newProv, ok := predicateTypeMap[pred.PredicateType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: unexpected predicate type '%s'", serrors.ErrorInvalidDssePayload, pred.PredicateType)
|
||||
}
|
||||
prov := ptype.(func() Provenance)()
|
||||
|
||||
// Strict unmarshal.
|
||||
// NOTE: this supports extensions because they are
|
||||
// only used as part of interface{}-defined fields.
|
||||
dec := json.NewDecoder(bytes.NewReader(pyld))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(prov); err != nil {
|
||||
prov, err := newProv(pyld)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error())
|
||||
}
|
||||
|
||||
return prov, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package v02
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// provenanceV02 implements basic logic for SLSA v0.2 provenance.
|
||||
type provenanceV02 struct {
|
||||
// upperEnv specifies if environment fields are in uppercase.
|
||||
upperEnv bool
|
||||
prov *intotoAttestation
|
||||
}
|
||||
|
||||
// Predicate implements provenanceV02.Predicate.
|
||||
func (p *provenanceV02) Predicate() slsa02.ProvenancePredicate {
|
||||
return p.prov.Predicate
|
||||
}
|
||||
|
||||
// BuilderID implements Provenance.BuilderID.
|
||||
func (p *provenanceV02) BuilderID() (string, error) {
|
||||
return p.prov.Predicate.Builder.ID, nil
|
||||
}
|
||||
|
||||
// SourceURI implements Provenance.SourceURI.
|
||||
func (p *provenanceV02) SourceURI() (string, error) {
|
||||
if len(p.prov.Predicate.Materials) == 0 {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no material")
|
||||
}
|
||||
uri := p.prov.Predicate.Materials[0].URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
}
|
||||
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
// TriggerURI implements Provenance.TriggerURI.
|
||||
func (p *provenanceV02) TriggerURI() (string, error) {
|
||||
uri := p.prov.Predicate.Invocation.ConfigSource.URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
}
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
// Subjects implements Provenance.Subjects.
|
||||
func (p *provenanceV02) 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 *provenanceV02) GetBranch() (string, error) {
|
||||
// GetBranch gets the branch from the invocation parameters.
|
||||
environment, ok := p.prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return common.GetBranch(environment, p.upperEnv)
|
||||
}
|
||||
|
||||
// GetTag implements Provenance.GetTag.
|
||||
func (p *provenanceV02) GetTag() (string, error) {
|
||||
environment, ok := p.prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return common.GetTag(environment, p.upperEnv)
|
||||
}
|
||||
|
||||
// GetWorkflowInputs implements Provenance.GetWorkflowInputs.
|
||||
func (p *provenanceV02) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
// Verify it's a workflow_dispatch trigger.
|
||||
environment, ok := p.prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return common.GetWorkflowInputs(environment, p.upperEnv)
|
||||
}
|
||||
|
||||
// GetBuildTriggerPath implements Provenance.GetBuildTriggerPath.
|
||||
func (p *provenanceV02) GetBuildTriggerPath() (string, error) {
|
||||
return p.prov.Predicate.Invocation.ConfigSource.EntryPoint, nil
|
||||
}
|
||||
|
||||
// GetBuildInvocationID implements Provenance.GetBuildInvocationID.
|
||||
func (p *provenanceV02) GetBuildInvocationID() (string, error) {
|
||||
if p.prov.Predicate.Metadata == nil {
|
||||
return "", nil
|
||||
}
|
||||
return p.prov.Predicate.Metadata.BuildInvocationID, nil
|
||||
}
|
||||
|
||||
// GetBuildStartTime implements Provenance.GetBuildStartTime.
|
||||
func (p *provenanceV02) GetBuildStartTime() (*time.Time, error) {
|
||||
if p.prov.Predicate.Metadata == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return p.prov.Predicate.Metadata.BuildStartedOn, nil
|
||||
}
|
||||
|
||||
// GetBuildFinishTime implements Provenance.GetBuildFinishTime.
|
||||
func (p *provenanceV02) GetBuildFinishTime() (*time.Time, error) {
|
||||
if p.prov.Predicate.Metadata == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return p.prov.Predicate.Metadata.BuildFinishedOn, nil
|
||||
}
|
||||
|
||||
// GetNumberResolvedDependencies implements Provenance.GetNumberResolvedDependencies.
|
||||
func (p *provenanceV02) GetNumberResolvedDependencies() (int, error) {
|
||||
return len(p.prov.Predicate.Materials), nil
|
||||
}
|
||||
|
||||
// GetSystemParameters implements Provenance.GetSystemParameters.
|
||||
func (p *provenanceV02) GetSystemParameters() (map[string]any, error) {
|
||||
environment, ok := p.prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
return environment, nil
|
||||
}
|
||||
@@ -1,126 +1,73 @@
|
||||
package v02
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
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"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/473): Use a static mapping.
|
||||
//
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
slsaprovenance.ProvenanceMap.Store(
|
||||
slsaprovenance.ProvenanceV02Type,
|
||||
New)
|
||||
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"
|
||||
)
|
||||
|
||||
// intotoAttestation is a SLSA v0.2 in-toto attestation statement.
|
||||
type intotoAttestation struct {
|
||||
intoto.StatementHeader
|
||||
Predicate slsa02.ProvenancePredicate `json:"predicate"`
|
||||
}
|
||||
|
||||
type ProvenanceV02 struct {
|
||||
*intoto.ProvenanceStatement
|
||||
// ProvenanceV02 represents v0.2 provenance.
|
||||
type ProvenanceV02 interface {
|
||||
Predicate() slsa02.ProvenancePredicate
|
||||
}
|
||||
|
||||
// This returns a new, empty instance of the v0.2 provenance.
|
||||
func New() slsaprovenance.Provenance {
|
||||
return &ProvenanceV02{}
|
||||
}
|
||||
// New returns a new Provenance for the given json payload.
|
||||
func New(payload []byte) (iface.Provenance, error) {
|
||||
// Strict unmarshal.
|
||||
// NOTE: this supports extensions because they are
|
||||
// only used as part of interface{}-defined fields.
|
||||
dec := json.NewDecoder(bytes.NewReader(payload))
|
||||
dec.DisallowUnknownFields()
|
||||
|
||||
func (prov *ProvenanceV02) BuilderID() (string, error) {
|
||||
return prov.Predicate.Builder.ID, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) SourceURI() (string, error) {
|
||||
if len(prov.Predicate.Materials) == 0 {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no material")
|
||||
}
|
||||
uri := prov.Predicate.Materials[0].URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
a := &intotoAttestation{}
|
||||
if err := dec.Decode(a); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) TriggerURI() (string, error) {
|
||||
uri := prov.Predicate.Invocation.ConfigSource.URI
|
||||
if uri == "" {
|
||||
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
|
||||
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)
|
||||
}
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) Subjects() ([]intoto.Subject, error) {
|
||||
subj := prov.Subject
|
||||
if len(subj) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no subjects")
|
||||
}
|
||||
return subj, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetBranch() (string, error) {
|
||||
// GetBranch gets the branch from the invocation parameters.
|
||||
environment, ok := prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return slsaprovenance.GetBranch(environment, prov.PredicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetTag() (string, error) {
|
||||
environment, ok := prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return slsaprovenance.GetTag(environment, prov.PredicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
// Verify it's a workflow_dispatch trigger.
|
||||
environment, ok := prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
|
||||
return slsaprovenance.GetWorkflowInputs(environment, prov.PredicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetBuildTriggerPath() (string, error) {
|
||||
return prov.Predicate.Invocation.ConfigSource.EntryPoint, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetBuildInvocationID() (string, error) {
|
||||
if prov.Predicate.Metadata == nil {
|
||||
return "", nil
|
||||
}
|
||||
return prov.Predicate.Metadata.BuildInvocationID, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetBuildStartTime() (*time.Time, error) {
|
||||
if prov.Predicate.Metadata == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return prov.Predicate.Metadata.BuildStartedOn, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetBuildFinishTime() (*time.Time, error) {
|
||||
if prov.Predicate.Metadata == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return prov.Predicate.Metadata.BuildFinishedOn, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetNumberResolvedDependencies() (int, error) {
|
||||
return len(prov.Predicate.Materials), nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV02) GetSystemParameters() (map[string]any, error) {
|
||||
environment, ok := prov.Predicate.Invocation.Environment.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "parameters type")
|
||||
}
|
||||
return environment, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// byobBuildType is the base build type for BYOB delegated builders.
|
||||
var byobBuildType = "https://github.com/slsa-framework/slsa-github-generator/delegator-generic@v0"
|
||||
|
||||
// BYOBProvenance is SLSA v1.0 provenance for the slsa-github-generator BYOB build type.
|
||||
type BYOBProvenance struct {
|
||||
prov *intotoAttestation
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
return common.GetBranch(sysParams, true)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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 *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
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package v1
|
||||
|
||||
// containerBasedBuildType is the build type for the container-based builder and is based on BYOB.
|
||||
var containerBasedBuildType = "https://slsa.dev/container-based-build/v0.1?draft"
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1,217 +1,52 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"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"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
|
||||
)
|
||||
|
||||
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/473): Use a static mapping.
|
||||
//
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
slsaprovenance.ProvenanceMap.Store(
|
||||
slsa1.PredicateSLSAProvenance,
|
||||
New)
|
||||
}
|
||||
|
||||
type ProvenanceV1 struct {
|
||||
type intotoAttestation struct {
|
||||
intoto.StatementHeader
|
||||
Predicate slsa1.ProvenancePredicate `json:"predicate"`
|
||||
predicateType string
|
||||
Predicate slsa1.ProvenancePredicate `json:"predicate"`
|
||||
}
|
||||
|
||||
// This returns a new, empty instance of the v0.2 provenance.
|
||||
func New() slsaprovenance.Provenance {
|
||||
return &ProvenanceV1{
|
||||
predicateType: slsa1.PredicateSLSAProvenance,
|
||||
}
|
||||
// ProvenanceV1 represents v1.0 provenance.
|
||||
type ProvenanceV1 interface {
|
||||
Predicate() slsa1.ProvenancePredicate
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) BuilderID() (string, error) {
|
||||
return prov.Predicate.RunDetails.Builder.ID, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) SourceURI() (string, error) {
|
||||
// Use resolvedDependencies.
|
||||
if len(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 := 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 (prov *ProvenanceV1) generatorTriggerInfo() (string, string, string, error) {
|
||||
// See https://github.com/slsa-framework/github-actions-buildtypes/blob/main/workflow/v1/example.json#L16-L19.
|
||||
extParams, ok := 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)
|
||||
// New returns a new Provenance object based on the payload.
|
||||
func New(payload []byte) (iface.Provenance, error) {
|
||||
// Strict unmarshal.
|
||||
// NOTE: this supports extensions because they are
|
||||
// only used as part of interface{}-defined fields.
|
||||
dec := json.NewDecoder(bytes.NewReader(payload))
|
||||
dec.DisallowUnknownFields()
|
||||
|
||||
a := &intotoAttestation{}
|
||||
if err := dec.Decode(a); err != nil {
|
||||
return nil, 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)
|
||||
}
|
||||
path, err := getValidateKey(workflowMap, "path")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
return repository, ref, path, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) builderTriggerInfo() (string, string, string, error) {
|
||||
sysParams, ok := 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 := slsaprovenance.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 (prov *ProvenanceV1) triggerInfo() (string, string, string, error) {
|
||||
// TODO(#613): Support for generators.
|
||||
return prov.builderTriggerInfo()
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) TriggerURI() (string, error) {
|
||||
repository, ref, _, err := prov.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
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) Subjects() ([]intoto.Subject, error) {
|
||||
subj := prov.Subject
|
||||
if len(subj) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no subjects")
|
||||
}
|
||||
return subj, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetBranch() (string, error) {
|
||||
// TODO(https://github.com/slsa-framework/slsa-verifier/issues/472): Add GetBranch() support.
|
||||
sysParams, ok := prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
|
||||
}
|
||||
|
||||
return slsaprovenance.GetBranch(sysParams, prov.predicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetTag() (string, error) {
|
||||
sysParams, ok := prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
return slsaprovenance.GetTag(sysParams, prov.predicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetWorkflowInputs() (map[string]interface{}, error) {
|
||||
sysParams, ok := prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
return slsaprovenance.GetWorkflowInputs(sysParams, prov.predicateType)
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetBuildTriggerPath() (string, error) {
|
||||
_, _, path, err := prov.triggerInfo()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetBuildInvocationID() (string, error) {
|
||||
return prov.Predicate.RunDetails.BuildMetadata.InvocationID, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetBuildStartTime() (*time.Time, error) {
|
||||
return prov.Predicate.RunDetails.BuildMetadata.StartedOn, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetBuildFinishTime() (*time.Time, error) {
|
||||
return prov.Predicate.RunDetails.BuildMetadata.FinishedOn, nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetNumberResolvedDependencies() (int, error) {
|
||||
return len(prov.Predicate.BuildDefinition.ResolvedDependencies), nil
|
||||
}
|
||||
|
||||
func (prov *ProvenanceV1) GetSystemParameters() (map[string]any, error) {
|
||||
sysParams, ok := prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "system parameters type")
|
||||
}
|
||||
|
||||
return sysParams, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user