Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 6e1ed91

Browse files
committed
Shuffle things around
Signed-off-by: Christian Dupuis <[email protected]>
1 parent b13049d commit 6e1ed91

File tree

7 files changed

+255
-130
lines changed

7 files changed

+255
-130
lines changed

commands/cmd.go

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
"github.com/docker/cli/cli"
2929
"github.com/docker/cli/cli-plugins/plugin"
3030
"github.com/docker/cli/cli/command"
31-
"github.com/docker/index-cli-plugin/internal"
31+
"github.com/docker/index-cli-plugin/format"
3232
"github.com/docker/index-cli-plugin/query"
3333
"github.com/docker/index-cli-plugin/sbom"
3434
"github.com/docker/index-cli-plugin/types"
@@ -225,67 +225,10 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
225225
return err
226226
}
227227

228-
if len(*cves) > 0 {
229-
for _, c := range *cves {
230-
types.FormatCve(sb, &c)
231-
232-
if !remediate {
233-
continue
234-
}
235-
236-
var remediation = make([]string, 0)
237-
layerIndex := -1
238-
for _, p := range sb.Artifacts {
239-
if p.Purl == c.Purl {
240-
loc := p.Locations[0]
241-
for i, l := range sb.Source.Image.Config.RootFS.DiffIDs {
242-
if l.String() == loc.DiffId && layerIndex < i {
243-
layerIndex = i
244-
}
245-
}
246-
247-
if rem := types.FormatPackageRemediation(p, c); rem != "" {
248-
remediation = append(remediation, rem)
249-
}
250-
}
251-
}
252-
253-
// see if the package comes in via the base image
254-
s := internal.StartInfoSpinner("Detecting base image", dockerCli.Out().IsTerminal())
255-
defer s.Stop()
256-
baseImages, index, _ := query.Detect(img, true, workspace, apiKey)
257-
s.Stop()
258-
var baseImage *types.Image
259-
if layerIndex <= index && baseImages != nil && len(*baseImages) > 0 {
260-
baseImage = &(*baseImages)[0]
261-
262-
fmt.Println("")
263-
fmt.Println("installed in base image")
264-
fmt.Println("")
265-
fmt.Println(types.FormatImage(baseImage))
266-
}
267-
268-
if baseImage != nil {
269-
s := internal.StartInfoSpinner("Finding alternative base images", dockerCli.Out().IsTerminal())
270-
defer s.Stop()
271-
aBaseImage, _ := query.ForBaseImageWithoutCve(c.SourceId, baseImage.Repository.Name, img, workspace, apiKey)
272-
s.Stop()
273-
if aBaseImage != nil && len(*aBaseImage) > 0 {
274-
e := []string{fmt.Sprintf("Update base image\n\nAlternative base images not vulnerable to %s", c.SourceId)}
275-
for _, a := range *aBaseImage {
276-
e = append(e, types.FormatImage(&a))
277-
}
278-
remediation = append(remediation, strings.Join(e, "\n\n"))
279-
}
280-
}
281-
282-
types.FormatRemediation(remediation)
283-
}
228+
format.Cves(cve, cves, img, sb, remediate, dockerCli, workspace, apiKey)
284229

230+
if len(*cves) > 0 {
285231
os.Exit(1)
286-
} else {
287-
fmt.Println(fmt.Sprintf("%s not detected", cve))
288-
os.Exit(0)
289232
}
290233
return nil
291234
},
@@ -303,7 +246,15 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
303246
},
304247
}
305248

306-
cmd.AddCommand(loginCommand, logoutCommand, sbomCommand, cveCommand, uploadCommand, diffCommand)
249+
watchCommand := &cobra.Command{
250+
Use: "watch",
251+
Short: "Watch for new images and trigger indexing",
252+
RunE: func(cmd *cobra.Command, args []string) error {
253+
return sbom.WatchImages(dockerCli)
254+
},
255+
}
256+
257+
cmd.AddCommand(loginCommand, logoutCommand, sbomCommand, cveCommand, uploadCommand, diffCommand, watchCommand)
307258
return cmd
308259
}
309260

format/cve.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright © 2022 Docker, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package format
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
"github.com/docker/cli/cli/command"
24+
"github.com/docker/index-cli-plugin/internal"
25+
"github.com/docker/index-cli-plugin/query"
26+
"github.com/docker/index-cli-plugin/types"
27+
v1 "github.com/google/go-containerregistry/pkg/v1"
28+
)
29+
30+
func Cves(cve string, cves *[]types.Cve, img *v1.Image, sb *types.Sbom, remediate bool, dockerCli command.Cli, workspace string, apiKey string) {
31+
if len(*cves) > 0 {
32+
for _, c := range *cves {
33+
Cve(sb, &c)
34+
35+
if !remediate {
36+
continue
37+
}
38+
39+
var remediation = make([]string, 0)
40+
layerIndex := -1
41+
for _, p := range sb.Artifacts {
42+
if p.Purl == c.Purl {
43+
loc := p.Locations[0]
44+
for i, l := range sb.Source.Image.Config.RootFS.DiffIDs {
45+
if l.String() == loc.DiffId && layerIndex < i {
46+
layerIndex = i
47+
}
48+
}
49+
50+
if rem := PackageRemediation(p, c); rem != "" {
51+
remediation = append(remediation, rem)
52+
}
53+
}
54+
}
55+
56+
// see if the package comes in via the base image
57+
s := internal.StartInfoSpinner("Detecting base image", dockerCli.Out().IsTerminal())
58+
defer s.Stop()
59+
baseImages, index, _ := query.Detect(img, true, workspace, apiKey)
60+
s.Stop()
61+
var baseImage *types.Image
62+
if layerIndex <= index && baseImages != nil && len(*baseImages) > 0 {
63+
baseImage = &(*baseImages)[0]
64+
65+
fmt.Println("")
66+
fmt.Println("installed in base image")
67+
fmt.Println("")
68+
fmt.Println(Image(baseImage, true))
69+
}
70+
71+
if baseImage != nil {
72+
s := internal.StartInfoSpinner("Finding alternative base images", dockerCli.Out().IsTerminal())
73+
defer s.Stop()
74+
aBaseImage, _ := query.ForBaseImageWithoutCve(c.SourceId, baseImage.Repository.Name, img, workspace, apiKey)
75+
s.Stop()
76+
77+
if aBaseImage != nil && len(*aBaseImage) > 0 {
78+
// attempt to filter the list to only include tags we know about
79+
mBaseImages := make([]types.Image, 0)
80+
for _, bi := range *aBaseImage {
81+
bTags := types.Tags(&bi)
82+
for _, t := range baseImage.Tags {
83+
if internal.Contains(bTags, t) {
84+
mBaseImages = append(mBaseImages, bi)
85+
break
86+
}
87+
}
88+
}
89+
90+
e := []string{fmt.Sprintf("Update base image\n\nAlternative base images not vulnerable to %s", c.SourceId)}
91+
if len(mBaseImages) > 0 {
92+
for _, a := range mBaseImages {
93+
e = append(e, Image(&a, false))
94+
}
95+
} else {
96+
for _, a := range *aBaseImage {
97+
e = append(e, Image(&a, false))
98+
}
99+
}
100+
remediation = append(remediation, strings.Join(e, "\n\n"))
101+
}
102+
}
103+
Remediation(remediation)
104+
}
105+
} else {
106+
fmt.Println(fmt.Sprintf("%s not detected", cve))
107+
}
108+
}

types/format.go renamed to format/format.go

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17-
package types
17+
package format
1818

1919
import (
2020
"fmt"
21-
"sort"
2221
"strconv"
2322
"strings"
2423

24+
"github.com/docker/index-cli-plugin/internal"
25+
"github.com/docker/index-cli-plugin/types"
2526
"github.com/gookit/color"
2627
"github.com/xeonx/timeago"
2728
)
@@ -64,22 +65,26 @@ func init() {
6465
}
6566
}
6667

67-
func FormatImage(image *Image) string {
68+
func Image(image *types.Image, imageTags bool) string {
6869
e := ""
6970
if image.Repository.Host != "hub.docker.com" {
7071
e += image.Repository.Host + "/"
7172
}
7273
e += image.Repository.Name
7374
e = defaultColors.green.Sprintf(e)
74-
if tags := Tags(image); len(tags) > 0 {
75+
tags := types.Tags(image)
76+
if imageTags {
77+
tags = types.ImageTags(image)
78+
}
79+
if len(tags) > 0 {
7580
for i, t := range tags {
7681
tags[i] = defaultColors.cyan.Sprintf(t)
7782
}
7883
e += ":" + strings.Join(tags, ", ")
7984
}
8085
if oc := officialContent(image); oc != "" {
8186
e += " " + defaultColors.blue.Sprintf(oc)
82-
if st := SupportedTag(image); st != "" {
87+
if st := types.SupportedTag(image); st != "" {
8388
e += " " + defaultColors.red.Sprintf(st)
8489
}
8590
}
@@ -97,7 +102,7 @@ func FormatImage(image *Image) string {
97102
return e
98103
}
99104

100-
func officialContent(image *Image) string {
105+
func officialContent(image *types.Image) string {
101106
switch image.Repository.Badge {
102107
case "open_source":
103108
return " Sponsored OSS "
@@ -111,60 +116,19 @@ func officialContent(image *Image) string {
111116
return ""
112117
}
113118

114-
func Tags(image *Image) []string {
115-
currentTags := make([]string, 0)
116-
for _, tag := range image.Tag {
117-
currentTags = append(currentTags, tag.Name)
118-
}
119-
for _, manifestList := range image.ManifestList {
120-
for _, tag := range manifestList.Tags {
121-
currentTags = append(currentTags, tag.Name)
122-
}
123-
}
124-
sort.Slice(currentTags, func(i, j int) bool {
125-
return len(currentTags[i]) < len(currentTags[j])
126-
})
127-
return currentTags
128-
}
129-
130-
func SupportedTag(image *Image) string {
131-
if tagCount := len(image.Repository.SupportedTags); tagCount > 0 {
132-
unsupportedTags := make([]string, 0)
133-
for _, tag := range image.Tags {
134-
if !contains(image.Repository.SupportedTags, tag) {
135-
unsupportedTags = append(unsupportedTags, tag)
136-
}
137-
}
138-
if len(unsupportedTags) == len(image.Tags) {
139-
return " unsupported tag "
140-
}
141-
}
142-
return ""
143-
}
144-
145-
func CurrentTag(image *Image) string {
146-
currentTags := Tags(image)
119+
func CurrentTag(image *types.Image) string {
120+
currentTags := types.Tags(image)
147121
if len(currentTags) > 0 {
148122
for _, tag := range image.Tags {
149-
if contains(currentTags, tag) {
123+
if internal.Contains(currentTags, tag) {
150124
return ""
151125
}
152126
}
153127
}
154128
return " tag moved "
155129
}
156130

157-
func contains(s []string, str string) bool {
158-
for _, v := range s {
159-
if v == str {
160-
return true
161-
}
162-
}
163-
164-
return false
165-
}
166-
167-
func RenderCommit(image *Image) string {
131+
func RenderCommit(image *types.Image) string {
168132
if image.TeamId == "A11PU8L1C" {
169133
return fmt.Sprintf("https://dso.docker.com/images/%s/digests/%s", image.Repository.Name, image.Digest)
170134
} else if image.Commit.Sha != "" {
@@ -177,7 +141,7 @@ func RenderCommit(image *Image) string {
177141
return ""
178142
}
179143

180-
func RenderVulnerabilities(image *Image) string {
144+
func RenderVulnerabilities(image *types.Image) string {
181145
if len(image.Report) > 0 {
182146
report := image.Report[0]
183147
if report.Total == -1 {
@@ -203,7 +167,7 @@ func RenderVulnerabilities(image *Image) string {
203167
return ""
204168
}
205169

206-
func FormatCve(sb *Sbom, c *Cve) {
170+
func Cve(sb *types.Sbom, c *types.Cve) {
207171
sourceId := c.SourceId
208172
if c.Cve != nil {
209173
sourceId = c.Cve.SourceId
@@ -228,7 +192,7 @@ func FormatCve(sb *Sbom, c *Cve) {
228192
}
229193
}
230194

231-
func FormatRemediation(remediation []string) {
195+
func Remediation(remediation []string) {
232196
if len(remediation) > 0 {
233197
fmt.Println("")
234198
fmt.Println(defaultColors.underline.Sprintf("Suggested remediation"))
@@ -238,8 +202,8 @@ func FormatRemediation(remediation []string) {
238202
}
239203
}
240204

241-
func FormatPackageRemediation(p Package, c Cve) string {
242-
purl, _ := ToPackageUrl(p.Purl)
205+
func PackageRemediation(p types.Package, c types.Cve) string {
206+
purl, _ := types.ToPackageUrl(p.Purl)
243207
if c.FixedBy != "not fixed" {
244208
switch purl.Type {
245209
case "alpine":
@@ -298,8 +262,8 @@ func ColorizeSeverity(severity string) string {
298262
}
299263
}
300264

301-
func ToSeverity(cve Cve) string {
302-
findSeverity := func(adv *Advisory) (string, bool) {
265+
func ToSeverity(cve types.Cve) string {
266+
findSeverity := func(adv *types.Advisory) (string, bool) {
303267
if adv == nil {
304268
return "", false
305269
}
@@ -328,7 +292,7 @@ func ToSeverity(cve Cve) string {
328292
return "IN TRIAGE"
329293
}
330294

331-
func ToSeverityInt(cve Cve) int {
295+
func ToSeverityInt(cve types.Cve) int {
332296
severity := ToSeverity(cve)
333297
switch severity {
334298
case "CRITICAL":

0 commit comments

Comments
 (0)