refactor: Use full builder id (#648)

Internally use full builder IDs including server url rather than worflow
ref as a path. This should hopefully avoid confusion between dealing
with builder IDs and `GITHUB_WORKFLOW_REF` which only contains the path
portion. `GITHUB_WORKFLOW_REF` is the only thing that doesn't include
the domain/server url part of the workflow/builder ID. The Fulcio OID
claims include the full url.

Code extracted from #641

---------

Signed-off-by: Ian Lewis <ianlewis@google.com>
This commit is contained in:
Ian Lewis
2023-07-10 06:23:48 +00:00
committed by GitHub
parent 965f5784c1
commit 1778495466
8 changed files with 359 additions and 236 deletions
+57 -34
View File
@@ -4,11 +4,13 @@ import (
"crypto/x509"
"encoding/asn1"
"fmt"
"net/url"
"strings"
fulcio "github.com/sigstore/fulcio/pkg/certificate"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)
@@ -24,23 +26,18 @@ var (
)
var defaultArtifactTrustedReusableWorkflows = map[string]bool{
trustedBuilderRepository + "/.github/workflows/generator_generic_slsa3.yml": true,
trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml": true,
trustedBuilderRepository + "/.github/workflows/builder_container-based_slsa3.yml": true,
common.GenericGeneratorBuilderID: true,
common.GoBuilderID: true,
common.ContainerBasedBuilderID: true,
}
var defaultContainerTrustedReusableWorkflows = map[string]bool{
trustedBuilderRepository + "/.github/workflows/generator_container_slsa3.yml": true,
common.ContainerGeneratorBuilderID: true,
}
var (
delegatorGenericReusableWorkflow = trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml"
delegatorLowPermsGenericReusableWorkflow = trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml"
)
var defaultBYOBReusableWorkflows = map[string]bool{
delegatorGenericReusableWorkflow: true,
delegatorLowPermsGenericReusableWorkflow: true,
common.GenericDelegatorBuilderID: true,
common.GenericLowPermsDelegatorBuilderID: true,
}
// VerifyCertficateSourceRepository verifies the source repository.
@@ -69,26 +66,27 @@ func VerifyBuilderIdentity(id *WorkflowIdentity,
// Issuer verification.
// NOTE: this is necessary before we do any further verification.
if id.Issuer != certOidcIssuer {
return nil, false, fmt.Errorf("%w: %s", serrors.ErrorInvalidOIDCIssuer, id.Issuer)
return nil, false, fmt.Errorf("%w: %q", serrors.ErrorInvalidOIDCIssuer, id.Issuer)
}
// cert URI path is /org/repo/path/to/workflow@ref
workflowPath := strings.SplitN(id.SubjectWorkflowRef, "@", 2)
if len(workflowPath) < 2 {
return nil, false, fmt.Errorf("%w: workflow uri: %s", serrors.ErrorMalformedURI, id.SubjectWorkflowRef)
// cert URI is https://github.com/org/repo/path/to/workflow@ref
// Remove '@' from Path
workflowID := id.SubjectWorkflowName()
workflowTag := id.SubjectWorkflowRef()
if workflowID == "" || workflowTag == "" {
return nil, false, fmt.Errorf("%w: workflow uri: %q", serrors.ErrorMalformedURI, id.SubjectWorkflow.String())
}
// Verify trusted workflow.
reusableWorkflowPath := strings.Trim(workflowPath[0], "/")
reusableWorkflowTag := strings.Trim(workflowPath[1], "/")
builderID, byob, err := verifyTrustedBuilderID(reusableWorkflowPath, reusableWorkflowTag,
builderID, byob, err := verifyTrustedBuilderID(workflowID, workflowTag,
builderOpts.ExpectedID, defaultBuilders)
if err != nil {
return nil, byob, err
}
// Verify the ref is a full semantic version tag.
if err := verifyTrustedBuilderRef(id, reusableWorkflowTag); err != nil {
if err := verifyTrustedBuilderRef(id, workflowTag); err != nil {
return nil, byob, err
}
@@ -97,26 +95,25 @@ func VerifyBuilderIdentity(id *WorkflowIdentity,
// Verifies the builder ID at path against an expected builderID.
// If an expected builderID is not provided, uses the defaultBuilders.
func verifyTrustedBuilderID(certPath, certTag string, expectedBuilderID *string, defaultTrustedBuilders map[string]bool) (*utils.TrustedBuilderID, bool, error) {
func verifyTrustedBuilderID(certBuilderID, certTag string, expectedBuilderID *string, defaultTrustedBuilders map[string]bool) (*utils.TrustedBuilderID, bool, error) {
var trustedBuilderID *utils.TrustedBuilderID
var err error
certBuilderName := httpsGithubCom + certPath
// WARNING: we don't validate the tag here, because we need to allow
// refs/heads/main for e2e tests. See verifyTrustedBuilderRef().
// 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 with builderID provided: %t", serrors.ErrorUntrustedReusableWorkflow, certPath, expectedBuilderID != nil)
if _, ok := defaultTrustedBuilders[certBuilderID]; !ok {
return nil, false, fmt.Errorf("%w: %s with builderID provided: %t", serrors.ErrorUntrustedReusableWorkflow, certBuilderID, expectedBuilderID != nil)
}
// Construct the builderID using the certificate's builder's name and tag.
trustedBuilderID, err = utils.TrustedBuilderIDNew(certBuilderName+"@"+certTag, true)
trustedBuilderID, err = utils.TrustedBuilderIDNew(certBuilderID+"@"+certTag, true)
if err != nil {
return nil, false, err
}
} else {
// Verify the builderID.
// We only accept IDs on github.com.
trustedBuilderID, err = utils.TrustedBuilderIDNew(certBuilderName+"@"+certTag, true)
trustedBuilderID, err = utils.TrustedBuilderIDNew(certBuilderID+"@"+certTag, true)
if err != nil {
return nil, false, err
}
@@ -144,7 +141,7 @@ func verifyTrustedBuilderID(certPath, certTag string, expectedBuilderID *string,
func isTrustedDelegatorBuilder(certBuilder *utils.TrustedBuilderID, trustedBuilders map[string]bool) bool {
for byobBuilder := range defaultBYOBReusableWorkflows {
// Check that the certificate builder is a BYOB workflow.
if err := certBuilder.MatchesLoose(httpsGithubCom+byobBuilder, true); err == nil {
if err := certBuilder.MatchesLoose(byobBuilder, true); err == nil {
// We found a delegator workflow that matches the certificate identity.
// Check that the BYOB builder is trusted by the caller.
if _, ok := trustedBuilders[byobBuilder]; !ok {
@@ -204,6 +201,7 @@ const (
HostedGitHub
)
// WorkflowIdentity is a identity captured from a Fulcio certificate.
// See https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md.
type WorkflowIdentity struct {
// The source repository
@@ -218,7 +216,7 @@ type WorkflowIdentity struct {
SourceOwnerID *string
// Workflow path OIDC subject - ref of reuseable workflow or trigger workflow.
SubjectWorkflowRef string
SubjectWorkflow *url.URL
// Subject commit sha1.
SubjectSha1 *string
// Hosted status of the subject.
@@ -235,6 +233,33 @@ type WorkflowIdentity struct {
Issuer string
}
// SubjectWorkflowName returns the subject workflow without the git ref.
func (id *WorkflowIdentity) SubjectWorkflowName() string {
// NOTE: You should be able to copy a net.URL struct safely.
// See: https://github.com/golang/go/issues/38351
withoutRef := *id.SubjectWorkflow
withoutRef.Path = id.SubjectWorkflowPath()
return withoutRef.String()
}
// SubjectWorkflowPath returns the subject workflow without the server url.
func (id *WorkflowIdentity) SubjectWorkflowPath() string {
i := strings.LastIndex(id.SubjectWorkflow.Path, "@")
if i == -1 {
return id.SubjectWorkflow.Path
}
return id.SubjectWorkflow.Path[:i]
}
// SubjectWorkflowRef returns the ref for the subject workflow.
func (id *WorkflowIdentity) SubjectWorkflowRef() string {
i := strings.LastIndex(id.SubjectWorkflow.Path, "@")
if i == -1 {
return ""
}
return id.SubjectWorkflow.Path[i+1:]
}
func getHosted(cert *x509.Certificate) (*Hosted, error) {
runnerEnv, err := getExtension(cert, fulcio.OIDRunnerEnvironment, true)
if err != nil {
@@ -423,9 +448,7 @@ func GetWorkflowInfoFromCertificate(cert *x509.Certificate) (*WorkflowIdentity,
if !strings.HasPrefix(cert.URIs[0].Path, "/") {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidFormat, cert.URIs[0].Path)
}
// Remove the starting '/'.
// NOTE: The Path has the following structure: repo/name/path/to/workflow.yml@ref.
subjectWorkflowRef := cert.URIs[0].Path[1:]
subjectWorkflow := cert.URIs[0]
var pSubjectSha1, pSourceID, pSourceRef, pSourceOwnerID, pBuildConfigPath, pRunID *string
if subjectSha1 != "" {
@@ -451,9 +474,9 @@ func GetWorkflowInfoFromCertificate(cert *x509.Certificate) (*WorkflowIdentity,
// Issuer.
Issuer: issuer,
// Subject
SubjectWorkflowRef: subjectWorkflowRef,
SubjectSha1: pSubjectSha1,
SubjectHosted: subjectHosted,
SubjectWorkflow: subjectWorkflow,
SubjectSha1: pSubjectSha1,
SubjectHosted: subjectHosted,
// Source.
SourceRepository: sourceRepository,
SourceSha1: sourceSha1,
+237 -166
View File
@@ -13,9 +13,18 @@ import (
fulcio "github.com/sigstore/fulcio/pkg/certificate"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)
// Must checks the error and panics if not nil.
func Must[T any](val T, err error) T {
if err != nil {
panic(err)
}
return val
}
func Test_VerifyBuilderIdentity(t *testing.T) {
t.Parallel()
tests := []struct {
@@ -30,11 +39,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "invalid job workflow ref",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: "random/workflow/ref",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse("https://github.com/random/workflow/ref")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorMalformedURI,
@@ -42,11 +51,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "untrusted job workflow ref",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: "/malicious/slsa-go/.github/workflows/builder.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse("https://github.com/malicious/slsa-go/.github/workflows/builder.yml@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorUntrustedReusableWorkflow,
@@ -54,11 +63,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "untrusted job workflow ref for general repos",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/heads/main",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/heads/main")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorInvalidRef,
@@ -66,11 +75,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "untrusted cert issuer for general repos",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: "https://bad.issuer.com",
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://bad.issuer.com",
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorInvalidOIDCIssuer,
@@ -78,11 +87,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid trusted builder without tag",
workflow: &WorkflowIdentity{
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultArtifactTrustedReusableWorkflows,
builderID: "https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml",
@@ -90,11 +99,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid generic delegator builder without tag",
workflow: &WorkflowIdentity{
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GenericDelegatorBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultBYOBReusableWorkflows,
builderID: "https://github.com/" + trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml",
@@ -103,11 +112,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid low-perms delegator builder with short tag",
workflow: &WorkflowIdentity{
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
SourceRepository: trustedBuilderRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GenericLowPermsDelegatorBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: "https://token.actions.githubusercontent.com",
},
defaults: defaultBYOBReusableWorkflows,
builderID: "https://github.com/" + trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml@v1.2.3",
@@ -116,11 +125,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid main ref for e2e test",
workflow: &WorkflowIdentity{
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
defaults: defaultArtifactTrustedReusableWorkflows,
builderID: "https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml",
@@ -128,11 +137,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid main ref for e2e test - match builderID",
workflow: &WorkflowIdentity{
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml"),
@@ -143,11 +152,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid main ref for e2e test - mismatch builderID",
workflow: &WorkflowIdentity{
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("some-other-builderID"),
@@ -158,11 +167,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity - match builderID",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml"),
@@ -173,11 +182,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity - mismatch builderID",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("some-other-builderID"),
@@ -188,11 +197,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "invalid workflow identity with prerelease",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3-alpha",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3-alpha")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
err: serrors.ErrorInvalidRef,
defaults: defaultArtifactTrustedReusableWorkflows,
@@ -201,11 +210,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "invalid workflow identity with build",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3+123",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3+123")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorInvalidRef,
@@ -213,11 +222,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "invalid workflow identity with metadata",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3-alpha+123",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3-alpha+123")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
defaults: defaultArtifactTrustedReusableWorkflows,
err: serrors.ErrorInvalidRef,
@@ -225,11 +234,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity with fully qualified source",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
defaults: defaultArtifactTrustedReusableWorkflows,
builderID: "https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml",
@@ -237,11 +246,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity with fully qualified source - no default",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml"),
@@ -251,11 +260,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity with fully qualified source - match builderID",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("https://github.com/" + trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml"),
@@ -266,11 +275,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity with fully qualified source - mismatch builderID",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
buildOpts: &options.BuilderOpts{
ExpectedID: asStringPointer("some-other-builderID"),
@@ -281,11 +290,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
{
name: "valid workflow identity with fully qualified source - mismatch defaults",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
defaults: defaultContainerTrustedReusableWorkflows,
err: serrors.ErrorUntrustedReusableWorkflow,
@@ -304,11 +313,11 @@ func Test_VerifyBuilderIdentity(t *testing.T) {
}
id, byob, err := VerifyBuilderIdentity(tt.workflow, opts, tt.defaults)
if byob != tt.byob {
t.Errorf(cmp.Diff(byob, tt.byob))
t.Errorf("unexpected byob value:\n%s", cmp.Diff(tt.byob, byob))
}
if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("unexpected error (-want +got):\n%s", diff)
}
if err != nil {
return
@@ -331,16 +340,16 @@ func Test_isTrustedDelegatorBuilder(t *testing.T) {
}{
{
name: "match byob",
certBuilderID: "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/delegator_lowperms-generic_slsa3.yml@refs/tags/v1.6.0",
certBuilderID: common.GenericLowPermsDelegatorBuilderID + "@refs/tags/v1.6.0",
trustedBuilderIDs: map[string]bool{
"slsa-framework/slsa-github-generator/.github/workflows/delegator_lowperms-generic_slsa3.yml": true,
"slsa-framework/slsa-github-generator/.github/workflows/some_delegator.yml": true,
common.GenericLowPermsDelegatorBuilderID: true,
"https://github.com/slsa-framework/slsa-github-generator/.github/workflows/some_delegator.yml": true,
},
result: true,
},
{
name: "match byob but not caller trusted",
certBuilderID: "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/delegator_lowperms-generic_slsa3.yml@refs/tags/v1.6.0",
certBuilderID: common.GenericLowPermsDelegatorBuilderID + "@refs/tags/v1.6.0",
trustedBuilderIDs: map[string]bool{
"slsa-framework/slsa-github-generator/.github/workflows/some_other_delegator.yml": true,
"slsa-framework/slsa-github-generator/.github/workflows/some_delegator.yml": true,
@@ -377,22 +386,22 @@ func Test_VerifyCertficateSourceRepository(t *testing.T) {
{
name: "repo match",
workflow: &WorkflowIdentity{
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "asraa/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
source: "github.com/asraa/slsa-on-github-test",
},
{
name: "unexpected source for e2e test",
workflow: &WorkflowIdentity{
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: e2eTestRepository,
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
source: "malicious/source",
err: serrors.ErrorMismatchSource,
@@ -400,10 +409,10 @@ func Test_VerifyCertficateSourceRepository(t *testing.T) {
{
name: "valid main ref for builder",
workflow: &WorkflowIdentity{
SourceRepository: trustedBuilderRepository,
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: trustedBuilderRepository,
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
source: "malicious/source",
err: serrors.ErrorMismatchSource,
@@ -411,11 +420,11 @@ func Test_VerifyCertficateSourceRepository(t *testing.T) {
{
name: "unexpected source",
workflow: &WorkflowIdentity{
SourceRepository: "malicious/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflowRef: trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.2.3",
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
SourceRepository: "malicious/slsa-on-github-test",
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + "@refs/tags/v1.2.3")),
BuildTrigger: "workflow_dispatch",
Issuer: certOidcIssuer,
},
source: "asraa/slsa-on-github-test",
err: serrors.ErrorMismatchSource,
@@ -579,19 +588,18 @@ func Test_verifyTrustedBuilderID(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()
id, byob, err := verifyTrustedBuilderID(tt.path, tt.tag, tt.id, tt.defaults)
id, byob, err := verifyTrustedBuilderID(httpsGithubCom+tt.path, tt.tag, tt.id, tt.defaults)
if byob != tt.byob {
t.Errorf(cmp.Diff(byob, tt.byob))
}
if !errCmp(err, tt.err) {
t.Errorf(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
t.Fatalf("unexpected error (-want +got):\n%s", diff)
}
if err != nil {
return
}
expectedID := "https://github.com/" + tt.path + "@" + tt.tag
if err := id.MatchesLoose(expectedID, true); err != nil {
t.Errorf("matches failed:%v", err)
if tt.err == nil {
expectedID := httpsGithubCom + tt.path + "@" + tt.tag
if err := id.MatchesLoose(expectedID, true); err != nil {
t.Errorf("matches failed:%v", err)
}
}
})
}
@@ -872,7 +880,9 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
cert: x509.Certificate{
URIs: []*url.URL{
{
Path: "/" + repo + "/" + buildConfigPath,
Scheme: "https",
Host: "github.com",
Path: "/" + repo + "/" + buildConfigPath,
},
},
Extensions: []pkix.Extension{
@@ -899,11 +909,11 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
},
},
workflow: WorkflowIdentity{
Issuer: issuer,
SubjectWorkflowRef: repo + "/" + buildConfigPath,
SourceRepository: repo,
SourceSha1: digest,
BuildTrigger: trigger,
Issuer: issuer,
SubjectWorkflow: Must(url.Parse(httpsGithubCom + repo + "/" + buildConfigPath)),
SourceRepository: repo,
SourceSha1: digest,
BuildTrigger: trigger,
},
},
{
@@ -939,7 +949,9 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
cert: x509.Certificate{
URIs: []*url.URL{
{
Path: "/" + repo + "/" + buildConfigPath,
Scheme: "https",
Host: "github.com",
Path: "/" + repo + "/" + buildConfigPath,
},
},
Extensions: []pkix.Extension{
@@ -1016,18 +1028,18 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
},
},
workflow: WorkflowIdentity{
Issuer: issuer,
SubjectSha1: &subjectSha1,
SubjectHosted: &hosted,
SubjectWorkflowRef: repo + "/" + buildConfigPath,
SourceRepository: repo,
SourceSha1: digest,
SourceRef: &ref,
SourceID: &sourceID,
SourceOwnerID: &sourceOwnerID,
BuildTrigger: trigger,
BuildConfigPath: &buildConfigPath,
RunID: &invocationID,
Issuer: issuer,
SubjectSha1: &subjectSha1,
SubjectHosted: &hosted,
SubjectWorkflow: Must(url.Parse(httpsGithubCom + repo + "/" + buildConfigPath)),
SourceRepository: repo,
SourceSha1: digest,
SourceRef: &ref,
SourceID: &sourceID,
SourceOwnerID: &sourceOwnerID,
BuildTrigger: trigger,
BuildConfigPath: &buildConfigPath,
RunID: &invocationID,
},
},
{
@@ -1109,7 +1121,9 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
cert: x509.Certificate{
URIs: []*url.URL{
{
Path: "/" + repo + "/" + buildConfigPath,
Scheme: "https",
Host: "github.com",
Path: "/" + repo + "/" + buildConfigPath,
},
},
Extensions: []pkix.Extension{
@@ -1161,17 +1175,17 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
},
},
workflow: WorkflowIdentity{
Issuer: issuer,
SubjectWorkflowRef: repo + "/" + buildConfigPath,
SourceRepository: repo,
SourceSha1: digest,
BuildTrigger: trigger,
SubjectHosted: &hosted,
SourceRef: &ref,
SourceID: &sourceID,
SourceOwnerID: &sourceOwnerID,
BuildConfigPath: &buildConfigPath,
RunID: &invocationID,
Issuer: issuer,
SubjectWorkflow: Must(url.Parse(httpsGithubCom + repo + "/" + buildConfigPath)),
SourceRepository: repo,
SourceSha1: digest,
BuildTrigger: trigger,
SubjectHosted: &hosted,
SourceRef: &ref,
SourceID: &sourceID,
SourceOwnerID: &sourceOwnerID,
BuildConfigPath: &buildConfigPath,
RunID: &invocationID,
},
},
{
@@ -1246,3 +1260,60 @@ func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
})
}
}
func TestWorkflowIdentity(t *testing.T) {
t.Parallel()
tests := []struct {
name string
workflow WorkflowIdentity
workflowName string
workflowPath string
workflowRef string
}{
{
name: "no ref",
workflow: WorkflowIdentity{
SubjectWorkflow: Must(url.Parse("https://github.com/random/workflow/ref")),
},
workflowName: "https://github.com/random/workflow/ref",
workflowPath: "/random/workflow/ref",
workflowRef: "",
},
{
name: "with ref",
workflow: WorkflowIdentity{
SubjectWorkflow: Must(url.Parse("https://github.com/random/workflow/ref@refs/heads/foo")),
},
workflowName: "https://github.com/random/workflow/ref",
workflowPath: "/random/workflow/ref",
workflowRef: "refs/heads/foo",
},
{
name: "multiple (at) symbols",
workflow: WorkflowIdentity{
SubjectWorkflow: Must(url.Parse("https://github.com/random/work@flow/ref@refs/heads/foo")),
},
workflowName: "https://github.com/random/work@flow/ref",
workflowPath: "/random/work@flow/ref",
workflowRef: "refs/heads/foo",
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
t.Run(tt.name, func(t *testing.T) {
if got, want := tt.workflow.SubjectWorkflowName(), tt.workflowName; got != want {
t.Errorf("unexpected subject workflow name, got %q, want %q", got, want)
}
if got, want := tt.workflow.SubjectWorkflowPath(), tt.workflowPath; got != want {
t.Errorf("unexpected subject workflow path, got %q, want %q", got, want)
}
if got, want := tt.workflow.SubjectWorkflowRef(), tt.workflowRef; got != want {
t.Errorf("unexpected subject workflow ref, got %q, want %q", got, want)
}
})
}
}
+1 -6
View File
@@ -25,13 +25,8 @@ type hosted string
const (
hostedSelf hosted = "self-hosted"
hostedGitHub hosted = "github-hosted"
)
const (
publishAttestationV01 = "https://github.com/npm/attestation/tree/main/specs/publish/"
builderLegacyGitHubRunnerID = "https://github.com/actions/runner"
builderGitHubHostedRunnerID = builderLegacyGitHubRunnerID + "/" + string(hostedGitHub)
builderSelfHostedRunnerID = builderLegacyGitHubRunnerID + "/" + string(hostedSelf)
publishAttestationV01 = "https://github.com/npm/attestation/tree/main/specs/publish/"
)
var errrorInvalidAttestations = errors.New("invalid npm attestations")
@@ -310,7 +310,9 @@ func verifySystemParameters(prov iface.Provenance, workflow *WorkflowIdentity) e
return err
}
// 7. GITHUB_WORKFLOW_REF
if err := verifySystemParameter(sysParams, "GITHUB_WORKFLOW_REF", &workflow.SubjectWorkflowRef); err != nil {
// NOTE: GITHUB_WORKFLOW_REF does not include the server url or leading '/'
workflowPath := strings.TrimLeft(workflow.SubjectWorkflow.Path, "/")
if err := verifySystemParameter(sysParams, "GITHUB_WORKFLOW_REF", &workflowPath); err != nil {
return err
}
// 8. GITHUB_WORKFLOW_SHA
@@ -1,6 +1,7 @@
package gha
import (
"net/url"
"testing"
"time"
@@ -566,15 +567,15 @@ func Test_verifyMetadata(t *testing.T) {
func Test_verifySystemParameters(t *testing.T) {
t.Parallel()
expectedWorkflow := WorkflowIdentity{
BuildTrigger: "workflow_dispatch",
SubjectWorkflowRef: "laurentsimon/provenance-npm-test/.github/workflows/release.yml@refs/heads/main",
SubjectSha1: asStringPointer("b38894f2dda4355ea5606fccb166e61565e12a14"),
SourceRepository: "laurentsimon/provenance-npm-test",
SourceRef: asStringPointer("refs/heads/main"),
SourceID: asStringPointer("602223945"),
SourceOwnerID: asStringPointer("64505099"),
SourceSha1: "b38894f2dda4355ea5606fccb166e61565e12a14",
RunID: asStringPointer("4757060009/attempt/1"),
BuildTrigger: "workflow_dispatch",
SubjectWorkflow: Must(url.Parse(httpsGithubCom + "laurentsimon/provenance-npm-test/.github/workflows/release.yml@refs/heads/main")),
SubjectSha1: asStringPointer("b38894f2dda4355ea5606fccb166e61565e12a14"),
SourceRepository: "laurentsimon/provenance-npm-test",
SourceRef: asStringPointer("refs/heads/main"),
SourceID: asStringPointer("602223945"),
SourceOwnerID: asStringPointer("64505099"),
SourceSha1: "b38894f2dda4355ea5606fccb166e61565e12a14",
RunID: asStringPointer("4757060009/attempt/1"),
}
tests := []struct {
name string
@@ -949,16 +950,16 @@ func Test_verifySystemParameters(t *testing.T) {
func Test_verifyProvenanceMatchesCertificate(t *testing.T) {
t.Parallel()
expectedWorkflow := WorkflowIdentity{
BuildTrigger: "workflow_dispatch",
BuildConfigPath: asStringPointer("release/workflow/path"),
SubjectWorkflowRef: "repo/name/release/workflow/path@subject-ref",
SubjectSha1: asStringPointer("subject-sha"),
SourceRepository: "repo/name",
SourceRef: asStringPointer("source-ref"),
SourceID: asStringPointer("source-id"),
SourceOwnerID: asStringPointer("source-owner-id"),
SourceSha1: "source-sha",
RunID: asStringPointer("run-id/attempt/run-attempt"),
BuildTrigger: "workflow_dispatch",
BuildConfigPath: asStringPointer("release/workflow/path"),
SubjectWorkflow: Must(url.Parse(httpsGithubCom + "repo/name/release/workflow/path@subject-ref")),
SubjectSha1: asStringPointer("subject-sha"),
SourceRepository: "repo/name",
SourceRef: asStringPointer("source-ref"),
SourceID: asStringPointer("source-id"),
SourceOwnerID: asStringPointer("source-owner-id"),
SourceSha1: "source-sha",
RunID: asStringPointer("run-id/attempt/run-attempt"),
}
tests := []struct {
name string
@@ -0,0 +1,26 @@
package common
var trustedBuilderRepository = "https://github.com/slsa-framework/slsa-github-generator"
var (
// GenericGeneratorBuilderID is the builder ID for the Generic Generator.
GenericGeneratorBuilderID = trustedBuilderRepository + "/.github/workflows/generator_generic_slsa3.yml"
// ContainerGeneratorBuilderID is the builder ID for the Container Generator.
ContainerGeneratorBuilderID = trustedBuilderRepository + "/.github/workflows/generator_container_slsa3.yml"
// GoBuilderID is the SLSA builder ID for the Go Builder.
GoBuilderID = trustedBuilderRepository + "/.github/workflows/builder_go_slsa3.yml"
// ContainerBasedBuilderID is the SLSA builder ID for the Container-Based Builder.
ContainerBasedBuilderID = trustedBuilderRepository + "/.github/workflows/builder_container-based_slsa3.yml"
// NpmCLILegacyBuilderID is the legacy builder ID for the npm CLI.
NpmCLILegacyBuilderID = "https://github.com/actions/runner"
// NpmCLIHostedBuilderID is the builder ID for the npm CLI on Hosted GitHub Actions.
NpmCLIHostedBuilderID = NpmCLILegacyBuilderID + "/github-hosted"
// NpmCLISelfHostedBuilderID is the builder ID for the npm CLI on Self-hosted GitHub Actions.
NpmCLISelfHostedBuilderID = NpmCLILegacyBuilderID + "/self-hosted"
// GenericDelegatorBuilderID is the SLSA builder ID for the BYOB Generic Low-Permissions Delegated Builder.
GenericDelegatorBuilderID = trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml"
// GenericLowPermsDelegatorBuilderID is the SLSA builder ID for the BYOB Generic Low-Permissions Delegated Builder.
GenericLowPermsDelegatorBuilderID = trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml"
)
@@ -4,12 +4,16 @@ 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"
// ProvenanceV1Type is the SLSA v1.0 predicate type.
ProvenanceV1Type = slsa1.PredicateSLSAProvenance
)
// GetWorkflowInputs gets the workflow inputs from the GitHub environment map
@@ -176,7 +180,7 @@ func GetTag(environment map[string]any, upperEnv bool) (string, error) {
}
return GetAsString(environment, refKey)
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
return "", fmt.Errorf("%w: %s %q", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
}
@@ -203,7 +207,7 @@ func GetBranch(environment map[string]any, upperEnv bool) (string, error) {
case "tag":
return getBranchForTag(environment, upperEnv)
default:
return "", fmt.Errorf("%w: %s %s", serrors.ErrorInvalidDssePayload,
return "", fmt.Errorf("%w: %s %q", serrors.ErrorInvalidDssePayload,
"unknown ref type", refType)
}
}
+9 -8
View File
@@ -16,6 +16,7 @@ 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/register"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils/container"
)
@@ -82,8 +83,8 @@ func verifyEnvAndCert(env *dsse.Envelope,
return nil, nil, err
}
fmt.Fprintf(os.Stderr, "Verified build using builder https://github.com%s at commit %s\n",
workflowInfo.SubjectWorkflowRef,
fmt.Fprintf(os.Stderr, "Verified build using builder %q at commit %s\n",
workflowInfo.SubjectWorkflow.String(),
workflowInfo.SourceSha1)
// Return verified provenance.
r, err := base64.StdEncoding.DecodeString(env.Payload)
@@ -111,7 +112,7 @@ func verifyNpmEnvAndCert(env *dsse.Envelope,
// We verify against the delegator re-usable workflow, not the user-provided
// builder. This is because the signing identity for delegator-based builders
// is *always* the delegator workflow.
expectedDelegatorWorkflow := httpsGithubCom + delegatorLowPermsGenericReusableWorkflow
expectedDelegatorWorkflow := httpsGithubCom + common.GenericLowPermsDelegatorBuilderID
delegatorBuilderOpts := options.BuilderOpts{
ExpectedID: &expectedDelegatorWorkflow,
}
@@ -165,20 +166,20 @@ func verifyNpmEnvAndCert(env *dsse.Envelope,
// Verify that the value provided is consistent with certificate information.
if workflowInfo.SubjectHosted == nil {
return nil, fmt.Errorf("%w: hosted status unknonwn", serrors.ErrorNotSupported)
return nil, fmt.Errorf("%w: hosted status unknown", serrors.ErrorNotSupported)
}
switch *builderOpts.ExpectedID {
case builderLegacyGitHubRunnerID, builderGitHubHostedRunnerID:
case common.NpmCLILegacyBuilderID, common.NpmCLIHostedBuilderID:
if *workflowInfo.SubjectHosted != HostedGitHub {
return nil, fmt.Errorf("%w: re-usable workflow is self-hosted", serrors.ErrorMismatchBuilderID)
}
case builderSelfHostedRunnerID:
case common.NpmCLISelfHostedBuilderID:
if *workflowInfo.SubjectHosted != HostedSelf {
return nil, fmt.Errorf("%w: re-usable workflow is GitHub-hosted", serrors.ErrorMismatchBuilderID)
}
default:
return nil, fmt.Errorf("%w: builder %v. Expected one of %v, %v", serrors.ErrorNotSupported, *builderOpts.ExpectedID,
builderSelfHostedRunnerID, builderGitHubHostedRunnerID)
return nil, fmt.Errorf("%w: builder %q. Expected one of %q, %q", serrors.ErrorNotSupported, *builderOpts.ExpectedID,
common.NpmCLISelfHostedBuilderID, common.NpmCLIHostedBuilderID)
}
trustedBuilderID, err = utils.TrustedBuilderIDNew(*builderOpts.ExpectedID, false)