diff --git a/pkg/cmd/attestation/verification/sigstore.go b/pkg/cmd/attestation/verification/sigstore.go index 95dc2fb9c..e76d55a6b 100644 --- a/pkg/cmd/attestation/verification/sigstore.go +++ b/pkg/cmd/attestation/verification/sigstore.go @@ -63,27 +63,39 @@ func NewLiveSigstoreVerifier(config SigstoreConfig) (*LiveSigstoreVerifier, erro Logger: config.Logger, NoPublicGood: config.NoPublicGood, } - // if a custom trusted root is set, configure custom verifiers + // if a custom trusted root is set, configure custom verifiers and assume no Public Good or GitHub verifiers + // are needed if config.TrustedRoot != "" { customVerifiers, err := createCustomVerifiers(config.TrustedRoot, config.NoPublicGood) if err != nil { - return nil, err + return nil, fmt.Errorf("error creating custom verifiers: %s", err) } liveVerifier.Custom = customVerifiers return liveVerifier, nil } + + // No custom trusted root is set, so configure Public Good and GitHub verifiers if !config.NoPublicGood { publicGoodVerifier, err := newPublicGoodVerifier(config.TUFMetadataDir, config.HttpClient) if err != nil { - return nil, err + // Log warning but continue - PGI unavailability should not block GitHub attestation verification + config.Logger.VerbosePrintf("Warning: failed to initialize Sigstore Public Good verifier: %v\n", err) + config.Logger.VerbosePrintf("Continuing without Public Good Instance verification\n") + } else { + liveVerifier.PublicGood = publicGoodVerifier } - liveVerifier.PublicGood = publicGoodVerifier } + github, err := newGitHubVerifier(config.TrustDomain, config.TUFMetadataDir, config.HttpClient) if err != nil { - return nil, err + config.Logger.VerbosePrintf("Warning: failed to initialize GitHub verifier: %v\n", err) + } else { + liveVerifier.GitHub = github + } + + if liveVerifier.noVerifierSet() { + return nil, fmt.Errorf("no valid Sigstore verifiers could be initialized") } - liveVerifier.GitHub = github return liveVerifier, nil } @@ -206,6 +218,9 @@ func (v *LiveSigstoreVerifier) chooseVerifier(issuer string) (*verify.Verifier, if v.NoPublicGood { return nil, fmt.Errorf("detected public good instance but requested verification without public good instance") } + if v.PublicGood == nil { + return nil, fmt.Errorf("public good verifier is not available (initialization may have failed)") + } return v.PublicGood, nil case GitHubIssuerOrg: return v.GitHub, nil @@ -372,3 +387,7 @@ func newPublicGoodVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verif return sv, nil } + +func (v *LiveSigstoreVerifier) noVerifierSet() bool { + return v.PublicGood == nil && v.GitHub == nil && len(v.Custom) == 0 +} diff --git a/pkg/cmd/attestation/verification/sigstore_test.go b/pkg/cmd/attestation/verification/sigstore_test.go new file mode 100644 index 000000000..1f7c925ba --- /dev/null +++ b/pkg/cmd/attestation/verification/sigstore_test.go @@ -0,0 +1,53 @@ +package verification + +import ( + "testing" + + "github.com/cli/cli/v2/pkg/cmd/attestation/io" + "github.com/stretchr/testify/require" +) + +// Note: Tests that require network access and TUF client initialization +// are in sigstore_integration_test.go with the //go:build integration tag. +// These unit tests focus on testing the logic without requiring network access. + +// TestChooseVerifierWithNilPublicGood tests that chooseVerifier returns an error +// when a PGI attestation is encountered but the PGI verifier is nil (failed initialization). +func TestChooseVerifierWithNilPublicGood(t *testing.T) { + verifier := &LiveSigstoreVerifier{ + Logger: io.NewTestHandler(), + NoPublicGood: false, + PublicGood: nil, // Simulate failed PGI initialization + GitHub: nil, // Not needed for this test + } + + _, err := verifier.chooseVerifier(PublicGoodIssuerOrg) + + require.Error(t, err) + require.ErrorContains(t, err, "public good verifier is not available") +} + +// TestChooseVerifierUnrecognizedIssuer tests that an error is returned +// for unrecognized issuers. +func TestChooseVerifierUnrecognizedIssuer(t *testing.T) { + verifier := &LiveSigstoreVerifier{ + Logger: io.NewTestHandler(), + NoPublicGood: false, + } + + _, err := verifier.chooseVerifier("unknown-issuer") + + require.Error(t, err) + require.ErrorContains(t, err, "leaf certificate issuer is not recognized") +} + +func TestLiveSigstoreVerifier_noVerifierSet(t *testing.T) { + verifier := &LiveSigstoreVerifier{ + Logger: io.NewTestHandler(), + NoPublicGood: true, + PublicGood: nil, + GitHub: nil, + } + + require.True(t, verifier.noVerifierSet()) +}