feat: Support for v1.0 verification in BYOB (#609)

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

* update

Signed-off-by: laurentsimon <laurentsimon@google.com>

---------

Signed-off-by: laurentsimon <laurentsimon@google.com>
This commit is contained in:
laurentsimon
2023-05-23 07:31:13 -07:00
committed by GitHub
parent bda35e0238
commit ba32c706ac
8 changed files with 254 additions and 318 deletions

View File

@@ -2,6 +2,8 @@
run:
concurrency: 2
deadline: 5m
# For generics.
go: 1.18
issues:
include:
- EXC0012

View File

@@ -1319,12 +1319,44 @@ func Test_runVerifyGHADockerBased(t *testing.T) {
inputs map[string]string
err error
}{
{
name: "valid main branch default",
artifacts: []string{"workflow_dispatch.main.default"},
source: "github.com/slsa-framework/example-package",
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
},
// TODO(#610): Re-enable these tests.
// {
// name: "valid main branch default",
// artifacts: []string{"workflow_dispatch.main.default"},
// source: "github.com/slsa-framework/example-package",
// pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
// },
// {
// name: "versioned tag no match empty tag workflow_dispatch",
// artifacts: []string{"workflow_dispatch.main.default"},
// source: "github.com/slsa-framework/example-package",
// pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
// pversiontag: pString("v1"),
// err: serrors.ErrorInvalidSemver,
// },
// {
// name: "tag no match empty tag workflow_dispatch",
// artifacts: []string{"workflow_dispatch.main.default"},
// source: "github.com/slsa-framework/example-package",
// pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
// ptag: pString("v1.2.3"),
// err: serrors.ErrorMismatchTag,
// },
// {
// name: "wrong branch master",
// artifacts: []string{"workflow_dispatch.main.default"},
// source: "github.com/slsa-framework/example-package",
// pbranch: pString("master"),
// pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
// err: serrors.ErrorMismatchBranch,
// },
// {
// name: "valid main branch set",
// artifacts: []string{"workflow_dispatch.main.default"},
// source: "github.com/slsa-framework/example-package",
// pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
// pbranch: pString("main"),
// },
{
name: "valid main branch default - invalid builderID",
artifacts: []string{"workflow_dispatch.main.default"},
@@ -1332,22 +1364,6 @@ func Test_runVerifyGHADockerBased(t *testing.T) {
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/not-trusted.yml"),
err: serrors.ErrorUntrustedReusableWorkflow,
},
{
name: "valid main branch set",
artifacts: []string{"workflow_dispatch.main.default"},
source: "github.com/slsa-framework/example-package",
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
pbranch: pString("main"),
},
{
name: "wrong branch master",
artifacts: []string{"workflow_dispatch.main.default"},
source: "github.com/slsa-framework/example-package",
pbranch: pString("master"),
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
err: serrors.ErrorMismatchBranch,
},
{
name: "wrong source append A",
artifacts: []string{"workflow_dispatch.main.default"},
@@ -1369,22 +1385,6 @@ func Test_runVerifyGHADockerBased(t *testing.T) {
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
err: serrors.ErrorMismatchSource,
},
{
name: "tag no match empty tag workflow_dispatch",
artifacts: []string{"workflow_dispatch.main.default"},
source: "github.com/slsa-framework/example-package",
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
ptag: pString("v1.2.3"),
err: serrors.ErrorMismatchTag,
},
{
name: "versioned tag no match empty tag workflow_dispatch",
artifacts: []string{"workflow_dispatch.main.default"},
source: "github.com/slsa-framework/example-package",
pBuilderID: pString("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_docker-based_slsa3.yml"),
pversiontag: pString("v1"),
err: serrors.ErrorInvalidSemver,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below

View File

@@ -108,7 +108,7 @@ func verifyTrustedBuilderID(certPath, certTag string, expectedBuilderID *string,
// No builder ID provided by user: use the default trusted workflows.
if expectedBuilderID == nil || *expectedBuilderID == "" {
if _, ok := defaultTrustedBuilders[certPath]; !ok {
return nil, false, fmt.Errorf("%w: %s got %t", serrors.ErrorUntrustedReusableWorkflow, certPath, expectedBuilderID == nil)
return nil, false, fmt.Errorf("%w: %s with builderID provided: %t", serrors.ErrorUntrustedReusableWorkflow, certPath, expectedBuilderID != nil)
}
// Construct the builderID using the certificate's builder's name and tag.
trustedBuilderID, err = utils.TrustedBuilderIDNew(certBuilderName+"@"+certTag, true)

View File

@@ -100,11 +100,12 @@ func verifySourceURI(prov slsaprovenance.Provenance, expectedSourceURI string, a
source)
}
// Verify source from ConfigSource field.
fullConfigURI, err := prov.ConfigURI()
// Verify source in the trigger
fullConfigURI, err := prov.TriggerURI()
if err != nil {
return err
}
configURI, err := sourceFromURI(fullConfigURI, false)
if err != nil {
return err
@@ -119,6 +120,7 @@ func verifySourceURI(prov slsaprovenance.Provenance, expectedSourceURI string, a
if err != nil {
return err
}
materialURI, err := sourceFromURI(materialSourceURI, allowNoMaterialRef)
if err != nil {
return err
@@ -165,6 +167,7 @@ func sourceFromURI(uri string, allowNoRef bool) (string, error) {
return "", fmt.Errorf("%w: %s", serrors.ErrorMalformedURI,
uri)
}
return r[0], nil
}

View File

@@ -182,286 +182,131 @@ func Test_verifySourceURI(t *testing.T) {
t.Parallel()
tests := []struct {
name string
prov *intoto.ProvenanceStatement
sourceURI string
provMaterialsURI string
provTriggerURI string
expectedSourceURI string
allowNoMaterialRef bool
expected error
err error
// v1 provenance does not include materials
skipv1 bool
}{
{
name: "source has no @",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "source has no @",
provMaterialsURI: "git+https://github.com/some/repo",
provTriggerURI: "git+https://github.com/some/repo",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "empty materials",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorInvalidDssePayload,
skipv1: true,
name: "empty materials",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorInvalidDssePayload,
},
{
name: "empty configSource",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "empty configSource",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "empty uri materials",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "empty uri materials",
provMaterialsURI: " ",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "no tag uri materials",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "no tag uri materials",
provTriggerURI: "git+https://github.com/some/repo",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "no tag uri configSource",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "no tag uri configSource",
provMaterialsURI: "git+https://github.com/some/repo",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "match source",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
name: "match source",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
},
{
name: "match source no git",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "https://github.com/some/repo",
name: "match source no git",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "https://github.com/some/repo",
},
{
name: "match source no git no material ref",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+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,
sourceURI: "https://github.com/some/repo",
expectedSourceURI: "https://github.com/some/repo",
},
{
name: "match source no git no material ref ref not allowed",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo",
},
},
},
},
sourceURI: "https://github.com/some/repo",
expected: serrors.ErrorMalformedURI,
skipv1: true,
name: "match source no git no material ref ref not allowed",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo",
expectedSourceURI: "https://github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "match source no git+https",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "github.com/some/repo",
name: "match source no git+https",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "github.com/some/repo",
},
{
name: "match source no repo",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "some/repo",
expected: serrors.ErrorMalformedURI,
name: "match source no repo",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "some/repo",
err: serrors.ErrorMalformedURI,
},
{
name: "mismatch materials configSource tag",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/repo@v1.2.4",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
skipv1: true,
expected: serrors.ErrorInvalidDssePayload,
name: "mismatch materials configSource tag",
provTriggerURI: "git+https://github.com/some/repo@v1.2.4",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorInvalidDssePayload,
},
{
name: "mismatch materials configSource org",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/other/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMismatchSource,
name: "mismatch materials configSource org",
provTriggerURI: "git+https://github.com/other/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMismatchSource,
},
{
name: "mismatch materials configSource name",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://github.com/some/other@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://github.com/some/repo",
expected: serrors.ErrorMismatchSource,
name: "mismatch configSource materials org",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/other/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMismatchSource,
},
{
name: "not github.com repo",
prov: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: "git+https://not-github.com/some/repo@v1.2.3",
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: "git+https://not-github.com/some/repo@v1.2.3",
},
},
},
},
sourceURI: "git+https://not-github.com/some/repo",
expected: serrors.ErrorMalformedURI,
name: "mismatch materials configSource name",
provTriggerURI: "git+https://github.com/some/other@v1.2.3",
provMaterialsURI: "git+https://github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMismatchSource,
},
{
name: "mismatch configSource materials name",
provTriggerURI: "git+https://github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://github.com/some/other@v1.2.3",
expectedSourceURI: "git+https://github.com/some/repo",
err: serrors.ErrorMismatchSource,
},
{
name: "not github.com repo",
provTriggerURI: "git+https://not-github.com/some/repo@v1.2.3",
provMaterialsURI: "git+https://not-github.com/some/repo@v1.2.3",
expectedSourceURI: "git+https://not-github.com/some/repo",
err: serrors.ErrorMalformedURI,
},
}
for _, tt := range tests {
@@ -470,12 +315,28 @@ func Test_verifySourceURI(t *testing.T) {
t.Parallel()
prov02 := &v02.ProvenanceV02{
ProvenanceStatement: tt.prov,
ProvenanceStatement: &intoto.ProvenanceStatement{
Predicate: slsa02.ProvenancePredicate{
Invocation: slsa02.ProvenanceInvocation{
ConfigSource: slsa02.ConfigSource{
URI: tt.provTriggerURI,
},
},
Materials: []slsacommon.ProvenanceMaterial{
{
URI: tt.provMaterialsURI,
},
},
},
},
}
if tt.provMaterialsURI == "" {
prov02.ProvenanceStatement.Predicate.Materials = nil
}
err := verifySourceURI(prov02, tt.sourceURI, tt.allowNoMaterialRef)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected))
err := verifySourceURI(prov02, tt.expectedSourceURI, tt.allowNoMaterialRef)
if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err))
}
if tt.skipv1 {
@@ -483,20 +344,40 @@ func Test_verifySourceURI(t *testing.T) {
}
// Update to v1 SLSA provenance.
var ref, repository string
a := strings.Split(tt.provTriggerURI, "@")
if len(a) > 0 {
repository = a[0]
}
if len(a) > 1 {
ref = a[1]
}
prov1 := &v1.ProvenanceV1{
Predicate: slsa1.ProvenancePredicate{
BuildDefinition: slsa1.ProvenanceBuildDefinition{
ExternalParameters: map[string]interface{}{
"source": slsa1.ResourceDescriptor{
URI: tt.prov.Predicate.Invocation.ConfigSource.URI,
"workflow": map[string]interface{}{
"ref": ref,
"repository": repository,
"path": "some/path",
},
},
ResolvedDependencies: []slsa1.ResourceDescriptor{
{
URI: tt.provMaterialsURI,
},
},
},
},
}
err = verifySourceURI(prov1, tt.sourceURI, tt.allowNoMaterialRef)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected))
if tt.provMaterialsURI == "" {
prov1.Predicate.BuildDefinition.ResolvedDependencies = nil
}
err = verifySourceURI(prov1, tt.expectedSourceURI, tt.allowNoMaterialRef)
if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err))
}
})
}

View File

@@ -24,8 +24,8 @@ type Provenance interface {
// SourceURI is the full URI (including tag) of the source material.
SourceURI() (string, error)
// ConfigURI is the full URI (including tag) of the configuration material.
ConfigURI() (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)

View File

@@ -35,11 +35,20 @@ func (prov *ProvenanceV02) SourceURI() (string, error) {
if len(prov.Predicate.Materials) == 0 {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "no material")
}
return prov.Predicate.Materials[0].URI, nil
uri := prov.Predicate.Materials[0].URI
if uri == "" {
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
}
return uri, nil
}
func (prov *ProvenanceV02) ConfigURI() (string, error) {
return prov.Predicate.Invocation.ConfigSource.URI, nil
func (prov *ProvenanceV02) TriggerURI() (string, error) {
uri := prov.Predicate.Invocation.ConfigSource.URI
if uri == "" {
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
}
return uri, nil
}
func (prov *ProvenanceV02) Subjects() ([]intoto.Subject, error) {

View File

@@ -1,7 +1,6 @@
package v1
import (
"encoding/json"
"fmt"
"time"
@@ -38,28 +37,70 @@ func (prov *ProvenanceV1) BuilderID() (string, error) {
}
func (prov *ProvenanceV1) SourceURI() (string, error) {
extParams, ok := prov.Predicate.BuildDefinition.ExternalParameters.(map[string]interface{})
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters type")
// Use resolvedDependencies.
if len(prov.Predicate.BuildDefinition.ResolvedDependencies) == 0 {
return "", fmt.Errorf("%w: empty resovedDependencies", serrors.ErrorInvalidDssePayload)
}
source, ok := extParams["source"]
if !ok {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters source")
uri := prov.Predicate.BuildDefinition.ResolvedDependencies[0].URI
if uri == "" {
return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI)
}
sourceBytes, err := json.Marshal(source)
if err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err)
}
var sourceRef slsa1.ResourceDescriptor
if err := json.Unmarshal(sourceBytes, &sourceRef); err != nil {
return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters source type")
}
return sourceRef.URI, nil
return uri, nil
}
func (prov *ProvenanceV1) ConfigURI() (string, error) {
// The source and config are the same for GHA provenance.
return prov.SourceURI()
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
}
func (prov *ProvenanceV1) triggerInfo() (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)
}
path, err := getValidateKey(workflowMap, "path")
if err != nil {
return "", "", "", err
}
return repository, ref, path, nil
}
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) {