319 lines
9.9 KiB
Go
319 lines
9.9 KiB
Go
package verify
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewEnforcementCriteria(t *testing.T) {
|
|
artifactPath := "../test/data/sigstore-js-2.1.0.tgz"
|
|
|
|
t.Run("sets SANRegex and SAN using SANRegex and SAN", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
SAN: "https://github/foo/bar/.github/workflows/attest.yml",
|
|
SANRegex: "(?i)^https://github/foo",
|
|
SignerRepo: "wrong/value",
|
|
SignerWorkflow: "wrong/value/.github/workflows/attest.yml",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://github/foo/bar/.github/workflows/attest.yml", c.SAN)
|
|
require.Equal(t, "(?i)^https://github/foo", c.SANRegex)
|
|
})
|
|
|
|
t.Run("sets SANRegex using SignerRepo", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SignerRepo: "foo/bar",
|
|
SignerWorkflow: "wrong/value/.github/workflows/attest.yml",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "(?i)^https://github.com/foo/bar/", c.SANRegex)
|
|
require.Zero(t, c.SAN)
|
|
})
|
|
|
|
t.Run("sets SANRegex using SignerRepo and Tenant", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SignerRepo: "foo/bar",
|
|
SignerWorkflow: "wrong/value/.github/workflows/attest.yml",
|
|
Tenant: "baz",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "(?i)^https://baz.ghe.com/foo/bar/", c.SANRegex)
|
|
require.Zero(t, c.SAN)
|
|
})
|
|
|
|
t.Run("sets SANRegex using SignerWorkflow matching host regex", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SignerWorkflow: "foo/bar/.github/workflows/attest.yml",
|
|
Hostname: "github.com",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "^https://github.com/foo/bar/.github/workflows/attest.yml", c.SANRegex)
|
|
require.Zero(t, c.SAN)
|
|
})
|
|
|
|
t.Run("sets SANRegex using opts.Repo", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "foo/bar",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "(?i)^https://github.com/foo/bar/", c.SANRegex)
|
|
})
|
|
|
|
t.Run("sets SANRegex using opts.Owner", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "(?i)^https://github.com/foo/", c.SANRegex)
|
|
})
|
|
|
|
t.Run("sets Extensions.RunnerEnvironment to GitHubRunner value if opts.DenySelfHostedRunner is true", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
DenySelfHostedRunner: true,
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, verification.GitHubRunner, c.Certificate.RunnerEnvironment)
|
|
})
|
|
|
|
t.Run("sets Extensions.RunnerEnvironment to * value if opts.DenySelfHostedRunner is false", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
DenySelfHostedRunner: false,
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Zero(t, c.Certificate.RunnerEnvironment)
|
|
})
|
|
|
|
t.Run("sets Extensions.SourceRepositoryURI using opts.Repo and opts.Tenant", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
Tenant: "baz",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://baz.ghe.com/foo/bar", c.Certificate.SourceRepositoryURI)
|
|
})
|
|
|
|
t.Run("sets Extensions.SourceRepositoryURI using opts.Repo", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://github.com/foo/bar", c.Certificate.SourceRepositoryURI)
|
|
})
|
|
|
|
t.Run("sets SANRegex and SAN using SANRegex and SAN, sets Extensions.SourceRepositoryURI using opts.Repo", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "baz",
|
|
Repo: "baz/xyz",
|
|
SAN: "https://github/foo/bar/.github/workflows/attest.yml",
|
|
SANRegex: "(?i)^https://github/foo",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://github/foo/bar/.github/workflows/attest.yml", c.SAN)
|
|
require.Equal(t, "(?i)^https://github/foo", c.SANRegex)
|
|
require.Equal(t, "https://github.com/baz/xyz", c.Certificate.SourceRepositoryURI)
|
|
})
|
|
|
|
t.Run("sets Extensions.SourceRepositoryOwnerURI using opts.Owner and opts.Tenant", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
Tenant: "baz",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://baz.ghe.com/foo", c.Certificate.SourceRepositoryOwnerURI)
|
|
})
|
|
|
|
t.Run("sets Extensions.SourceRepositoryOwnerURI using opts.Owner", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://github.com/foo", c.Certificate.SourceRepositoryOwnerURI)
|
|
})
|
|
|
|
t.Run("sets OIDCIssuer using opts.Tenant", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
Tenant: "baz",
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://token.actions.baz.ghe.com", c.Certificate.Issuer)
|
|
})
|
|
|
|
t.Run("sets OIDCIssuer using opts.OIDCIssuer", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "foo",
|
|
Repo: "foo/bar",
|
|
OIDCIssuer: "https://foo.com",
|
|
Tenant: "baz",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "https://foo.com", c.Certificate.Issuer)
|
|
})
|
|
|
|
t.Run("sets Certificate.BuildSignerDigest using opts.SignerDigest", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SignerDigest: "foo",
|
|
Hostname: "github.com",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "foo", c.Certificate.BuildSignerDigest)
|
|
})
|
|
|
|
t.Run("sets Certificate.SourceRepositoryDigest using opts.SourceDigest", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SourceDigest: "foo",
|
|
Hostname: "github.com",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "foo", c.Certificate.SourceRepositoryDigest)
|
|
})
|
|
|
|
t.Run("sets Certificate.SourceRepositoryRef using opts.SourceRef", func(t *testing.T) {
|
|
opts := &Options{
|
|
ArtifactPath: artifactPath,
|
|
Owner: "wrong",
|
|
Repo: "wrong/value",
|
|
SourceRef: "refs/heads/main",
|
|
Hostname: "github.com",
|
|
}
|
|
|
|
c, err := newEnforcementCriteria(opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "refs/heads/main", c.Certificate.SourceRepositoryRef)
|
|
})
|
|
}
|
|
|
|
func TestValidateSignerWorkflow(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
providedSignerWorkflow string
|
|
expectedWorkflowRegex string
|
|
host string
|
|
expectErr bool
|
|
errContains string
|
|
}
|
|
|
|
testcases := []testcase{
|
|
{
|
|
name: "workflow with no host specified",
|
|
providedSignerWorkflow: "github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
expectErr: true,
|
|
errContains: "unknown signer workflow host",
|
|
},
|
|
{
|
|
name: "workflow with default host",
|
|
providedSignerWorkflow: "github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
expectedWorkflowRegex: "^https://github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
host: "github.com",
|
|
},
|
|
{
|
|
name: "workflow with workflow URL included",
|
|
providedSignerWorkflow: "github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
expectedWorkflowRegex: "^https://github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
host: "github.com",
|
|
},
|
|
{
|
|
name: "workflow with GH_HOST set",
|
|
providedSignerWorkflow: "github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
expectedWorkflowRegex: "^https://myhost.github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
host: "myhost.github.com",
|
|
},
|
|
{
|
|
name: "workflow with authenticated host",
|
|
providedSignerWorkflow: "github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
expectedWorkflowRegex: "^https://authedhost.github.com/github/artifact-attestations-workflows/.github/workflows/attest.yml",
|
|
host: "authedhost.github.com",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
// All host resolution is done verify.go:RunE
|
|
workflowRegex, err := validateSignerWorkflow(tc.host, tc.providedSignerWorkflow)
|
|
require.Equal(t, tc.expectedWorkflowRegex, workflowRegex)
|
|
|
|
if tc.expectErr {
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, tc.errContains)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expectedWorkflowRegex, workflowRegex)
|
|
}
|
|
}
|
|
}
|