mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-08-19 11:36:49 +00:00
This PR updates go to 1.23.1 and updates golanci-lint to v1.61.1, while fixing new lint errors. --------- Signed-off-by: Ramon Petgrave <ramon.petgrave64@gmail.com> Signed-off-by: Ramon Petgrave <32398091+ramonpetgrave64@users.noreply.github.com>
1338 lines
40 KiB
Go
1338 lines
40 KiB
Go
package gha
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
|
|
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"
|
|
)
|
|
|
|
var (
|
|
refs123 = "@refs/tags/v1.2.3"
|
|
githubWorkflows = "/.github/workflows/"
|
|
builderGoSlsa3 = githubWorkflows + "builder_go_slsa3.yml"
|
|
builderGoSlsa3GitURL = httpsGithubCom + trustedBuilderRepository + builderGoSlsa3
|
|
delegatorGenericSlsa3 = githubWorkflows + "delegator_generic_slsa3.yml"
|
|
generatorGenericSlsa3 = githubWorkflows + "generator_generic_slsa3.yml"
|
|
)
|
|
|
|
// 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 {
|
|
name string
|
|
workflow *WorkflowIdentity
|
|
buildOpts *options.BuilderOpts
|
|
builderID string
|
|
defaults map[string]bool
|
|
err error
|
|
byob bool
|
|
}{
|
|
{
|
|
name: "invalid job workflow ref",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
},
|
|
{
|
|
name: "untrusted job workflow ref",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
},
|
|
{
|
|
name: "untrusted job workflow ref for general repos",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
},
|
|
{
|
|
name: "untrusted cert issuer for general repos",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: "https://bad.issuer.com",
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
err: serrors.ErrorInvalidOIDCIssuer,
|
|
},
|
|
{
|
|
name: "valid trusted builder without tag",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: trustedBuilderRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: "https://token.actions.githubusercontent.com",
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid generic delegator builder without tag",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: trustedBuilderRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GenericDelegatorBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: "https://token.actions.githubusercontent.com",
|
|
},
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
builderID: httpsGithubCom + trustedBuilderRepository + delegatorGenericSlsa3,
|
|
byob: true,
|
|
},
|
|
{
|
|
name: "valid low-perms delegator builder with short tag",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: trustedBuilderRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GenericLowPermsDelegatorBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: "https://token.actions.githubusercontent.com",
|
|
},
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
builderID: httpsGithubCom + trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml@v1.2.3",
|
|
byob: true,
|
|
},
|
|
{
|
|
name: "valid main ref for e2e test",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: e2eTestRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid main ref for e2e test - match builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: e2eTestRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer(builderGoSlsa3GitURL),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid main ref for e2e test - mismatch builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: e2eTestRepository,
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer("some-other-builderID"),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "valid workflow identity - match builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer(builderGoSlsa3GitURL),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid workflow identity - mismatch builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer("some-other-builderID"),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "invalid workflow identity with prerelease",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "invalid workflow identity with build",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
},
|
|
{
|
|
name: "invalid workflow identity with metadata",
|
|
workflow: &WorkflowIdentity{
|
|
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,
|
|
},
|
|
{
|
|
name: "valid workflow identity with fully qualified source",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid workflow identity with fully qualified source - no default",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer(builderGoSlsa3GitURL),
|
|
},
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid workflow identity with fully qualified source - match builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer(builderGoSlsa3GitURL),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
builderID: builderGoSlsa3GitURL,
|
|
},
|
|
{
|
|
name: "valid workflow identity with fully qualified source - mismatch builderID",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
buildOpts: &options.BuilderOpts{
|
|
ExpectedID: asStringPointer("some-other-builderID"),
|
|
},
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "valid workflow identity with fully qualified source - mismatch defaults",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
defaults: defaultContainerTrustedReusableWorkflows,
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
opts := tt.buildOpts
|
|
if opts == nil {
|
|
opts = &options.BuilderOpts{}
|
|
}
|
|
if tt.builderID != "" {
|
|
opts.ExpectedID = &tt.builderID
|
|
}
|
|
id, byob, err := VerifyBuilderIdentity(tt.workflow, opts, tt.defaults)
|
|
if byob != tt.byob {
|
|
t.Errorf("unexpected byob value:\n%s", cmp.Diff(tt.byob, byob))
|
|
}
|
|
|
|
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
|
t.Errorf("unexpected error (-want +got):\n%s", diff)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err := id.MatchesLoose(tt.builderID, true); err != nil {
|
|
t.Errorf("matches failed:%v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_isTrustedDelegatorBuilder(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
certBuilderID string
|
|
trustedBuilderIDs map[string]bool
|
|
result bool
|
|
}{
|
|
{
|
|
name: "match byob",
|
|
certBuilderID: common.GenericLowPermsDelegatorBuilderID + "@refs/tags/v1.6.0",
|
|
trustedBuilderIDs: map[string]bool{
|
|
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: 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,
|
|
},
|
|
result: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
trustedBuilderID, err := utils.TrustedBuilderIDNew(tt.certBuilderID, true)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
|
|
res := isTrustedDelegatorBuilder(trustedBuilderID, tt.trustedBuilderIDs)
|
|
if res != tt.result {
|
|
t.Error(cmp.Diff(res, tt.result))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_VerifyCertficateSourceRepository(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
workflow *WorkflowIdentity
|
|
source string
|
|
err error
|
|
}{
|
|
{
|
|
name: "repo match",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "asraa/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
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",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
source: "malicious/source",
|
|
err: serrors.ErrorMismatchSource,
|
|
},
|
|
{
|
|
name: "valid main ref for builder",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: trustedBuilderRepository,
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
source: "malicious/source",
|
|
err: serrors.ErrorMismatchSource,
|
|
},
|
|
{
|
|
name: "unexpected source",
|
|
workflow: &WorkflowIdentity{
|
|
SourceRepository: "malicious/slsa-on-github-test",
|
|
SourceSha1: "0dfcd24824432c4ce587f79c918eef8fc2c44d7b",
|
|
SubjectWorkflow: Must(url.Parse(common.GoBuilderID + refs123)),
|
|
BuildTrigger: "workflow_dispatch",
|
|
Issuer: certOidcIssuer,
|
|
},
|
|
source: "asraa/slsa-on-github-test",
|
|
err: serrors.ErrorMismatchSource,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
err := VerifyCertficateSourceRepository(tt.workflow, tt.source)
|
|
if !errCmp(err, tt.err) {
|
|
t.Error(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func asStringPointer(s string) *string {
|
|
return &s
|
|
}
|
|
|
|
func Test_verifyTrustedBuilderID(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
id *string
|
|
path string
|
|
tag string
|
|
defaults map[string]bool
|
|
err error
|
|
byob bool
|
|
}{
|
|
{
|
|
name: "default trusted short tag",
|
|
path: trustedBuilderRepository + generatorGenericSlsa3,
|
|
tag: "v1.2.3",
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
},
|
|
{
|
|
name: "default trusted long tag",
|
|
path: trustedBuilderRepository + generatorGenericSlsa3,
|
|
tag: "refs/tags/v1.2.3",
|
|
defaults: defaultArtifactTrustedReusableWorkflows,
|
|
},
|
|
{
|
|
name: "generic delegator workflow long tag",
|
|
path: trustedBuilderRepository + delegatorGenericSlsa3,
|
|
id: asStringPointer(trustedBuilderRepository + "/.github/workflows/delegator_generic_slsa3.yml@refs/tags/v1.2.3"),
|
|
tag: "refs/tags/v1.2.3",
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
byob: true,
|
|
},
|
|
{
|
|
// This is a BYOB workflow without an id that tests non-compulsory builder-id
|
|
// feature of slsa-verifier and expects byob to be true
|
|
name: "generic delegator workflow no id",
|
|
path: trustedBuilderRepository + delegatorGenericSlsa3,
|
|
// NOTE: id is nil.
|
|
id: nil,
|
|
tag: "refs/tags/v1.2.3",
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
byob: true,
|
|
},
|
|
{
|
|
name: "low perms delegator workflow short tag",
|
|
path: trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml",
|
|
id: asStringPointer(trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml@refs/tags/v1.2.3"),
|
|
tag: "v1.2.3",
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
byob: true,
|
|
},
|
|
{
|
|
// This is a BYOB workflow without an id that tests non-compulsory builder-id
|
|
// feature of slsa-verifier and expects byob to be true
|
|
name: "low perms delegator workflow no ID provided",
|
|
path: trustedBuilderRepository + "/.github/workflows/delegator_lowperms-generic_slsa3.yml",
|
|
// NOTE: id is nil.
|
|
id: nil,
|
|
tag: "v1.2.3",
|
|
defaults: defaultBYOBReusableWorkflows,
|
|
byob: true,
|
|
},
|
|
{
|
|
name: "default mismatch against container defaults long tag",
|
|
path: trustedBuilderRepository + generatorGenericSlsa3,
|
|
tag: "refs/tags/v1.2.3",
|
|
defaults: defaultContainerTrustedReusableWorkflows,
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "valid ID for GitHub builder short tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid ID for GitHub builder long tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@refs/tags/v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid short ID for GitHub builder long tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid long ID for GitHub builder short tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@refs/tags/v1.2.3"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "valid ID for GitHub builder long tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@refs/tags/v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid ID for GitHub builder short tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid short ID for GitHub builder long tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@v1.2.3"),
|
|
},
|
|
{
|
|
name: "valid long ID for GitHub builder short tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/someBuilderID@refs/tags/v1.2.3"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "non GitHub builder ID long builder tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://not-github.com/some/repo/someBuilderID"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "mismatch org GitHub short builder tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "v1.2.3",
|
|
id: asStringPointer("https://github.com/other/repo/someBuilderID"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "mismatch org GitHub long builder tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/other/repo/someBuilderID"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "mismatch name GitHub long builder tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/other/someBuilderID"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
{
|
|
name: "mismatch id GitHub long builder tag",
|
|
path: "some/repo/someBuilderID",
|
|
tag: "refs/tags/v1.2.3",
|
|
id: asStringPointer("https://github.com/some/repo/ID"),
|
|
err: serrors.ErrorUntrustedReusableWorkflow,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
id, byob, err := verifyTrustedBuilderID(httpsGithubCom+tt.path, tt.tag, tt.id, tt.defaults)
|
|
if byob != tt.byob {
|
|
t.Error(cmp.Diff(byob, tt.byob))
|
|
}
|
|
if diff := cmp.Diff(tt.err, err, cmpopts.EquateErrors()); diff != "" {
|
|
t.Fatalf("unexpected error (-want +got):\n%s", diff)
|
|
}
|
|
if tt.err == nil {
|
|
expectedID := httpsGithubCom + tt.path + "@" + tt.tag
|
|
if err := id.MatchesLoose(expectedID, true); err != nil {
|
|
t.Errorf("matches failed:%v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_verifyTrustedBuilderRef(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
callerRepo string
|
|
builderRef string
|
|
expected error
|
|
testingEnabled bool
|
|
}{
|
|
// Trusted repo.
|
|
{
|
|
name: "main not allowed for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/heads/main",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "main allowed for builder w/ testing enabled",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/heads/main",
|
|
testingEnabled: true,
|
|
},
|
|
{
|
|
name: "full semver for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1.2.3",
|
|
},
|
|
{
|
|
name: "no patch semver for other builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1.2",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "no min semver for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with prerelease for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1.2.3-alpha",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1.2.3+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build/prerelease for builder",
|
|
callerRepo: trustedBuilderRepository,
|
|
builderRef: "refs/tags/v1.2.3-alpha+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
// E2e tests repo.
|
|
{
|
|
name: "main not allowed for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/heads/main",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "main allowed for test repo w/ testing enabled",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/heads/main",
|
|
testingEnabled: true,
|
|
},
|
|
|
|
{
|
|
name: "full semver for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1.2.3",
|
|
},
|
|
{
|
|
name: "no patch semver for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1.2",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "no min semver for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with prerelease for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1.2.3-alpha",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1.2.3+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build/prerelease for test repo",
|
|
callerRepo: e2eTestRepository,
|
|
builderRef: "refs/tags/v1.2.3-alpha+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
// Other repos.
|
|
{
|
|
name: "main not allowed for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/heads/main",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "main not allowed for other repos w/ testing enabled",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/heads/main",
|
|
testingEnabled: true,
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3",
|
|
},
|
|
{
|
|
name: "no patch semver for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "no min semver for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with prerelease for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3-alpha",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with prerelease for other repos w/ testing enabled",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3-alpha",
|
|
testingEnabled: true,
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build for other repos w/ testing enabled",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3+123",
|
|
testingEnabled: true,
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build/prerelease for other repos",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3-alpha+123",
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
{
|
|
name: "full semver with build/prerelease for other repos w/ testing enabled",
|
|
callerRepo: "some/repo",
|
|
builderRef: "refs/tags/v1.2.3-alpha+123",
|
|
testingEnabled: true,
|
|
expected: serrors.ErrorInvalidRef,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
wf := WorkflowIdentity{
|
|
SourceRepository: tt.callerRepo,
|
|
}
|
|
|
|
if tt.testingEnabled {
|
|
t.Setenv("SLSA_VERIFIER_TESTING", "1")
|
|
} else {
|
|
// Ensure that the variable is not set.
|
|
t.Setenv("SLSA_VERIFIER_TESTING", "")
|
|
}
|
|
|
|
err := verifyTrustedBuilderRef(&wf, tt.builderRef)
|
|
if !errCmp(err, tt.expected) {
|
|
t.Error(cmp.Diff(err, tt.expected, cmpopts.EquateErrors()))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_GetWorkflowInfoFromCertificate(t *testing.T) {
|
|
t.Parallel()
|
|
// See https://github.com/sigstore/fulcio/blob/e763d76e3f7786b52db4b27ab87dc446da24895a/pkg/certificate/extensions.go.
|
|
trigger := "workflow_dispatch"
|
|
encodedTrigger, err := asn1.MarshalWithParams(trigger, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
repo := "org/repo"
|
|
encodedRepoURI, err := asn1.MarshalWithParams(httpsGithubCom+repo, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
issuer := "the-issuer"
|
|
encodedIssuer, err := asn1.MarshalWithParams(issuer, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
digest := "abcdef"
|
|
encodedDigest, err := asn1.MarshalWithParams(digest, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
encodedHosted, err := asn1.MarshalWithParams("github-hosted", "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
hosted := HostedGitHub
|
|
ref := "refs/tags/v1.2.3"
|
|
encodedRef, err := asn1.MarshalWithParams(ref, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
sourceID := "12345"
|
|
encodedSourceID, err := asn1.MarshalWithParams(sourceID, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
sourceOwnerID := "12345"
|
|
encodedSourceOwnerID, err := asn1.MarshalWithParams(sourceOwnerID, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
|
|
buildConfigSha1 := "abcdef"
|
|
encodedBuildConfigSha1, err := asn1.MarshalWithParams(buildConfigSha1, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
buildConfigPath := "path/to/workflow"
|
|
encodedBuildConfigURI, err := asn1.MarshalWithParams(httpsGithubCom+repo+"/"+buildConfigPath+"@"+ref, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
|
|
invocationID := "9207262"
|
|
encodedInvocationURI, err := asn1.MarshalWithParams(httpsGithubCom+repo+"/actions/runs/"+invocationID, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
subjectSha1 := "subjectSha1"
|
|
encodedSubjectSha1, err := asn1.MarshalWithParams(subjectSha1, "utf8")
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
cert x509.Certificate
|
|
workflow WorkflowIdentity
|
|
err error
|
|
}{
|
|
{
|
|
name: "old cert",
|
|
cert: x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "github.com",
|
|
Path: "/" + repo + "/" + buildConfigPath,
|
|
},
|
|
},
|
|
Extensions: []pkix.Extension{
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDIssuer,
|
|
Value: []byte(issuer),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowTrigger,
|
|
Value: []byte(trigger),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowSHA,
|
|
Value: []byte(digest),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowRepository,
|
|
Value: []byte(repo),
|
|
},
|
|
},
|
|
},
|
|
workflow: WorkflowIdentity{
|
|
Issuer: issuer,
|
|
SubjectWorkflow: Must(url.Parse(httpsGithubCom + repo + "/" + buildConfigPath)),
|
|
SourceRepository: repo,
|
|
SourceSha1: digest,
|
|
BuildTrigger: trigger,
|
|
},
|
|
},
|
|
{
|
|
name: "old cert empty URIs",
|
|
cert: x509.Certificate{
|
|
Extensions: []pkix.Extension{
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDIssuer,
|
|
Value: []byte(issuer),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowTrigger,
|
|
Value: []byte(trigger),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowSHA,
|
|
Value: []byte(digest),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowRepository,
|
|
Value: []byte(repo),
|
|
},
|
|
},
|
|
},
|
|
err: serrors.ErrorInvalidFormat,
|
|
},
|
|
{
|
|
name: "new cert",
|
|
cert: x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "github.com",
|
|
Path: "/" + repo + "/" + buildConfigPath,
|
|
},
|
|
},
|
|
Extensions: []pkix.Extension{
|
|
// Deprecated claims.
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDIssuer,
|
|
Value: []byte(issuer),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowTrigger,
|
|
Value: []byte(trigger),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowSHA,
|
|
Value: []byte(digest),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowRepository,
|
|
Value: []byte(repo),
|
|
},
|
|
// New claims.
|
|
{
|
|
Id: fulcio.OIDBuildTrigger,
|
|
Value: encodedTrigger,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryURI,
|
|
Value: encodedRepoURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDIssuerV2,
|
|
Value: encodedIssuer,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryDigest,
|
|
Value: encodedDigest,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunnerEnvironment,
|
|
Value: encodedHosted,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryRef,
|
|
Value: encodedRef,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryIdentifier,
|
|
Value: encodedSourceID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryOwnerIdentifier,
|
|
Value: encodedSourceOwnerID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigDigest,
|
|
Value: encodedBuildConfigSha1,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigURI,
|
|
Value: encodedBuildConfigURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunInvocationURI,
|
|
Value: encodedInvocationURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildSignerDigest,
|
|
Value: encodedSubjectSha1,
|
|
},
|
|
},
|
|
},
|
|
workflow: WorkflowIdentity{
|
|
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,
|
|
},
|
|
},
|
|
{
|
|
name: "new cert empty URIs",
|
|
cert: x509.Certificate{
|
|
Extensions: []pkix.Extension{
|
|
// Deprecated claims.
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDIssuer,
|
|
Value: []byte(issuer),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowTrigger,
|
|
Value: []byte(trigger),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowSHA,
|
|
Value: []byte(digest),
|
|
},
|
|
{
|
|
//nolint: staticcheck // SA1019: Need to support older signatures.
|
|
Id: fulcio.OIDGitHubWorkflowRepository,
|
|
Value: []byte(repo),
|
|
},
|
|
// New claims.
|
|
{
|
|
Id: fulcio.OIDBuildTrigger,
|
|
Value: encodedTrigger,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryURI,
|
|
Value: encodedRepoURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDIssuerV2,
|
|
Value: encodedIssuer,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryDigest,
|
|
Value: encodedDigest,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunnerEnvironment,
|
|
Value: encodedHosted,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryRef,
|
|
Value: encodedRef,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryIdentifier,
|
|
Value: encodedSourceID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryOwnerIdentifier,
|
|
Value: encodedSourceOwnerID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigDigest,
|
|
Value: encodedBuildConfigSha1,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigURI,
|
|
Value: encodedBuildConfigURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunInvocationURI,
|
|
Value: encodedInvocationURI,
|
|
},
|
|
},
|
|
},
|
|
err: serrors.ErrorInvalidFormat,
|
|
},
|
|
{
|
|
name: "new cert no deprecated claims",
|
|
cert: x509.Certificate{
|
|
URIs: []*url.URL{
|
|
{
|
|
Scheme: "https",
|
|
Host: "github.com",
|
|
Path: "/" + repo + "/" + buildConfigPath,
|
|
},
|
|
},
|
|
Extensions: []pkix.Extension{
|
|
// New claims.
|
|
{
|
|
Id: fulcio.OIDBuildTrigger,
|
|
Value: encodedTrigger,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryURI,
|
|
Value: encodedRepoURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDIssuerV2,
|
|
Value: encodedIssuer,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryDigest,
|
|
Value: encodedDigest,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunnerEnvironment,
|
|
Value: encodedHosted,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryRef,
|
|
Value: encodedRef,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryIdentifier,
|
|
Value: encodedSourceID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryOwnerIdentifier,
|
|
Value: encodedSourceOwnerID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigDigest,
|
|
Value: encodedBuildConfigSha1,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigURI,
|
|
Value: encodedBuildConfigURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunInvocationURI,
|
|
Value: encodedInvocationURI,
|
|
},
|
|
},
|
|
},
|
|
workflow: WorkflowIdentity{
|
|
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,
|
|
},
|
|
},
|
|
{
|
|
name: "new cert no deprecated claims empty URIs",
|
|
cert: x509.Certificate{
|
|
Extensions: []pkix.Extension{
|
|
// New claims.
|
|
{
|
|
Id: fulcio.OIDBuildTrigger,
|
|
Value: encodedTrigger,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryURI,
|
|
Value: encodedRepoURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDIssuerV2,
|
|
Value: encodedIssuer,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryDigest,
|
|
Value: encodedDigest,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunnerEnvironment,
|
|
Value: encodedHosted,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryRef,
|
|
Value: encodedRef,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryIdentifier,
|
|
Value: encodedSourceID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDSourceRepositoryOwnerIdentifier,
|
|
Value: encodedSourceOwnerID,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigDigest,
|
|
Value: encodedBuildConfigSha1,
|
|
},
|
|
{
|
|
Id: fulcio.OIDBuildConfigURI,
|
|
Value: encodedBuildConfigURI,
|
|
},
|
|
{
|
|
Id: fulcio.OIDRunInvocationURI,
|
|
Value: encodedInvocationURI,
|
|
},
|
|
},
|
|
},
|
|
err: serrors.ErrorInvalidFormat,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
workflow, err := GetWorkflowInfoFromCertificate(&tt.cert)
|
|
if !errCmp(err, tt.err) {
|
|
t.Error(cmp.Diff(err, tt.err, cmpopts.EquateErrors()))
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !cmp.Equal(*workflow, tt.workflow) {
|
|
t.Error(cmp.Diff(*workflow, tt.workflow))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|