Merge pull request #9392 from cli/eugene/gh-attestation-case-insensitivity

handle attest case insensitivity
This commit is contained in:
Eugene 2024-07-31 15:19:49 -07:00 committed by GitHub
commit 89cbcfe7eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 120 additions and 30 deletions

View 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
}

View 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")
})
}

View file

@ -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",
},
},
},

View file

@ -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) {

View file

@ -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 {

View file

@ -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

View file

@ -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")
})
}

View file

@ -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 = ""