diff --git a/USAGE.md b/USAGE.md index a13607f72f1..e58d1295161 100644 --- a/USAGE.md +++ b/USAGE.md @@ -138,13 +138,6 @@ $ cosign attach signature --signature file.sig $IMAGE_DIGEST Pushing signature to: dlorenc/demo:sha256-87ef60f558bad79beea6425a3b28989f01dd417164150ab3baab98dcbf04def8.sig ``` -the base64-encoded signature: - -```shell -$ cosign attach signature --signature Qr883oPOj0dj82PZ0d9mQ2lrdM0lbyLSXUkjt6ejrxtHxwe7bU6Gr27Sysgk1jagf1htO/gvkkg71oJiwWryCQ== $IMAGE_DIGEST -Pushing signature to: dlorenc/demo:sha256-87ef60f558bad79beea6425a3b28989f01dd417164150ab3baab98dcbf04def.sig -``` - or, `-` for stdin for chaining from other commands: ```shell diff --git a/cmd/cosign/cli/attach/sig.go b/cmd/cosign/cli/attach/sig.go index fff1409453d..1492f7fb233 100644 --- a/cmd/cosign/cli/attach/sig.go +++ b/cmd/cosign/cli/attach/sig.go @@ -112,14 +112,7 @@ func signatureType(sigRef string) SignatureArgType { switch { case sigRef == "-": return StdinSignature - case signatureFileNotExists(sigRef): - return RawSignature default: return FileSignature } } - -func signatureFileNotExists(sigRef string) bool { - _, err := os.Stat(sigRef) - return os.IsNotExist(err) -} diff --git a/cmd/cosign/cli/options/attach.go b/cmd/cosign/cli/options/attach.go index a0b213f3a21..750810d34b5 100644 --- a/cmd/cosign/cli/options/attach.go +++ b/cmd/cosign/cli/options/attach.go @@ -39,7 +39,7 @@ func (o *AttachSignatureOptions) AddFlags(cmd *cobra.Command) { o.Registry.AddFlags(cmd) cmd.Flags().StringVar(&o.Signature, "signature", "", - "the signature, path to the signature, or {-} for stdin") + "path to the signature, or {-} for stdin") cmd.Flags().StringVar(&o.Payload, "payload", "", "path to the payload covered by the signature (if using another format)") diff --git a/doc/cosign_attach_signature.md b/doc/cosign_attach_signature.md index 65b074f2de2..02ddb753f77 100644 --- a/doc/cosign_attach_signature.md +++ b/doc/cosign_attach_signature.md @@ -21,7 +21,7 @@ cosign attach signature [flags] -h, --help help for signature --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). --payload string path to the payload covered by the signature (if using another format) - --signature string the signature, path to the signature, or {-} for stdin + --signature string path to the signature, or {-} for stdin ``` ### Options inherited from parent commands diff --git a/test/e2e_test.go b/test/e2e_test.go index abe9b1cc647..caee9484676 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -1452,6 +1452,11 @@ func TestUploadDownload(t *testing.T) { signatureType attach.SignatureArgType expectedErr bool }{ + "stdin containing signature": { + signature: "testsignatureraw", + signatureType: attach.StdinSignature, + expectedErr: false, + }, "file containing signature": { signature: "testsignaturefile", signatureType: attach.FileSignature, @@ -1460,7 +1465,7 @@ func TestUploadDownload(t *testing.T) { "raw signature as argument": { signature: "testsignatureraw", signatureType: attach.RawSignature, - expectedErr: false, + expectedErr: true, }, "empty signature as argument": { signature: "", @@ -1476,10 +1481,14 @@ func TestUploadDownload(t *testing.T) { payload := "testpayload" payloadPath := mkfile(payload, td, t) signature := base64.StdEncoding.EncodeToString([]byte(testCase.signature)) + restoreStdin := func() {} var sigRef string if testCase.signatureType == attach.FileSignature { sigRef = mkfile(signature, td, t) + } else if testCase.signatureType == attach.StdinSignature { + sigRef = "-" + restoreStdin = mockStdin(signature, td, t) } else { sigRef = signature } @@ -1491,6 +1500,7 @@ func TestUploadDownload(t *testing.T) { } else { must(err, t) } + restoreStdin() // Now download it! se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...)) @@ -1757,6 +1767,97 @@ func TestAttachSBOM(t *testing.T) { mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom"), t) } +func TestAttachSBOM_bom_flag(t *testing.T) { + repo, stop := reg(t) + defer stop() + td := t.TempDir() + ctx := context.Background() + bomData, err := os.ReadFile("./testdata/bom-go-mod.spdx") + must(err, t) + + testCases := map[string]struct { + bom string + bomType attach.SignatureArgType + expectedErr bool + }{ + "stdin containing bom": { + bom: string(bomData), + bomType: attach.StdinSignature, + expectedErr: false, + }, + "file containing bom": { + bom: string(bomData), + bomType: attach.FileSignature, + expectedErr: false, + }, + "raw bom as argument": { + bom: string(bomData), + bomType: attach.RawSignature, + expectedErr: true, + }, + "empty bom as argument": { + bom: "", + bomType: attach.RawSignature, + expectedErr: true, + }, + } + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + imgName := path.Join(repo, "sbom-image") + img, _, cleanup := mkimage(t, imgName) + var sbomRef string + restoreStdin := func() {} + if testCase.bomType == attach.FileSignature { + sbomRef = mkfile(testCase.bom, td, t) + } else if testCase.bomType == attach.StdinSignature { + sbomRef = "-" + restoreStdin = mockStdin(testCase.bom, td, t) + } else { + sbomRef = testCase.bom + } + + out := bytes.Buffer{} + _, errPl := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{Platform: "darwin/amd64"}, img.Name(), &out) + if errPl == nil { + t.Fatalf("Expected error when passing Platform to single arch image") + } + _, err := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{}, img.Name(), &out) + if err == nil { + t.Fatal("Expected error") + } + t.Log(out.String()) + out.Reset() + + // Upload it! + err = attach.SBOMCmd(ctx, options.RegistryOptions{}, sbomRef, "spdx", imgName) + restoreStdin() + + if testCase.expectedErr { + mustErr(err, t) + } else { + sboms, err := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{}, imgName, &out) + if err != nil { + t.Fatal(err) + } + t.Log(out.String()) + if len(sboms) != 1 { + t.Fatalf("Expected one sbom, got %d", len(sboms)) + } + want, err := os.ReadFile("./testdata/bom-go-mod.spdx") + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(string(want), sboms[0]); diff != "" { + t.Errorf("diff: %s", diff) + } + } + + cleanup() + }) + } +} + func setenv(t *testing.T, k, v string) func() { if err := os.Setenv(k, v); err != nil { t.Fatalf("error setting env: %v", err) @@ -1868,6 +1969,19 @@ func TestGetPublicKeyCustomOut(t *testing.T) { equals(keys.PublicBytes, output, t) } +func mockStdin(contents, td string, t *testing.T) func() { + origin := os.Stdin + + p := mkfile(contents, td, t) + f, err := os.Open(p) + if err != nil { + t.Fatal(err) + } + os.Stdin = f + + return func() { os.Stdin = origin } +} + func mkfile(contents, td string, t *testing.T) string { f, err := os.CreateTemp(td, "") if err != nil {