mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 21:17:58 +08:00
feat(cosign): add support for cosign bundle (#4023)
Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
This commit is contained in:
committed by
GitHub
parent
993a17f5d0
commit
0b2eaa0f9a
@@ -16,7 +16,7 @@ GOLINTER_VERSION := v2.6.2
|
||||
NOTATION := $(TOOLSDIR)/bin/notation
|
||||
NOTATION_VERSION := 1.3.2
|
||||
COSIGN := $(TOOLSDIR)/bin/cosign
|
||||
COSIGN_VERSION := 2.2.0
|
||||
COSIGN_VERSION := 3.0.6
|
||||
HELM := $(TOOLSDIR)/bin/helm
|
||||
ORAS := $(TOOLSDIR)/bin/oras
|
||||
ORAS_VERSION := 1.2.1
|
||||
|
||||
+14
-13
@@ -546,23 +546,24 @@ func isCosignSigned(ctx context.Context, repo, digestStr string, searchConf Sear
|
||||
return true
|
||||
}
|
||||
|
||||
var referrers ispec.Index
|
||||
for _, artifactType := range []string{common.ArtifactTypeCosign, common.ArtifactTypeCosignBundle} {
|
||||
var referrers ispec.Index
|
||||
|
||||
artifactType := url.QueryEscape(common.ArtifactTypeCosign)
|
||||
URL = fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s",
|
||||
searchConf.ServURL, repo, digestStr, artifactType)
|
||||
URL = fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s",
|
||||
searchConf.ServURL, repo, digestStr, url.QueryEscape(artifactType))
|
||||
|
||||
_, err = httpClient.makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS,
|
||||
searchConf.Debug, &referrers, searchConf.ResultWriter)
|
||||
if err != nil {
|
||||
return false
|
||||
_, err = httpClient.makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS,
|
||||
searchConf.Debug, &referrers, searchConf.ResultWriter)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(referrers.Manifests) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if len(referrers.Manifests) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *requestsPool) submitJob(job *httpJob) {
|
||||
|
||||
@@ -30,8 +30,9 @@ const (
|
||||
// ArtifactTypeNotation is the same value as github.com/notaryproject/notation-go/registry.ArtifactTypeNotation
|
||||
// (assert by internal test).
|
||||
// reason used: to reduce zot minimal binary size (otherwise adds oras.land/oras-go/v2 deps).
|
||||
ArtifactTypeNotation = "application/vnd.cncf.notary.signature"
|
||||
ArtifactTypeCosign = "application/vnd.dev.cosign.artifact.sig.v1+json"
|
||||
ArtifactTypeNotation = "application/vnd.cncf.notary.signature"
|
||||
ArtifactTypeCosign = "application/vnd.dev.cosign.artifact.sig.v1+json"
|
||||
ArtifactTypeCosignBundle = "application/vnd.dev.sigstore.bundle.v0.3+json"
|
||||
// CosignSignatureTagSuffix is the suffix used for cosign signature tags (e.g., "sha256-digest.sig").
|
||||
// Using constant to avoid pulling in cosign dependency.
|
||||
CosignSignatureTagSuffix = "sig"
|
||||
@@ -53,6 +54,12 @@ func IsCosignTag(tag string) bool {
|
||||
return IsCosignSignature(tag) || IsCosignSBOM(tag)
|
||||
}
|
||||
|
||||
// IsArtifactTypeCosign returns true if the given artifact type corresponds to a cosign signature,
|
||||
// covering both the legacy type and the newer sigstore bundle type.
|
||||
func IsArtifactTypeCosign(artifactType string) bool {
|
||||
return artifactType == ArtifactTypeCosign || artifactType == ArtifactTypeCosignBundle
|
||||
}
|
||||
|
||||
// RemoveFrom removes matches of item in [].
|
||||
func RemoveFrom(inputSlice []string, item string) []string {
|
||||
var newSlice []string
|
||||
|
||||
@@ -61,6 +61,13 @@ func TestCommon(t *testing.T) {
|
||||
So(common.ArtifactTypeNotation, ShouldEqual, notreg.ArtifactTypeNotation)
|
||||
})
|
||||
|
||||
Convey("Test IsArtifactTypeCosign", t, func() {
|
||||
So(common.IsArtifactTypeCosign(common.ArtifactTypeCosign), ShouldBeTrue)
|
||||
So(common.IsArtifactTypeCosign(common.ArtifactTypeCosignBundle), ShouldBeTrue)
|
||||
So(common.IsArtifactTypeCosign(common.ArtifactTypeNotation), ShouldBeFalse)
|
||||
So(common.IsArtifactTypeCosign("application/example"), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Test GetLocalIPs", t, func() {
|
||||
localIPs, err := common.GetLocalIPs()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -26,7 +26,7 @@ func hasSignatureReferrers(refs referrer.ReferrerList) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if desc.ArtifactType == common.ArtifactTypeCosign {
|
||||
if common.IsArtifactTypeCosign(desc.ArtifactType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -439,7 +439,7 @@ func isSignature(reference string, manifestContent ispec.Manifest) (bool, string
|
||||
}
|
||||
|
||||
// check cosign signature
|
||||
if manifestArtifactType == zcommon.ArtifactTypeCosign && manifestContent.Subject != nil {
|
||||
if zcommon.IsArtifactTypeCosign(manifestArtifactType) && manifestContent.Subject != nil {
|
||||
return true, CosignType, manifestContent.Subject.Digest
|
||||
}
|
||||
|
||||
|
||||
@@ -556,6 +556,35 @@ func RunParseStorageTests(rootDir string, metaDB mTypes.MetaDB, log log.Logger)
|
||||
So(repos[0].Signatures, ShouldContainKey, missingImageDigest.String())
|
||||
})
|
||||
|
||||
Convey("Detect cosign bundle signatures by artifact type and subject", func() {
|
||||
imageStore := local.NewImageStore(rootDir, false, false,
|
||||
log, monitoring.NewMetricsServer(false, log), nil, nil, nil, nil)
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: imageStore}
|
||||
|
||||
signedImage := CreateRandomImage()
|
||||
err := WriteImageToFileSystem(signedImage, repo, "signed", storeController)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
bundleSig := CreateMockCosignBundleSignature(signedImage.DescriptorRef())
|
||||
err = WriteImageToFileSystem(bundleSig, repo, "bundle-sig", storeController)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = meta.ParseStorage(metaDB, storeController, log) //nolint: contextcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
repos, err := metaDB.GetMultipleRepoMeta(ctx,
|
||||
func(repoMeta mTypes.RepoMeta) bool { return true })
|
||||
So(err, ShouldBeNil)
|
||||
So(repos, ShouldNotBeEmpty)
|
||||
|
||||
repoMeta := repos[0]
|
||||
subjectDigest := signedImage.DigestStr()
|
||||
So(repoMeta.Signatures, ShouldContainKey, subjectDigest)
|
||||
So(repoMeta.Signatures[subjectDigest], ShouldContainKey, zcommon.CosignSignature)
|
||||
So(len(repoMeta.Signatures[subjectDigest][zcommon.CosignSignature]), ShouldBeGreaterThan, 0)
|
||||
})
|
||||
|
||||
Convey("Check statistics after load", func() {
|
||||
imageStore := local.NewImageStore(rootDir, false, false,
|
||||
log, monitoring.NewMetricsServer(false, log), nil, nil, nil, nil)
|
||||
|
||||
@@ -653,7 +653,7 @@ func IsSignature(descriptor ispec.Descriptor) bool {
|
||||
}
|
||||
|
||||
// is cosign signature (OCI 1.1 support)
|
||||
if descriptor.ArtifactType == zcommon.ArtifactTypeCosign {
|
||||
if zcommon.IsArtifactTypeCosign(descriptor.ArtifactType) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ func (gc GarbageCollect) removeReferrer(repo string, index *ispec.Index, manifes
|
||||
// check if its notation or cosign signature
|
||||
if artifactType == zcommon.ArtifactTypeNotation {
|
||||
signatureType = storage.NotationType
|
||||
} else if artifactType == zcommon.ArtifactTypeCosign {
|
||||
} else if zcommon.IsArtifactTypeCosign(artifactType) {
|
||||
signatureType = storage.CosignType
|
||||
}
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ func CheckIsImageSignature(repoName string, manifestBlob []byte, reference strin
|
||||
}
|
||||
|
||||
// check cosign signature (OCI 1.1 support)
|
||||
if manifestArtifactType == zcommon.ArtifactTypeCosign && manifestContent.Subject != nil {
|
||||
if zcommon.IsArtifactTypeCosign(manifestArtifactType) && manifestContent.Subject != nil {
|
||||
return true, CosignType, manifestContent.Subject.Digest, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,11 @@ func CreateMockCosignSignature(subject *ispec.Descriptor) Image {
|
||||
ArtifactType(common.ArtifactTypeCosign).Build()
|
||||
}
|
||||
|
||||
func CreateMockCosignBundleSignature(subject *ispec.Descriptor) Image {
|
||||
return CreateImageWith().EmptyLayer().EmptyConfig().Subject(subject).
|
||||
ArtifactType(common.ArtifactTypeCosignBundle).Build()
|
||||
}
|
||||
|
||||
type BaseImageBuilder struct {
|
||||
layers []Layer
|
||||
|
||||
|
||||
@@ -266,12 +266,12 @@ func (olu BaseOciLayoutUtils) checkCosignSignature(name string, digest godigest.
|
||||
return true
|
||||
}
|
||||
|
||||
mediaType := common.ArtifactTypeCosign
|
||||
mediaTypes := []string{common.ArtifactTypeCosign, common.ArtifactTypeCosignBundle}
|
||||
|
||||
referrers, err := imageStore.GetReferrers(name, digest, []string{mediaType})
|
||||
referrers, err := imageStore.GetReferrers(name, digest, mediaTypes)
|
||||
if err != nil {
|
||||
olu.Log.Info().Err(err).Str("repository", name).Str("digest",
|
||||
digest.String()).Str("mediatype", mediaType).Msg("invalid cosign signature")
|
||||
digest.String()).Interface("mediatypes", mediaTypes).Msg("invalid cosign signature")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ function verify_prerequisites {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! $(command -v cosign) ]; then
|
||||
echo "you need to install cosign as a prerequisite to running the tests" >&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -132,18 +137,37 @@ function teardown_file() {
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test"
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --key ${BATS_FILE_TMPDIR}/cosign-sign-test.key localhost:${zot_port}/annotations:latest --yes
|
||||
run cosign sign --registry-referrers-mode=legacy --key ${BATS_FILE_TMPDIR}/cosign-sign-test.key localhost:${zot_port}/annotations:latest --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test.pub localhost:${zot_port}/annotations:latest
|
||||
[ "$status" -eq 0 ]
|
||||
local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"')
|
||||
[[ "$sigName" == *"${digest}"* ]]
|
||||
tags=( $(oras repo tags --plain-http localhost:${zot_port}/annotations) )
|
||||
[ "$status" -eq 0 ]
|
||||
local sigdes=$(oras manifest fetch --descriptor localhost:${zot_port}/annotations:${tags[1]} | jq .digest | tr -d \")
|
||||
[ "$status" -eq 0 ]
|
||||
run oras manifest fetch --plain-http localhost:${zot_port}/annotations@${sigdes}
|
||||
run oras repo tags --plain-http localhost:${zot_port}/annotations
|
||||
[ "$status" -eq 0 ]
|
||||
local sigTag=""
|
||||
for tag in "${lines[@]}"; do
|
||||
if [[ "${tag}" == *.sig ]]; then
|
||||
sigTag="${tag}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "${sigTag}" ]; then
|
||||
run oras manifest fetch --plain-http --descriptor localhost:${zot_port}/annotations:${sigTag}
|
||||
[ "$status" -eq 0 ]
|
||||
local sigdes=$(echo "${output}" | jq -r .digest)
|
||||
[ -n "${sigdes}" ]
|
||||
|
||||
run oras manifest fetch --plain-http localhost:${zot_port}/annotations@${sigdes}
|
||||
[ "$status" -eq 0 ]
|
||||
else
|
||||
# Fallback lookup via referrers API for registries where cosign v3 does not expose legacy .sig tags.
|
||||
run oras discover --plain-http --distribution-spec v1.1-referrers-api --format json localhost:${zot_port}/annotations:latest
|
||||
[ "$status" -eq 0 ]
|
||||
local sigRefCount=$(echo "${output}" | jq '(.referrers // .manifests // []) | length')
|
||||
[ "${sigRefCount}" -gt 0 ]
|
||||
fi
|
||||
}
|
||||
|
||||
@test "sign/verify with cosign (only referrers)" {
|
||||
@@ -153,20 +177,33 @@ function teardown_file() {
|
||||
[ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ]
|
||||
local digest=$(echo "${lines[-1]}" | jq -r '.data.ImageList.Results[0].Manifests[0].Digest')
|
||||
|
||||
export COSIGN_OCI_EXPERIMENTAL=1
|
||||
export COSIGN_EXPERIMENTAL=1
|
||||
run cosign initialize
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-experimental"
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-experimental.key localhost:${zot_port}/annotations:latest --yes
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-experimental.key localhost:${zot_port}/annotations:latest --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-experimental.pub localhost:${zot_port}/annotations:latest
|
||||
[ "$status" -eq 0 ]
|
||||
local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"')
|
||||
[[ "$sigName" == *"${digest}"* ]]
|
||||
unset COSIGN_OCI_EXPERIMENTAL
|
||||
unset COSIGN_EXPERIMENTAL
|
||||
}
|
||||
|
||||
@test "zot reports IsSigned true for cosign referrer signatures" {
|
||||
zot_port=`cat ${BATS_FILE_TMPDIR}/zot.port`
|
||||
run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageList(repo: \"annotations\") { Results { RepoName Tag Manifests {Digest ConfigDigest Size Layers { Size Digest }} Vendor Licenses }}}"}' http://localhost:${zot_port}/v2/_zot/ext/search
|
||||
[ "$status" -eq 0 ]
|
||||
[ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ]
|
||||
|
||||
run cosign initialize
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-is-signed"
|
||||
[ "$status" -eq 0 ]
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-is-signed.key --allow-insecure-registry localhost:${zot_port}/annotations:latest --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ GlobalSearch(query: \"annotations:latest\") { Images { IsSigned } } }" }' http://localhost:${zot_port}/v2/_zot/ext/search
|
||||
[ "$status" -eq 0 ]
|
||||
[ $(echo "${lines[-1]}" | jq '.data.GlobalSearch.Images[0].IsSigned') = 'true' ]
|
||||
}
|
||||
|
||||
@test "sign/verify with cosign (tag and referrers)" {
|
||||
@@ -176,8 +213,6 @@ function teardown_file() {
|
||||
[ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ]
|
||||
local digest=$(echo "${lines[-1]}" | jq -r '.data.ImageList.Results[0].Manifests[0].Digest')
|
||||
|
||||
export COSIGN_OCI_EXPERIMENTAL=1
|
||||
export COSIGN_EXPERIMENTAL=1
|
||||
run cosign initialize
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -188,7 +223,7 @@ function teardown_file() {
|
||||
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1"
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1.key localhost:${zot_port}/annotations:latest --yes
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1.key localhost:${zot_port}/annotations:latest --yes
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-tag-2"
|
||||
@@ -211,15 +246,13 @@ function teardown_file() {
|
||||
|
||||
run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2"
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2.key localhost:${zot_port}/annotations:latest --yes
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2.key localhost:${zot_port}/annotations:latest --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2.pub localhost:${zot_port}/annotations:latest
|
||||
[ "$status" -eq 0 ]
|
||||
local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"')
|
||||
[[ "$sigName" == *"${digest}"* ]]
|
||||
|
||||
unset COSIGN_OCI_EXPERIMENTAL
|
||||
unset COSIGN_EXPERIMENTAL
|
||||
}
|
||||
|
||||
@test "sign/verify with notation" {
|
||||
|
||||
@@ -17,13 +17,16 @@ function verify_prerequisites() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! $(command -v cosign) ]; then
|
||||
echo "you need to install cosign as a prerequisite to running the tests" >&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function setup_file() {
|
||||
export COSIGN_PASSWORD=""
|
||||
export COSIGN_OCI_EXPERIMENTAL=1
|
||||
export COSIGN_EXPERIMENTAL=1
|
||||
|
||||
# Verify prerequisites are available
|
||||
if ! $(verify_prerequisites); then
|
||||
@@ -275,7 +278,7 @@ function teardown_file() {
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.pub localhost:${zot_port3}/golang:1.20
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -17,13 +17,16 @@ function verify_prerequisites() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! $(command -v cosign) ]; then
|
||||
echo "you need to install cosign as a prerequisite to running the tests" >&3
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function setup_file() {
|
||||
export COSIGN_PASSWORD=""
|
||||
export COSIGN_OCI_EXPERIMENTAL=1
|
||||
export COSIGN_EXPERIMENTAL=1
|
||||
|
||||
# Verify prerequisites are available
|
||||
if ! $(verify_prerequisites); then
|
||||
@@ -302,7 +305,7 @@ function teardown_file() {
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
run env COSIGN_EXPERIMENTAL=1 cosign sign --new-bundle-format=true --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.key localhost:${zot_port3}/golang:1.20 --yes
|
||||
[ "$status" -eq 0 ]
|
||||
run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-sync-test.pub localhost:${zot_port3}/golang:1.20
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
Reference in New Issue
Block a user