Merge pull request #9392 from cli/eugene/gh-attestation-case-insensitivity
handle attest case insensitivity
This commit is contained in:
commit
89cbcfe7eb
8 changed files with 120 additions and 30 deletions
28
pkg/cmd/attestation/verification/extensions.go
Normal file
28
pkg/cmd/attestation/verification/extensions.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package verification
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func VerifyCertExtensions(results []*AttestationProcessingResult, owner string, repo string) error {
|
||||
for _, attestation := range results {
|
||||
// TODO: handle proxima prefix
|
||||
expectedSourceRepositoryOwnerURI := fmt.Sprintf("https://github.com/%s", owner)
|
||||
sourceRepositoryOwnerURI := attestation.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryOwnerURI
|
||||
if !strings.EqualFold(expectedSourceRepositoryOwnerURI, sourceRepositoryOwnerURI) {
|
||||
return fmt.Errorf("expected SourceRepositoryOwnerURI to be %s, got %s", expectedSourceRepositoryOwnerURI, sourceRepositoryOwnerURI)
|
||||
}
|
||||
|
||||
// if repo is set, check the SourceRepositoryURI field
|
||||
if repo != "" {
|
||||
// TODO: handle proxima prefix
|
||||
expectedSourceRepositoryURI := fmt.Sprintf("https://github.com/%s", repo)
|
||||
sourceRepositoryURI := attestation.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryURI
|
||||
if !strings.EqualFold(expectedSourceRepositoryURI, sourceRepositoryURI) {
|
||||
return fmt.Errorf("expected SourceRepositoryURI to be %s, got %s", expectedSourceRepositoryURI, sourceRepositoryURI)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
46
pkg/cmd/attestation/verification/extensions_test.go
Normal file
46
pkg/cmd/attestation/verification/extensions_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package verification
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sigstore/sigstore-go/pkg/fulcio/certificate"
|
||||
"github.com/sigstore/sigstore-go/pkg/verify"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestVerifyCertExtensions(t *testing.T) {
|
||||
results := []*AttestationProcessingResult{
|
||||
{
|
||||
VerificationResult: &verify.VerificationResult{
|
||||
Signature: &verify.SignatureVerificationResult{
|
||||
Certificate: &certificate.Summary{
|
||||
Extensions: certificate.Extensions{
|
||||
SourceRepositoryOwnerURI: "https://github.com/owner",
|
||||
SourceRepositoryURI: "https://github.com/owner/repo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("VerifyCertExtensions with owner and repo", func(t *testing.T) {
|
||||
err := VerifyCertExtensions(results, "owner", "owner/repo")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("VerifyCertExtensions with owner", func(t *testing.T) {
|
||||
err := VerifyCertExtensions(results, "owner", "")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("VerifyCertExtensions with wrong owner", func(t *testing.T) {
|
||||
err := VerifyCertExtensions(results, "wrong", "")
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryOwnerURI to be https://github.com/wrong, got https://github.com/owner")
|
||||
})
|
||||
|
||||
t.Run("VerifyCertExtensions with wrong repo", func(t *testing.T) {
|
||||
err := VerifyCertExtensions(results, "owner", "wrong")
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/wrong, got https://github.com/owner/repo")
|
||||
})
|
||||
}
|
||||
|
|
@ -31,7 +31,9 @@ func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve
|
|||
Signature: &verify.SignatureVerificationResult{
|
||||
Certificate: &certificate.Summary{
|
||||
Extensions: certificate.Extensions{
|
||||
BuildSignerURI: "https://github.com/github/example/.github/workflows/release.yml@refs/heads/main",
|
||||
BuildSignerURI: "https://github.com/github/example/.github/workflows/release.yml@refs/heads/main",
|
||||
SourceRepositoryOwnerURI: "https://github.com/sigstore",
|
||||
SourceRepositoryURI: "https://github.com/sigstore/sigstore-js",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func TestSetPolicyFlags(t *testing.T) {
|
|||
opts.SetPolicyFlags()
|
||||
require.Equal(t, "sigstore", opts.Owner)
|
||||
require.Equal(t, "sigstore/sigstore-js", opts.Repo)
|
||||
require.Equal(t, "^https://github.com/sigstore/sigstore-js/", opts.SANRegex)
|
||||
require.Equal(t, "(?i)^https://github.com/sigstore/sigstore-js/", opts.SANRegex)
|
||||
})
|
||||
|
||||
t.Run("does not set SANRegex when SANRegex and Repo are provided", func(t *testing.T) {
|
||||
|
|
@ -99,7 +99,7 @@ func TestSetPolicyFlags(t *testing.T) {
|
|||
|
||||
opts.SetPolicyFlags()
|
||||
require.Equal(t, "sigstore", opts.Owner)
|
||||
require.Equal(t, "^https://github.com/sigstore/", opts.SANRegex)
|
||||
require.Equal(t, "(?i)^https://github.com/sigstore/", opts.SANRegex)
|
||||
})
|
||||
|
||||
t.Run("does not set SANRegex when SANRegex and Owner are provided", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ const (
|
|||
)
|
||||
|
||||
func expandToGitHubURL(ownerOrRepo string) string {
|
||||
return fmt.Sprintf("^https://github.com/%s/", ownerOrRepo)
|
||||
// TODO: handle proxima prefix
|
||||
return fmt.Sprintf("(?i)^https://github.com/%s/", ownerOrRepo)
|
||||
}
|
||||
|
||||
func buildSANMatcher(opts *Options) (verify.SubjectAlternativeNameMatcher, error) {
|
||||
|
|
@ -42,19 +43,6 @@ func buildSANMatcher(opts *Options) (verify.SubjectAlternativeNameMatcher, error
|
|||
return verify.SubjectAlternativeNameMatcher{}, nil
|
||||
}
|
||||
|
||||
func buildCertExtensions(opts *Options, runnerEnv string) certificate.Extensions {
|
||||
extensions := certificate.Extensions{
|
||||
SourceRepositoryOwnerURI: fmt.Sprintf("https://github.com/%s", opts.Owner),
|
||||
RunnerEnvironment: runnerEnv,
|
||||
}
|
||||
|
||||
// if opts.Repo is set, set the SourceRepositoryURI field before returning the extensions
|
||||
if opts.Repo != "" {
|
||||
extensions.SourceRepositoryURI = fmt.Sprintf("https://github.com/%s", opts.Repo)
|
||||
}
|
||||
return extensions
|
||||
}
|
||||
|
||||
func buildCertificateIdentityOption(opts *Options, runnerEnv string) (verify.PolicyOption, error) {
|
||||
sanMatcher, err := buildSANMatcher(opts)
|
||||
if err != nil {
|
||||
|
|
@ -66,7 +54,9 @@ func buildCertificateIdentityOption(opts *Options, runnerEnv string) (verify.Pol
|
|||
return nil, err
|
||||
}
|
||||
|
||||
extensions := buildCertExtensions(opts, runnerEnv)
|
||||
extensions := certificate.Extensions{
|
||||
RunnerEnvironment: runnerEnv,
|
||||
}
|
||||
|
||||
certId, err := verify.NewCertificateIdentity(sanMatcher, issuerMatcher, extensions)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -235,6 +235,12 @@ func runVerify(opts *Options) error {
|
|||
return sigstoreRes.Error
|
||||
}
|
||||
|
||||
// Verify extensions
|
||||
if err := verification.VerifyCertExtensions(sigstoreRes.VerifyResults, opts.Owner, opts.Repo); err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
return err
|
||||
}
|
||||
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Green("✓ Verification succeeded!\n"))
|
||||
|
||||
// If an exporter is provided with the --json flag, write the results to the terminal in JSON format
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
|
||||
err := runVerify(&opts)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "verifying with issuer \"sigstore.dev\": failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SourceRepositoryURI to be \"https://github.com/sigstore/fakerepo\", got \"https://github.com/sigstore/sigstore-js\"")
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/sigstore/fakerepo, got https://github.com/sigstore/sigstore-js")
|
||||
})
|
||||
|
||||
t.Run("with invalid owner", func(t *testing.T) {
|
||||
|
|
@ -69,7 +69,7 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
|
||||
err := runVerify(&opts)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "verifying with issuer \"sigstore.dev\": failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SourceRepositoryOwnerURI to be \"https://github.com/fakeowner\", got \"https://github.com/sigstore\"")
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryOwnerURI to be https://github.com/fakeowner, got https://github.com/sigstore")
|
||||
})
|
||||
|
||||
t.Run("with invalid owner and invalid repo", func(t *testing.T) {
|
||||
|
|
@ -78,7 +78,7 @@ func TestVerifyIntegration(t *testing.T) {
|
|||
|
||||
err := runVerify(&opts)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "verifying with issuer \"sigstore.dev\": failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SourceRepositoryURI to be \"https://github.com/fakeowner/fakerepo\"")
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/fakeowner/fakerepo, got https://github.com/sigstore/sigstore-js")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
Limit: 30,
|
||||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: false,
|
||||
|
|
@ -91,7 +91,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
Limit: 30,
|
||||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: false,
|
||||
|
|
@ -105,7 +105,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 30,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: true,
|
||||
|
|
@ -133,7 +133,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
Limit: 30,
|
||||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: false,
|
||||
|
|
@ -147,7 +147,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 101,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: false,
|
||||
|
|
@ -161,7 +161,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
Limit: 0,
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: true,
|
||||
|
|
@ -176,7 +176,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
SAN: "https://github.com/sigstore/",
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsErr: true,
|
||||
|
|
@ -191,7 +191,7 @@ func TestNewVerifyCmd(t *testing.T) {
|
|||
Limit: 30,
|
||||
OIDCIssuer: GitHubOIDCIssuer,
|
||||
Owner: "sigstore",
|
||||
SANRegex: "^https://github.com/sigstore/",
|
||||
SANRegex: "(?i)^https://github.com/sigstore/",
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
},
|
||||
wantsExporter: true,
|
||||
|
|
@ -340,14 +340,32 @@ func TestRunVerify(t *testing.T) {
|
|||
require.Nil(t, runVerify(&opts))
|
||||
})
|
||||
|
||||
t.Run("with owner which not matches SourceRepositoryOwnerURI", func(t *testing.T) {
|
||||
opts := publicGoodOpts
|
||||
opts.BundlePath = ""
|
||||
opts.Owner = "owner"
|
||||
|
||||
err := runVerify(&opts)
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryOwnerURI to be https://github.com/owner, got https://github.com/sigstore")
|
||||
})
|
||||
|
||||
t.Run("with repo", func(t *testing.T) {
|
||||
opts := publicGoodOpts
|
||||
opts.BundlePath = ""
|
||||
opts.Repo = "github/example"
|
||||
opts.Repo = "sigstore/sigstore-js"
|
||||
|
||||
require.Nil(t, runVerify(&opts))
|
||||
})
|
||||
|
||||
t.Run("with repo which not matches SourceRepositoryURI", func(t *testing.T) {
|
||||
opts := publicGoodOpts
|
||||
opts.BundlePath = ""
|
||||
opts.Repo = "wrong/example"
|
||||
|
||||
err := runVerify(&opts)
|
||||
require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/wrong/example, got https://github.com/sigstore/sigstore-js")
|
||||
})
|
||||
|
||||
t.Run("with invalid repo", func(t *testing.T) {
|
||||
opts := publicGoodOpts
|
||||
opts.BundlePath = ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue