diff --git a/commands/cmd.go b/commands/cmd.go index 95255d0..f0f8f48 100644 --- a/commands/cmd.go +++ b/commands/cmd.go @@ -28,10 +28,10 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli/command" + "github.com/docker/index-cli-plugin/internal" "github.com/docker/index-cli-plugin/query" "github.com/docker/index-cli-plugin/sbom" "github.com/docker/index-cli-plugin/types" - "github.com/docker/index-cli-plugin/util" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/moby/term" "github.com/pkg/errors" @@ -251,7 +251,7 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman } // see if the package comes in via the base image - s := util.StartInfoSpinner("Detecting base image", dockerCli.Out().IsTerminal()) + s := internal.StartInfoSpinner("Detecting base image", dockerCli.Out().IsTerminal()) defer s.Stop() baseImages, index, _ := query.Detect(img, true, workspace, apiKey) s.Stop() @@ -266,7 +266,7 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman } if baseImage != nil { - s := util.StartInfoSpinner("Finding alternative base images", dockerCli.Out().IsTerminal()) + s := internal.StartInfoSpinner("Finding alternative base images", dockerCli.Out().IsTerminal()) defer s.Stop() aBaseImage, _ := query.ForBaseImageWithoutCve(c.SourceId, baseImage.Repository.Name, img, workspace, apiKey) s.Stop() diff --git a/util/spinner.go b/internal/spinner.go similarity index 99% rename from util/spinner.go rename to internal/spinner.go index 4a6c91e..d6fff1b 100644 --- a/util/spinner.go +++ b/internal/spinner.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package util +package internal import ( "fmt" diff --git a/internal/util.go b/internal/util.go index bca6316..917934a 100644 --- a/internal/util.go +++ b/internal/util.go @@ -54,3 +54,19 @@ func Contains[K interface{}](slice []K, value K) bool { } return false } + +func UniqueBy[K interface{}](slice []K, by func(K) string) []K { + values := make(map[string]K) + for _, s := range slice { + k := by(s) + if _, ok := values[k]; !ok { + values[k] = s + } + } + + v := make([]K, 0, len(values)) + for _, value := range values { + v = append(v, value) + } + return v +} diff --git a/query/query.go b/query/query.go index 926463e..a02a294 100644 --- a/query/query.go +++ b/query/query.go @@ -86,7 +86,15 @@ func QueryCves(sb *types.Sbom, cve string, workspace string, apiKey string) (*[] } else { skill.Log.Infof("Detected %d vulnerabilities", len(result.Query.Data[0].Cves)) } - return &result.Query.Data[0].Cves, nil + fcves := internal.UniqueBy(result.Query.Data[0].Cves, func(cve types.Cve) string { + if cve.Cve != nil { + return fmt.Sprintf("%s %s", cve.Purl, cve.Cve.SourceId) + } else { + return fmt.Sprintf("%s %s", cve.Purl, cve.Advisory.SourceId) + } + }) + return &fcves, nil + } else { return nil, nil } diff --git a/registry/save.go b/registry/save.go index 2dab2b4..f161a26 100644 --- a/registry/save.go +++ b/registry/save.go @@ -25,7 +25,7 @@ import ( "github.com/atomist-skills/go-skill" "github.com/docker/cli/cli/command" - "github.com/docker/index-cli-plugin/util" + "github.com/docker/index-cli-plugin/internal" "github.com/dustin/go-humanize" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" @@ -88,14 +88,14 @@ func (c *ImageCache) StoreImage() error { var update v1.Update var err error var pp int64 - spinner := util.StartSpinner("info", "Copying image", c.cli.Out().IsTerminal()) + spinner := internal.StartSpinner("info", "Copying image", c.cli.Out().IsTerminal()) defer spinner.Stop() for { select { case update = <-u: p := 100 * update.Complete / update.Total if pp != p { - spinner.WithFields(util.Fields{ + spinner.WithFields(internal.Fields{ "event": "progress", "total": update.Total, "complete": update.Complete, diff --git a/sbom/index.go b/sbom/index.go index 5f075b8..565af10 100644 --- a/sbom/index.go +++ b/sbom/index.go @@ -30,7 +30,6 @@ import ( "github.com/docker/index-cli-plugin/query" "github.com/docker/index-cli-plugin/registry" "github.com/docker/index-cli-plugin/types" - "github.com/docker/index-cli-plugin/util" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/pkg/errors" @@ -102,7 +101,7 @@ func indexImage(cache *registry.ImageCache, cli command.Cli) (*types.Sbom, *v1.I lm := createLayerMapping(*cache.Image) skill.Log.Debugf("Created layer mapping") - s := util.StartSpinner("info", "Indexing", cli.Out().IsTerminal()) + s := internal.StartSpinner("info", "Indexing", cli.Out().IsTerminal()) defer s.Stop() trivyResultChan := make(chan types.IndexResult) syftResultChan := make(chan types.IndexResult) diff --git a/types/format.go b/types/format.go index 043788d..c488cc2 100644 --- a/types/format.go +++ b/types/format.go @@ -220,8 +220,8 @@ func FormatCve(sb *Sbom, c *Cve) { for i, l := range sb.Source.Image.Config.RootFS.DiffIDs { if l.String() == loc.DiffId { h := sb.Source.Image.Config.History[i] - fmt.Println(fmt.Sprintf("Instruction: %s", h.CreatedBy)) - fmt.Println(fmt.Sprintf("Layer %d: %s", i, loc.Digest)) + fmt.Println(formatCreatedBy(h.CreatedBy)) + fmt.Println(fmt.Sprintf("%d: %s", i, loc.Digest)) } } } @@ -343,3 +343,14 @@ func ToSeverityInt(cve Cve) int { return 0 } } + +func formatCreatedBy(createdBy string) string { + trim := func(prefix string) { + if strings.HasPrefix(createdBy, prefix) { + createdBy = strings.TrimSpace(createdBy[len(prefix):]) + } + } + trim("/bin/sh -c") + trim("#(nop)") + return createdBy +}