Skip to content

Commit

Permalink
feat: tree command utility (sigstore#1603)
Browse files Browse the repository at this point in the history
Fixes sigstore#1569

Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
Co-authored-by: Hector Fernandez <hectorj@gmail.com>
Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>

Co-authored-by: Hector Fernandez <hectorj@gmail.com>
  • Loading branch information
developer-guy and hectorj2f committed Mar 22, 2022
1 parent 45dbd39 commit be77bb0
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmd/cosign/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func New() *cobra.Command {
cmd.AddCommand(Attach())
cmd.AddCommand(Attest())
cmd.AddCommand(Clean())
cmd.AddCommand(Tree())
cmd.AddCommand(Completion())
cmd.AddCommand(Copy())
cmd.AddCommand(Dockerfile())
Expand Down
28 changes: 28 additions & 0 deletions cmd/cosign/cli/options/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2022 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 options

import "github.com/spf13/cobra"

type TreeOptions struct {
Registry RegistryOptions
CleanType string
}

var _ Interface = (*TreeOptions)(nil)

func (c *TreeOptions) AddFlags(cmd *cobra.Command) {
c.Registry.AddFlags(cmd)
}
167 changes: 167 additions & 0 deletions cmd/cosign/cli/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//
// 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 cli

import (
"context"
"fmt"
"os"
"strings"

v1 "github.com/google/go-containerregistry/pkg/v1"

"github.com/google/go-containerregistry/pkg/name"
"github.com/spf13/cobra"

"github.com/sigstore/cosign/cmd/cosign/cli/options"
ociremote "github.com/sigstore/cosign/pkg/oci/remote"
)

func Tree() *cobra.Command {
c := &options.TreeOptions{}

cmd := &cobra.Command{
Use: "tree",
Short: "Display supply chain security related artifacts for an image such as signatures, SBOMs and attestations",
Example: " cosign tree <IMAGE>",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return TreeCmd(cmd.Context(), c.Registry, args[0])
},
}

c.AddFlags(cmd)
return cmd
}

const (
SignatureTagSuffix = ".sig"
SBOMTagSuffix = ".sbom"
AttestationTagSuffix = ".att"
)

func TreeCmd(ctx context.Context, regOpts options.RegistryOptions, imageRef string) error {
scsaMap := map[name.Tag][]v1.Layer{}
ref, err := name.ParseReference(imageRef)
if err != nil {
return err
}

remoteOpts, err := regOpts.ClientOpts(ctx)
if err != nil {
return err
}
fmt.Fprintf(os.Stdout, "📦 Supply Chain Security Related artifacts for an image: %s\n", ref.String())

simg, err := ociremote.SignedEntity(ref, remoteOpts...)
if err != nil {
return err
}

registryClientOpts := regOpts.GetRegistryClientOpts(ctx)

attRef, err := ociremote.AttestationTag(ref, ociremote.WithRemoteOptions(registryClientOpts...))
if err != nil {
return err
}

atts, err := simg.Attestations()
var attLayers []v1.Layer
if err == nil {
layers, err := atts.Layers()
if err != nil {
return err
}
attLayers = append(attLayers, layers...)
}

scsaMap[attRef] = attLayers

sigRef, err := ociremote.SignatureTag(ref, ociremote.WithRemoteOptions(registryClientOpts...))
if err != nil {
return err
}

sigs, err := simg.Signatures()
var sigLayers []v1.Layer
if err == nil {
layers, err := sigs.Layers()
if err != nil {
return err
}
sigLayers = append(sigLayers, layers...)
}

scsaMap[sigRef] = sigLayers

sbomRef, err := ociremote.SBOMTag(ref, ociremote.WithRemoteOptions(registryClientOpts...))
if err != nil {
return err
}

sbombs, err := simg.Attachment("sbom")
var sbomLayers []v1.Layer
if err == nil {
layers, err := sbombs.Layers()
if err != nil {
return err
}
sbomLayers = append(sbomLayers, layers...)
}

scsaMap[sbomRef] = sbomLayers

if len(scsaMap) == 0 {
fmt.Fprintf(os.Stdout, "No Supply Chain Security Related Artifacts artifacts found for image %s\n, start creating one with simply running"+
"$ COSIGN_EXPERIMENTAL=1 cosign sign <img>", ref.String())
return nil
}

for t, k := range scsaMap {
switch {
case strings.HasSuffix(t.TagStr(), SignatureTagSuffix):
fmt.Fprintf(os.Stdout, "└── 🔐 Signatures for an image tag: %s\n", t.String())
case strings.HasSuffix(t.TagStr(), SBOMTagSuffix):
fmt.Fprintf(os.Stdout, "└── 📦 SBOMs for an image tag: %s\n", t.String())
case strings.HasSuffix(t.TagStr(), AttestationTagSuffix):
fmt.Fprintf(os.Stdout, "└── 💾 Attestations for an image tag: %s\n", t.String())
}

if err := printLayers(k); err != nil {
return err
}
}

return nil
}

func printLayers(layers []v1.Layer) error {
for i, l := range layers {
last := i == len(layers)-1
var sym string
if last {
sym = " └──"
} else {
sym = " ├──"
}
digest, err := l.Digest()
if err != nil {
return err
}
fmt.Printf("%s 🍒 %s\n", sym, digest)
}
return nil
}
1 change: 1 addition & 0 deletions doc/cosign.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions doc/cosign_tree.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions pkg/cosign/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ type AttestationPayload struct {
Signatures []Signatures `json:"signatures"`
}

const (
SignatureTagSuffix = ".sig"
SBOMTagSuffix = ".sbom"
AttestationTagSuffix = ".att"
)

const (
Signature = "signature"
SBOM = "sbom"
Expand Down

0 comments on commit be77bb0

Please sign in to comment.