535 lines
18 KiB
Go
535 lines
18 KiB
Go
package verify
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/artifact/oci"
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/test"
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
|
"github.com/cli/cli/v2/pkg/cmdutil"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/cli/cli/v2/pkg/httpmock"
|
|
"github.com/cli/cli/v2/pkg/iostreams"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
SigstoreSanValue = "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main"
|
|
SigstoreSanRegex = "^https://github.com/sigstore/sigstore-js/"
|
|
)
|
|
|
|
var (
|
|
artifactPath = test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz")
|
|
bundlePath = test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json")
|
|
)
|
|
|
|
func TestNewVerifyCmd(t *testing.T) {
|
|
testIO, _, _, _ := iostreams.Test()
|
|
var testReg httpmock.Registry
|
|
var metaResp = api.MetaResponse{
|
|
Domains: api.Domain{
|
|
ArtifactAttestations: api.ArtifactAttestations{
|
|
TrustDomain: "foo",
|
|
},
|
|
},
|
|
}
|
|
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
|
|
httpmock.StatusJSONResponse(200, &metaResp))
|
|
|
|
f := &cmdutil.Factory{
|
|
IOStreams: testIO,
|
|
HttpClient: func() (*http.Client, error) {
|
|
reg := &testReg
|
|
client := &http.Client{}
|
|
httpmock.ReplaceTripper(client, reg)
|
|
return client, nil
|
|
},
|
|
}
|
|
|
|
testcases := []struct {
|
|
name string
|
|
cli string
|
|
wants Options
|
|
wantsErr bool
|
|
wantsExporter bool
|
|
}{
|
|
{
|
|
name: "Invalid digest-alg flag",
|
|
cli: fmt.Sprintf("%s --bundle %s --digest-alg sha384 --owner sigstore", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
|
DigestAlgorithm: "sha384",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Use default digest-alg value",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: false,
|
|
},
|
|
{
|
|
name: "Custom host",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --hostname foo.ghe.com", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "foo.ghe.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: false,
|
|
},
|
|
{
|
|
name: "Invalid custom host",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --hostname foo.bar.com", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "foo.ghe.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Use custom digest-alg value",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --digest-alg sha512", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
BundlePath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json"),
|
|
DigestAlgorithm: "sha512",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: false,
|
|
},
|
|
{
|
|
name: "Missing owner and repo flags",
|
|
cli: artifactPath,
|
|
wants: Options{
|
|
ArtifactPath: test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0.tgz"),
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SANRegex: "(?i)^https://github.com/sigstore/",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Has both owner and repo flags",
|
|
cli: fmt.Sprintf("%s --owner sigstore --repo sigstore/sigstore-js", artifactPath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
Repo: "sigstore/sigstore-js",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Uses default limit flag",
|
|
cli: fmt.Sprintf("%s --owner sigstore", artifactPath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: false,
|
|
},
|
|
{
|
|
name: "Uses custom limit flag",
|
|
cli: fmt.Sprintf("%s --owner sigstore --limit 101", artifactPath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 101,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: false,
|
|
},
|
|
{
|
|
name: "Uses invalid limit flag",
|
|
cli: fmt.Sprintf("%s --owner sigstore --limit 0", artifactPath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 0,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SANRegex: "(?i)^https://github.com/sigstore/",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Has both cert-identity and cert-identity-regex flags",
|
|
cli: fmt.Sprintf("%s --owner sigstore --cert-identity https://github.com/sigstore/ --cert-identity-regex ^https://github.com/sigstore/", artifactPath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SAN: "https://github.com/sigstore/",
|
|
SANRegex: "(?i)^https://github.com/sigstore/",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsErr: true,
|
|
},
|
|
{
|
|
name: "Prints output in JSON format",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --format json", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
BundlePath: bundlePath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsExporter: true,
|
|
},
|
|
{
|
|
name: "Use specified predicate type",
|
|
cli: fmt.Sprintf("%s --bundle %s --owner sigstore --predicate-type https://spdx.dev/Document/v2.3 --format json", artifactPath, bundlePath),
|
|
wants: Options{
|
|
ArtifactPath: artifactPath,
|
|
BundlePath: bundlePath,
|
|
DigestAlgorithm: "sha256",
|
|
Hostname: "github.com",
|
|
Limit: 30,
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: "https://spdx.dev/Document/v2.3",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
},
|
|
wantsExporter: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var opts *Options
|
|
cmd := NewVerifyCmd(f, func(o *Options) error {
|
|
opts = o
|
|
return nil
|
|
})
|
|
|
|
argv := strings.Split(tc.cli, " ")
|
|
cmd.SetArgs(argv)
|
|
cmd.SetIn(&bytes.Buffer{})
|
|
cmd.SetOut(&bytes.Buffer{})
|
|
cmd.SetErr(&bytes.Buffer{})
|
|
_, err := cmd.ExecuteC()
|
|
if tc.wantsErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.wants.ArtifactPath, opts.ArtifactPath)
|
|
assert.Equal(t, tc.wants.BundlePath, opts.BundlePath)
|
|
assert.Equal(t, tc.wants.DenySelfHostedRunner, opts.DenySelfHostedRunner)
|
|
assert.Equal(t, tc.wants.DigestAlgorithm, opts.DigestAlgorithm)
|
|
assert.Equal(t, tc.wants.Hostname, opts.Hostname)
|
|
assert.Equal(t, tc.wants.Limit, opts.Limit)
|
|
assert.Equal(t, tc.wants.NoPublicGood, opts.NoPublicGood)
|
|
assert.Equal(t, tc.wants.OIDCIssuer, opts.OIDCIssuer)
|
|
assert.Equal(t, tc.wants.Owner, opts.Owner)
|
|
assert.Equal(t, tc.wants.PredicateType, opts.PredicateType)
|
|
assert.Equal(t, tc.wants.Repo, opts.Repo)
|
|
assert.Equal(t, tc.wants.SAN, opts.SAN)
|
|
assert.Equal(t, tc.wants.SANRegex, opts.SANRegex)
|
|
assert.Equal(t, tc.wants.TrustedRoot, opts.TrustedRoot)
|
|
assert.NotNil(t, opts.APIClient)
|
|
assert.NotNil(t, opts.Logger)
|
|
assert.NotNil(t, opts.OCIClient)
|
|
assert.Equal(t, tc.wantsExporter, opts.exporter != nil)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVerifyCmdAuthChecks(t *testing.T) {
|
|
f := &cmdutil.Factory{}
|
|
|
|
t.Run("by default auth check is required", func(t *testing.T) {
|
|
cmd := NewVerifyCmd(f, func(o *Options) error {
|
|
return nil
|
|
})
|
|
|
|
// IsAuthCheckEnabled assumes commands under test are subcommands
|
|
parent := &cobra.Command{Use: "root"}
|
|
parent.AddCommand(cmd)
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{}))
|
|
require.True(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check to be required")
|
|
})
|
|
|
|
t.Run("when --bundle flag is provided, auth check is not required", func(t *testing.T) {
|
|
cmd := NewVerifyCmd(f, func(o *Options) error {
|
|
return nil
|
|
})
|
|
|
|
// IsAuthCheckEnabled assumes commands under test are subcommands
|
|
parent := &cobra.Command{Use: "root"}
|
|
parent.AddCommand(cmd)
|
|
|
|
require.NoError(t, cmd.ParseFlags([]string{"--bundle", "not-important"}))
|
|
require.False(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check not to be required due to --bundle flag")
|
|
})
|
|
}
|
|
|
|
func TestJSONOutput(t *testing.T) {
|
|
testIO, _, out, _ := iostreams.Test()
|
|
opts := Options{
|
|
ArtifactPath: artifactPath,
|
|
BundlePath: bundlePath,
|
|
DigestAlgorithm: "sha512",
|
|
APIClient: api.NewTestClient(),
|
|
Logger: io.NewHandler(testIO),
|
|
OCIClient: oci.MockClient{},
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SANRegex: "^https://github.com/sigstore/",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
exporter: cmdutil.NewJSONExporter(),
|
|
}
|
|
require.NoError(t, runVerify(&opts))
|
|
|
|
var target []*verification.AttestationProcessingResult
|
|
err := json.Unmarshal(out.Bytes(), &target)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestRunVerify(t *testing.T) {
|
|
logger := io.NewTestHandler()
|
|
|
|
publicGoodOpts := Options{
|
|
ArtifactPath: artifactPath,
|
|
BundlePath: bundlePath,
|
|
DigestAlgorithm: "sha512",
|
|
APIClient: api.NewTestClient(),
|
|
Logger: logger,
|
|
OCIClient: oci.MockClient{},
|
|
OIDCIssuer: verification.GitHubOIDCIssuer,
|
|
Owner: "sigstore",
|
|
PredicateType: verification.SLSAPredicateV1,
|
|
SANRegex: "^https://github.com/sigstore/",
|
|
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
|
}
|
|
|
|
t.Run("with valid artifact and bundle", func(t *testing.T) {
|
|
require.NoError(t, runVerify(&publicGoodOpts))
|
|
})
|
|
|
|
t.Run("with failing OCI artifact fetch", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
opts.OCIClient = oci.ReferenceFailClient{}
|
|
|
|
err := runVerify(&opts)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "failed to parse reference")
|
|
})
|
|
|
|
t.Run("with missing artifact path", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.ArtifactPath = "../test/data/non-existent-artifact.zip"
|
|
require.Error(t, runVerify(&opts))
|
|
})
|
|
|
|
t.Run("with missing bundle path", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = "../test/data/non-existent-sigstoreBundle.json"
|
|
require.Error(t, runVerify(&opts))
|
|
})
|
|
|
|
t.Run("with owner", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = ""
|
|
opts.Owner = "sigstore"
|
|
|
|
require.NoError(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 = "sigstore/sigstore-js"
|
|
|
|
require.Nil(t, runVerify(&opts))
|
|
})
|
|
|
|
// Test with bad tenancy
|
|
t.Run("with bad tenancy", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = ""
|
|
opts.Repo = "sigstore/sigstore-js"
|
|
opts.Tenant = "foo"
|
|
|
|
err := runVerify(&opts)
|
|
require.ErrorContains(t, err, "expected SourceRepositoryOwnerURI to be https://foo.ghe.com/sigstore, got https://github.com/sigstore")
|
|
})
|
|
|
|
t.Run("with repo which not matches SourceRepositoryURI", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = ""
|
|
opts.Repo = "sigstore/wrong"
|
|
|
|
err := runVerify(&opts)
|
|
require.ErrorContains(t, err, "expected SourceRepositoryURI to be https://github.com/sigstore/wrong, got https://github.com/sigstore/sigstore-js")
|
|
})
|
|
|
|
t.Run("with invalid repo", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = ""
|
|
opts.Repo = "wrong/example"
|
|
opts.APIClient = api.NewFailTestClient()
|
|
|
|
err := runVerify(&opts)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "failed to fetch attestations from wrong/example")
|
|
})
|
|
|
|
t.Run("with invalid owner", func(t *testing.T) {
|
|
opts := publicGoodOpts
|
|
opts.BundlePath = ""
|
|
opts.APIClient = api.NewFailTestClient()
|
|
opts.Owner = "wrong-owner"
|
|
|
|
err := runVerify(&opts)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "failed to fetch attestations from wrong-owner")
|
|
})
|
|
|
|
t.Run("with missing API client", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.APIClient = nil
|
|
customOpts.BundlePath = ""
|
|
require.Error(t, runVerify(&customOpts))
|
|
})
|
|
|
|
t.Run("with valid OCI artifact", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
customOpts.BundlePath = ""
|
|
|
|
require.Nil(t, runVerify(&customOpts))
|
|
})
|
|
|
|
t.Run("with valid OCI artifact with UseBundleFromRegistry flag", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
customOpts.BundlePath = ""
|
|
customOpts.UseBundleFromRegistry = true
|
|
|
|
require.Nil(t, runVerify(&customOpts))
|
|
})
|
|
|
|
t.Run("with valid OCI artifact with UseBundleFromRegistry flag and unknown predicate type", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
customOpts.BundlePath = ""
|
|
customOpts.UseBundleFromRegistry = true
|
|
customOpts.PredicateType = "https://predicate.type"
|
|
|
|
err := runVerify(&customOpts)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "no attestations found with predicate type")
|
|
})
|
|
|
|
t.Run("with valid OCI artifact with UseBundleFromRegistry flag but no bundle return from registry", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
customOpts.BundlePath = ""
|
|
customOpts.UseBundleFromRegistry = true
|
|
customOpts.OCIClient = oci.NoAttestationsClient{}
|
|
|
|
require.ErrorContains(t, runVerify(&customOpts), "no attestations found in the OCI registry. Retry the command without the --bundle-from-oci flag to check GitHub for the attestation")
|
|
})
|
|
|
|
t.Run("with valid OCI artifact with UseBundleFromRegistry flag but fail on fetching bundle from registry", func(t *testing.T) {
|
|
customOpts := publicGoodOpts
|
|
customOpts.ArtifactPath = "oci://ghcr.io/github/test"
|
|
customOpts.BundlePath = ""
|
|
customOpts.UseBundleFromRegistry = true
|
|
customOpts.OCIClient = oci.NoAttestationsClient{}
|
|
|
|
require.ErrorContains(t, runVerify(&customOpts), "no attestations found in the OCI registry. Retry the command without the --bundle-from-oci flag to check GitHub for the attestation")
|
|
})
|
|
}
|