diff --git a/.golangci.yml b/.golangci.yml index 5c11d7a65..50e604020 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,6 +2,8 @@ run: concurrency: 2 deadline: 5m + # For generics. + go: 1.18 issues: include: - EXC0012 diff --git a/cli/slsa-verifier/main_regression_test.go b/cli/slsa-verifier/main_regression_test.go index b82f68b59..c3f6eb7e7 100644 --- a/cli/slsa-verifier/main_regression_test.go +++ b/cli/slsa-verifier/main_regression_test.go @@ -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 diff --git a/verifiers/internal/gha/builder.go b/verifiers/internal/gha/builder.go index fa3f89059..8149d95c3 100644 --- a/verifiers/internal/gha/builder.go +++ b/verifiers/internal/gha/builder.go @@ -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) diff --git a/verifiers/internal/gha/provenance.go b/verifiers/internal/gha/provenance.go index 05cc98ec0..d73568ab7 100644 --- a/verifiers/internal/gha/provenance.go +++ b/verifiers/internal/gha/provenance.go @@ -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 } diff --git a/verifiers/internal/gha/provenance_test.go b/verifiers/internal/gha/provenance_test.go index 8f994d3be..6907ed112 100644 --- a/verifiers/internal/gha/provenance_test.go +++ b/verifiers/internal/gha/provenance_test.go @@ -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)) } }) } diff --git a/verifiers/internal/gha/slsaprovenance/slsaprovenance.go b/verifiers/internal/gha/slsaprovenance/slsaprovenance.go index eded600d4..dc8b5fca1 100644 --- a/verifiers/internal/gha/slsaprovenance/slsaprovenance.go +++ b/verifiers/internal/gha/slsaprovenance/slsaprovenance.go @@ -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) diff --git a/verifiers/internal/gha/slsaprovenance/v0.2/provenance.go b/verifiers/internal/gha/slsaprovenance/v0.2/provenance.go index 46f6b7125..64b6543a3 100644 --- a/verifiers/internal/gha/slsaprovenance/v0.2/provenance.go +++ b/verifiers/internal/gha/slsaprovenance/v0.2/provenance.go @@ -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) { diff --git a/verifiers/internal/gha/slsaprovenance/v1.0/provenance.go b/verifiers/internal/gha/slsaprovenance/v1.0/provenance.go index 66c61cd35..c6f5a3397 100644 --- a/verifiers/internal/gha/slsaprovenance/v1.0/provenance.go +++ b/verifiers/internal/gha/slsaprovenance/v1.0/provenance.go @@ -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) { + // Use resolvedDependencies. + if len(prov.Predicate.BuildDefinition.ResolvedDependencies) == 0 { + return "", fmt.Errorf("%w: empty resovedDependencies", serrors.ErrorInvalidDssePayload) + } + uri := prov.Predicate.BuildDefinition.ResolvedDependencies[0].URI + if uri == "" { + return "", fmt.Errorf("%w: empty uri", serrors.ErrorMalformedURI) + } + return uri, nil +} + +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") + 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") } - source, ok := extParams["source"] + workflowMap, ok := workflow.(map[string]interface{}) if !ok { - return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters source") + return "", "", "", fmt.Errorf("%w: %s, type %T", serrors.ErrorInvalidDssePayload, "not a map of interface{}", workflow) } - sourceBytes, err := json.Marshal(source) + ref, err := getValidateKey(workflowMap, "ref") if err != nil { - return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err) + return "", "", "", fmt.Errorf("%w: %v", serrors.ErrorMalformedURI, err) } - var sourceRef slsa1.ResourceDescriptor - if err := json.Unmarshal(sourceBytes, &sourceRef); err != nil { - return "", fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters source type") + repository, err := getValidateKey(workflowMap, "repository") + if err != nil { + return "", "", "", fmt.Errorf("%w: %v", serrors.ErrorMalformedURI, err) } - return sourceRef.URI, nil + path, err := getValidateKey(workflowMap, "path") + if err != nil { + return "", "", "", err + } + return repository, ref, path, nil } -func (prov *ProvenanceV1) ConfigURI() (string, error) { - // The source and config are the same for GHA provenance. - return prov.SourceURI() +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) {