diff --git a/cmd/cosign/cli/attach.go b/cmd/cosign/cli/attach.go index f8c384f97ac..bfe33125ba1 100644 --- a/cmd/cosign/cli/attach.go +++ b/cmd/cosign/cli/attach.go @@ -16,9 +16,6 @@ package cli import ( - "fmt" - "os" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/attach" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/spf13/cobra" @@ -32,7 +29,6 @@ func Attach() *cobra.Command { cmd.AddCommand( attachSignature(), - attachSBOM(), attachAttestation(), ) @@ -75,32 +71,6 @@ func attachSignature() *cobra.Command { return cmd } -func attachSBOM() *cobra.Command { - o := &options.AttachSBOMOptions{} - - cmd := &cobra.Command{ - Use: "sbom", - Short: "DEPRECATED: Attach sbom to the supplied container image", - Long: "Attach sbom to the supplied container image\n\n" + options.SBOMAttachmentDeprecation, - Example: " cosign attach sbom ", - Args: cobra.ExactArgs(1), - PersistentPreRun: options.BindViper, - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Fprintln(os.Stderr, options.SBOMAttachmentDeprecation) - mediaType, err := o.MediaType() - if err != nil { - return err - } - fmt.Fprintf(os.Stderr, "WARNING: Attaching SBOMs this way does not sign them. To sign them, use 'cosign attest --predicate %s --key '.\n", o.SBOM) - return attach.SBOMCmd(cmd.Context(), o.Registry, o.RegistryExperimental, o.SBOM, mediaType, args[0]) - }, - } - - o.AddFlags(cmd) - - return cmd -} - func attachAttestation() *cobra.Command { o := &options.AttachAttestationOptions{} diff --git a/cmd/cosign/cli/attach/sbom.go b/cmd/cosign/cli/attach/sbom.go deleted file mode 100644 index df651197f63..00000000000 --- a/cmd/cosign/cli/attach/sbom.go +++ /dev/null @@ -1,149 +0,0 @@ -// -// Copyright 2021 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 attach - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - - "github.com/google/go-containerregistry/pkg/logs" - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/remote/transport" - ocistatic "github.com/google/go-containerregistry/pkg/v1/static" - ocitypes "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" - ociexperimental "github.com/sigstore/cosign/v2/internal/pkg/oci/remote" - "github.com/sigstore/cosign/v2/internal/ui" - ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote" - "github.com/sigstore/cosign/v2/pkg/oci/static" -) - -func SBOMCmd(ctx context.Context, regOpts options.RegistryOptions, regExpOpts options.RegistryExperimentalOptions, sbomRef string, sbomType ocitypes.MediaType, imageRef string) error { - if regExpOpts.RegistryReferrersMode == options.RegistryReferrersModeOCI11 { - return sbomCmdOCIExperimental(ctx, regOpts, sbomRef, sbomType, imageRef) - } - - ref, err := name.ParseReference(imageRef, regOpts.NameOptions()...) - if err != nil { - return err - } - - b, err := sbomBytes(sbomRef) - if err != nil { - return err - } - - remoteOpts, err := regOpts.ClientOpts(ctx) - if err != nil { - return err - } - - dstRef, err := ociremote.SBOMTag(ref, remoteOpts...) - if err != nil { - return err - } - - ui.Infof(ctx, "Uploading SBOM file for [%s] to [%s] with mediaType [%s].\n", ref.Name(), dstRef.Name(), sbomType) - img, err := static.NewFile(b, static.WithLayerMediaType(sbomType)) - if err != nil { - return err - } - return remote.Write(dstRef, img, regOpts.GetRegistryClientOpts(ctx)...) -} - -func sbomCmdOCIExperimental(ctx context.Context, regOpts options.RegistryOptions, sbomRef string, sbomType ocitypes.MediaType, imageRef string) error { - var dig name.Digest - ref, err := name.ParseReference(imageRef, regOpts.NameOptions()...) - if err != nil { - return err - } - if digr, ok := ref.(name.Digest); ok { - dig = digr - } else { - desc, err := remote.Head(ref, regOpts.GetRegistryClientOpts(ctx)...) - if err != nil { - return err - } - dig = ref.Context().Digest(desc.Digest.String()) - } - - artifactType := ociexperimental.ArtifactType("sbom") - - desc, err := remote.Head(dig, regOpts.GetRegistryClientOpts(ctx)...) - var terr *transport.Error - if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { - h, err := v1.NewHash(dig.DigestStr()) - if err != nil { - return err - } - // The subject doesn't exist, attach to it as if it's an empty OCI image. - logs.Progress.Println("subject doesn't exist, attaching to empty image") - desc = &v1.Descriptor{ - ArtifactType: artifactType, - MediaType: ocitypes.OCIManifestSchema1, - Size: 0, - Digest: h, - } - } else if err != nil { - return err - } - - b, err := sbomBytes(sbomRef) - if err != nil { - return err - } - - empty := mutate.MediaType( - mutate.ConfigMediaType(empty.Image, ocitypes.MediaType(artifactType)), - ocitypes.OCIManifestSchema1) - att, err := mutate.AppendLayers(empty, ocistatic.NewLayer(b, sbomType)) - if err != nil { - return err - } - att = mutate.Subject(att, *desc).(v1.Image) - attdig, err := att.Digest() - if err != nil { - return err - } - dstRef := ref.Context().Digest(attdig.String()) - - fmt.Fprintf(os.Stderr, "Uploading SBOM file for [%s] to [%s] with config.mediaType [%s] layers[0].mediaType [%s].\n", - ref.Name(), dstRef.String(), artifactType, sbomType) - return remote.Write(dstRef, att, regOpts.GetRegistryClientOpts(ctx)...) -} - -func sbomBytes(sbomRef string) ([]byte, error) { - // sbomRef can be "-", a string or a file. - switch signatureType(sbomRef) { - case StdinSignature: - return io.ReadAll(os.Stdin) - case RawSignature: - return []byte(sbomRef), nil - case FileSignature: - return os.ReadFile(filepath.Clean(sbomRef)) - default: - return nil, errors.New("unknown SBOM arg type") - } -} diff --git a/cmd/cosign/cli/download.go b/cmd/cosign/cli/download.go index a9d082705a2..29e89cb3504 100644 --- a/cmd/cosign/cli/download.go +++ b/cmd/cosign/cli/download.go @@ -16,9 +16,6 @@ package cli import ( - "fmt" - "os" - "github.com/spf13/cobra" "github.com/sigstore/cosign/v2/cmd/cosign/cli/download" @@ -33,7 +30,6 @@ func Download() *cobra.Command { cmd.AddCommand( downloadSignature(), - downloadSBOM(), downloadAttestation(), ) @@ -59,31 +55,6 @@ func downloadSignature() *cobra.Command { return cmd } -func downloadSBOM() *cobra.Command { - o := &options.RegistryOptions{} - do := &options.SBOMDownloadOptions{} - - cmd := &cobra.Command{ - Use: "sbom", - Short: "DEPRECATED: Download SBOMs from the supplied container image", - Long: "Download SBOMs from the supplied container image\n\n" + options.SBOMAttachmentDeprecation, - Example: " cosign download sbom ", - Args: cobra.ExactArgs(1), - PersistentPreRun: options.BindViper, - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Fprintln(os.Stderr, options.SBOMAttachmentDeprecation) - fmt.Fprintln(os.Stderr, "WARNING: Downloading SBOMs this way does not ensure its authenticity. If you want to ensure a tamper-proof SBOM, download it using 'cosign download attestation '.") - _, err := download.SBOMCmd(cmd.Context(), *o, *do, args[0], cmd.OutOrStdout()) - return err - }, - } - - do.AddFlags(cmd) - o.AddFlags(cmd) - - return cmd -} - func downloadAttestation() *cobra.Command { o := &options.RegistryOptions{} ao := &options.AttestationDownloadOptions{} diff --git a/doc/cosign_attach.md b/doc/cosign_attach.md index 181dcb60bdf..3899f7e5d8c 100644 --- a/doc/cosign_attach.md +++ b/doc/cosign_attach.md @@ -20,6 +20,5 @@ Provides utilities for attaching artifacts to other artifacts in a registry * [cosign](cosign.md) - A tool for Container Signing, Verification and Storage in an OCI registry. * [cosign attach attestation](cosign_attach_attestation.md) - Attach attestation to the supplied container image -* [cosign attach sbom](cosign_attach_sbom.md) - DEPRECATED: Attach sbom to the supplied container image * [cosign attach signature](cosign_attach_signature.md) - Attach signatures to the supplied container image diff --git a/doc/cosign_attach_sbom.md b/doc/cosign_attach_sbom.md deleted file mode 100644 index 07948123883..00000000000 --- a/doc/cosign_attach_sbom.md +++ /dev/null @@ -1,49 +0,0 @@ -## cosign attach sbom - -DEPRECATED: Attach sbom to the supplied container image - -### Synopsis - -Attach sbom to the supplied container image - -WARNING: SBOM attachments are deprecated and support will be removed in a Cosign release soon after 2024-02-22 (see https://github.com/sigstore/cosign/issues/2755). Instead, please use SBOM attestations. - -``` -cosign attach sbom [flags] -``` - -### Examples - -``` - cosign attach sbom -``` - -### Options - -``` - --allow-http-registry whether to allow using HTTP protocol while connecting to registries. Don't use this for anything but testing - --allow-insecure-registry whether to allow insecure connections to registries (e.g., with expired or self-signed TLS certificates). Don't use this for anything but testing - --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] - -h, --help help for sbom - --input-format string type of sbom input format (json|xml|text) - --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). - --registry-password string registry basic auth password - --registry-referrers-mode registryReferrersMode mode for fetching references from the registry. allowed: legacy, oci-1-1 - --registry-token string registry bearer auth token - --registry-username string registry basic auth username - --sbom string path to the sbom, or {-} for stdin - --type string type of sbom (spdx|cyclonedx|syft) (default "spdx") -``` - -### Options inherited from parent commands - -``` - --output-file string log output to a file - -t, --timeout duration timeout for commands (default 3m0s) - -d, --verbose log debug output -``` - -### SEE ALSO - -* [cosign attach](cosign_attach.md) - Provides utilities for attaching artifacts to other artifacts in a registry - diff --git a/doc/cosign_download.md b/doc/cosign_download.md index 0700d193023..84dcebb7103 100644 --- a/doc/cosign_download.md +++ b/doc/cosign_download.md @@ -20,6 +20,5 @@ Provides utilities for downloading artifacts and attached artifacts in a registr * [cosign](cosign.md) - A tool for Container Signing, Verification and Storage in an OCI registry. * [cosign download attestation](cosign_download_attestation.md) - Download in-toto attestations from the supplied container image -* [cosign download sbom](cosign_download_sbom.md) - DEPRECATED: Download SBOMs from the supplied container image * [cosign download signature](cosign_download_signature.md) - Download signatures from the supplied container image diff --git a/doc/cosign_download_sbom.md b/doc/cosign_download_sbom.md deleted file mode 100644 index 77bbaa8c2cf..00000000000 --- a/doc/cosign_download_sbom.md +++ /dev/null @@ -1,46 +0,0 @@ -## cosign download sbom - -DEPRECATED: Download SBOMs from the supplied container image - -### Synopsis - -Download SBOMs from the supplied container image - -WARNING: SBOM attachments are deprecated and support will be removed in a Cosign release soon after 2024-02-22 (see https://github.com/sigstore/cosign/issues/2755). Instead, please use SBOM attestations. - -``` -cosign download sbom [flags] -``` - -### Examples - -``` - cosign download sbom -``` - -### Options - -``` - --allow-http-registry whether to allow using HTTP protocol while connecting to registries. Don't use this for anything but testing - --allow-insecure-registry whether to allow insecure connections to registries (e.g., with expired or self-signed TLS certificates). Don't use this for anything but testing - --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] - -h, --help help for sbom - --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). - --platform string download SBOM for a specific platform image - --registry-password string registry basic auth password - --registry-token string registry bearer auth token - --registry-username string registry basic auth username -``` - -### Options inherited from parent commands - -``` - --output-file string log output to a file - -t, --timeout duration timeout for commands (default 3m0s) - -d, --verbose log debug output -``` - -### SEE ALSO - -* [cosign download](cosign_download.md) - Provides utilities for downloading artifacts and attached artifacts in a registry - diff --git a/test/e2e_attach_test.go b/test/e2e_attach_test.go index 7385da4f4a6..a551257ea48 100644 --- a/test/e2e_attach_test.go +++ b/test/e2e_attach_test.go @@ -39,7 +39,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/sigstore/cosign/v2/cmd/cosign/cli/attach" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/download" "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify" @@ -402,95 +401,3 @@ func TestUploadDownload(t *testing.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() {} - switch { - case testCase.bomType == attach.FileSignature: - sbomRef = mkfile(testCase.bom, td, t) - case testCase.bomType == attach.StdinSignature: - sbomRef = "-" - restoreStdin = mockStdin(testCase.bom, td, t) - default: - 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{}, options.RegistryExperimentalOptions{}, 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() - }) - } -} diff --git a/test/e2e_test.go b/test/e2e_test.go index 598d7faa5c0..71548211385 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -38,7 +38,6 @@ import ( "testing" "time" - "github.com/google/go-cmp/cmp" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -2023,81 +2022,6 @@ func TestSaveLoadAttestation(t *testing.T) { must(verifyAttestation.Exec(ctx, []string{imageDir}), t) } -func TestAttachSBOM(t *testing.T) { - td := t.TempDir() - err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) - if err != nil { - t.Fatal(err) - } - - repo, stop := reg(t) - defer stop() - ctx := context.Background() - - imgName := path.Join(repo, "sbom-image") - img, _, cleanup := mkimage(t, imgName) - defer cleanup() - - 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! - must(attach.SBOMCmd(ctx, options.RegistryOptions{}, options.RegistryExperimentalOptions{}, "./testdata/bom-go-mod.spdx", "spdx", imgName), t) - - 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) - } - - // Generate key pairs to sign the sbom - td1 := t.TempDir() - td2 := t.TempDir() - _, privKeyPath1, pubKeyPath1 := keypair(t, td1) - _, _, pubKeyPath2 := keypair(t, td2) - - // Verify should fail on a bad input - mustErr(verify(pubKeyPath1, imgName, true, nil, "sbom", false), t) - mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom", false), t) - - // Now sign the sbom with one key - ko1 := options.KeyOpts{ - KeyRef: privKeyPath1, - PassFunc: passFunc, - RekorURL: rekorURL, - } - so := options.SignOptions{ - Upload: true, - TlogUpload: true, - Attachment: "sbom", - } - must(sign.SignCmd(ro, ko1, so, []string{imgName}), t) - - // Now verify should work with that one, but not the other - must(verify(pubKeyPath1, imgName, true, nil, "sbom", false), t) - mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom", false), t) -} - func TestNoTlog(t *testing.T) { repo, stop := reg(t) defer stop()