Repository: sigstore/cosign
Pull request #2650 (2023-01-24 17:42:28)

Add warning to use digest instead of tags to other cosign commands
fixes https://github.com/sigstore/cosign/issues/2047 for 2.0

Signed-off-by: Priya Wadhwa <priya@chainguard.dev>




Patch:
diff --git a/cmd/cosign/cli/attach/attach.go b/cmd/cosign/cli/attach/attach.go
index c587e074..9b645422 100644
--- a/cmd/cosign/cli/attach/attach.go
+++ b/cmd/cosign/cli/attach/attach.go
@@ -23,6 +23,7 @@ import (
 	"github.com/google/go-containerregistry/pkg/name"
 	ssldsse "github.com/secure-systems-lab/go-securesystemslib/dsse"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
+	"github.com/sigstore/cosign/v2/internal/ui"
 	"github.com/sigstore/cosign/v2/pkg/oci/mutate"
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
 	"github.com/sigstore/cosign/v2/pkg/oci/static"
@@ -36,7 +37,7 @@ func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, signed
 	}
 
 	for _, payload := range signedPayloads {
-		if err := attachAttestation(ociremoteOpts, payload, imageRef, regOpts.NameOptions()); err != nil {
+		if err := attachAttestation(ctx, ociremoteOpts, payload, imageRef, regOpts.NameOptions()); err != nil {
 			return fmt.Errorf("attaching payload from %s: %w", payload, err)
 		}
 	}
@@ -44,7 +45,7 @@ func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, signed
 	return nil
 }
 
-func attachAttestation(remoteOpts []ociremote.Option, signedPayload, imageRef string, nameOpts []name.Option) error {
+func attachAttestation(ctx context.Context, remoteOpts []ociremote.Option, signedPayload, imageRef string, nameOpts []name.Option) error {
 	fmt.Fprintf(os.Stderr, "Using payload from: %s", signedPayload)
 	attestationFile, err := os.Open(signedPayload)
 	if err != nil {
@@ -75,6 +76,10 @@ func attachAttestation(remoteOpts []ociremote.Option, signedPayload, imageRef st
 		if err != nil {
 			return err
 		}
+		if _, ok := ref.(name.Digest); !ok {
+			msg := fmt.Sprintf(ui.TagReferenceMessage, imageRef)
+			ui.Warnf(ctx, msg)
+		}
 		digest, err := ociremote.ResolveDigest(ref, remoteOpts...)
 		if err != nil {
 			return err
diff --git a/cmd/cosign/cli/copy/copy.go b/cmd/cosign/cli/copy/copy.go
index 7270cef4..679c1454 100644
--- a/cmd/cosign/cli/copy/copy.go
+++ b/cmd/cosign/cli/copy/copy.go
@@ -26,6 +26,7 @@ import (
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
+	"github.com/sigstore/cosign/v2/internal/ui"
 	"github.com/sigstore/cosign/v2/pkg/oci"
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
 	"github.com/sigstore/cosign/v2/pkg/oci/walk"
@@ -39,6 +40,10 @@ func CopyCmd(ctx context.Context, regOpts options.RegistryOptions, srcImg, dstIm
 	if err != nil {
 		return err
 	}
+	if _, ok := srcRef.(name.Digest); !ok {
+		msg := fmt.Sprintf(ui.TagReferenceMessage, srcImg)
+		ui.Warnf(ctx, msg)
+	}
 	srcRepoRef := srcRef.Context()
 
 	dstRef, err := name.ParseReference(dstImg, no...)
diff --git a/cmd/cosign/cli/save.go b/cmd/cosign/cli/save.go
index 0d0e437c..bcf8faf6 100644
--- a/cmd/cosign/cli/save.go
+++ b/cmd/cosign/cli/save.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
+	"github.com/sigstore/cosign/v2/internal/ui"
 	"github.com/sigstore/cosign/v2/pkg/oci"
 	"github.com/sigstore/cosign/v2/pkg/oci/layout"
 	ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
@@ -52,6 +53,10 @@ func SaveCmd(ctx context.Context, opts options.SaveOptions, imageRef string) err
 	if err != nil {
 		return fmt.Errorf("parsing image name %s: %w", imageRef, err)
 	}
+	if _, ok := ref.(name.Digest); !ok {
+		msg := fmt.Sprintf(ui.TagReferenceMessage, imageRef)
+		ui.Warnf(ctx, msg)
+	}
 
 	se, err := ociremote.SignedEntity(ref)
 	if err != nil {
diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go
index cbf447cc..eed2b0d2 100644
--- a/cmd/cosign/cli/sign/sign.go
+++ b/cmd/cosign/cli/sign/sign.go
@@ -60,14 +60,6 @@ import (
 	_ "github.com/sigstore/cosign/v2/pkg/providers/all"
 )
 
-const TagReferenceMessage string = `Image reference %s uses a tag, not a digest, to identify the image to sign.
-
-    This can lead you to sign a different image than the intended one. Please use a
-    digest (example.com/ubuntu@sha256:abc123...) rather than tag
-    (example.com/ubuntu:latest) for the input to cosign. The ability to refer to
-    images by tag will be removed in a future release.
-`
-
 func ShouldUploadToTlog(ctx context.Context, ko options.KeyOpts, ref name.Reference, tlogUpload bool) (bool, error) {
 	upload := shouldUploadToTlog(ctx, ko, ref, tlogUpload)
 	var statementErr error
@@ -128,7 +120,7 @@ func ParseOCIReference(ctx context.Context, refStr string, opts ...name.Option)
 		return nil, fmt.Errorf("parsing reference: %w", err)
 	}
 	if _, ok := ref.(name.Digest); !ok {
-		msg := fmt.Sprintf(TagReferenceMessage, refStr)
+		msg := fmt.Sprintf(ui.TagReferenceMessage, refStr)
 		ui.Warnf(ctx, msg)
 	}
 	return ref, nil
diff --git a/internal/ui/warnings.go b/internal/ui/warnings.go
new file mode 100644
index 00000000..0643f41f
--- /dev/null
+++ b/internal/ui/warnings.go
@@ -0,0 +1,22 @@
+// Copyright 2023 The Sigstore Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ui
+
+const TagReferenceMessage string = `Image reference %s uses a tag, not a digest, to identify the image to sign.
+    This can lead you to sign a different image than the intended one. Please use a
+    digest (example.com/ubuntu@sha256:abc123...) rather than tag
+    (example.com/ubuntu:latest) for the input to cosign. The ability to refer to
+    images by tag will be removed in a future release.
+`


Test patch:
diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go
index 3b38f38c..6500de16 100644
--- a/cmd/cosign/cli/attest/attest.go
+++ b/cmd/cosign/cli/attest/attest.go
@@ -32,6 +32,7 @@ import (
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
 	"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
+	"github.com/sigstore/cosign/v2/internal/ui"
 	"github.com/sigstore/cosign/v2/pkg/cosign"
 	"github.com/sigstore/cosign/v2/pkg/cosign/attestation"
 	cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle"
@@ -97,6 +98,10 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
 	if err != nil {
 		return fmt.Errorf("parsing reference: %w", err)
 	}
+	if _, ok := ref.(name.Digest); !ok {
+		msg := fmt.Sprintf(ui.TagReferenceMessage, imageRef)
+		ui.Warnf(ctx, msg)
+	}
 
 	if c.Timeout != 0 {
 		var cancelFn context.CancelFunc
diff --git a/cmd/cosign/cli/download/attestation.go b/cmd/cosign/cli/download/attestation.go
index ea91bf0f..cb8c6b35 100644
--- a/cmd/cosign/cli/download/attestation.go
+++ b/cmd/cosign/cli/download/attestation.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
+	"github.com/sigstore/cosign/v2/internal/ui"
 	"github.com/sigstore/cosign/v2/pkg/cosign"
 )
 
@@ -30,6 +31,10 @@ func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, imageR
 	if err != nil {
 		return err
 	}
+	if _, ok := ref.(name.Digest); !ok {
+		msg := fmt.Sprintf(ui.TagReferenceMessage, imageRef)
+		ui.Warnf(ctx, msg)
+	}
 	ociremoteOpts, err := regOpts.ClientOpts(ctx)
 	if err != nil {
 		return err