fix(zli): Improve zli CVE diff output (#3994)

* fix(cli): improve zli CVE diff output

Signed-off-by: Akash Kumar <meakash7902@gmail.com>

* test(api): avoid TestRoutes port collision

Signed-off-by: Akash Kumar <meakash7902@gmail.com>

* test(cli): cover CVE diff formatting helpers

Signed-off-by: Akash Kumar <meakash7902@gmail.com>

* test(search): remove redundant test case copy

Signed-off-by: Akash Kumar <meakash7902@gmail.com>

---------

Signed-off-by: Akash Kumar <meakash7902@gmail.com>
This commit is contained in:
Akash Kumar
2026-04-27 00:55:10 +05:30
committed by GitHub
parent 934b22d124
commit 8905b48bb7
8 changed files with 191 additions and 11 deletions
+17
View File
@@ -386,6 +386,7 @@ func TestCVEDiffList(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(str, ShouldContainSubstring, "CVEs in image repo:image that are not in image repo:base-image")
So(str, ShouldContainSubstring, "CVE3")
So(str, ShouldNotContainSubstring, "CVE1")
So(str, ShouldNotContainSubstring, "CVE2")
@@ -406,6 +407,22 @@ func TestCVEDiffList(t *testing.T) {
cveCmd.SetArgs(args)
So(cveCmd.Execute(), ShouldNotBeNil)
})
Convey("Minuend image not found includes image name", func() {
args := []string{"diff", "repo:missing-image", "repo:base-image", "--config", "cvetest"}
cveCmd.SetArgs(args)
err := cveCmd.Execute()
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "minuend image repo:missing-image")
So(err.Error(), ShouldContainSubstring, "image not found")
})
Convey("Subtrahend image not found includes image name", func() {
args := []string{"diff", "repo:image", "repo:missing-base", "--config", "cvetest"}
cveCmd.SetArgs(args)
err := cveCmd.Execute()
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "subtrahend image repo:missing-base")
So(err.Error(), ShouldContainSubstring, "image not found")
})
Convey("Second input is arch but not enough args", func() {
args := []string{"diff", "repo:base-image", "linux/amd64", "--config", "cvetest"}
cveCmd.SetArgs(args)
+22
View File
@@ -297,6 +297,9 @@ func SearchCVEDiffList(config SearchConfig, minuend, subtrahend ImageIdentifier)
var builder strings.Builder
if config.OutputFormat == defaultOutputFormat || config.OutputFormat == "" {
fmt.Fprintf(config.ResultWriter, "CVEs in image %s that are not in image %s\n\n",
formatImageIdentifier(cveDiffResult.Minuend), formatImageIdentifier(cveDiffResult.Subtrahend))
imageCVESummary := result.Data.CVEListForImage.Summary
statsStr := fmt.Sprintf("CRITICAL %d, HIGH %d, MEDIUM %d, LOW %d, UNKNOWN %d, TOTAL %d\n\n",
@@ -319,6 +322,25 @@ func SearchCVEDiffList(config SearchConfig, minuend, subtrahend ImageIdentifier)
return nil
}
func formatImageIdentifier(image ImageIdentifier) string {
if image.Repo == "" {
return "unknown image"
}
name := image.Repo
if image.Tag != "" {
name += ":" + image.Tag
} else if image.Digest != "" {
name += "@" + image.Digest
}
if image.Platform != nil && image.Platform.Os != "" && image.Platform.Arch != "" {
name += fmt.Sprintf(" (%s/%s)", image.Platform.Os, image.Platform.Arch)
}
return name
}
func SearchImagesByCVEIDGQL(config SearchConfig, repo, cveid string) error {
username, password := getUsernameAndPassword(config.User)
ctx, cancel := context.WithCancel(context.Background())
@@ -561,6 +561,58 @@ func TestSearchCVEForImageGQL(t *testing.T) {
})
}
func TestFormatImageIdentifier(t *testing.T) {
Convey("Format image identifier", t, func() {
testCases := []struct {
name string
image ImageIdentifier
expected string
}{
{
name: "unknown image",
image: ImageIdentifier{},
expected: "unknown image",
},
{
name: "tag reference",
image: ImageIdentifier{Repo: "repo", Tag: "tag"},
expected: "repo:tag",
},
{
name: "digest reference",
image: ImageIdentifier{Repo: "repo", Digest: "sha256:123"},
expected: "repo@sha256:123",
},
{
name: "tag reference with platform",
image: ImageIdentifier{
Repo: "repo",
Tag: "tag",
Platform: &osArch{Os: "linux", Arch: "amd64"},
},
expected: "repo:tag (linux/amd64)",
},
{
name: "partial platform is omitted",
image: ImageIdentifier{
Repo: "repo",
Tag: "tag",
Platform: &osArch{Os: "linux"},
},
expected: "repo:tag",
},
}
for _, testCase := range testCases {
testCase := testCase
Convey(testCase.name, func() {
So(formatImageIdentifier(testCase.image), ShouldEqual, testCase.expected)
})
}
})
}
func TestSearchImagesByCVEIDGQL(t *testing.T) {
Convey("SearchImagesByCVEIDGQL", t, func() {
buff := bytes.NewBufferString("")
+2 -2
View File
@@ -168,8 +168,8 @@ func (service *searchService) getCVEDiffListGQL(ctx context.Context, config Sear
query := fmt.Sprintf(`
{
CVEDiffListForImages( minuend: %s, subtrahend: %s ) {
Minuend {Repo Tag}
Subtrahend {Repo Tag}
Minuend {Repo Tag Digest Platform {Os Arch}}
Subtrahend {Repo Tag Digest Platform {Os Arch}}
CVEList {
Id Title Description Severity Reference
PackageList {Name InstalledVersion FixedVersion}