feat(CVE): add CVE severity counters to returned images and CVE list calls (#2131)

For CLI output is similar to:

CRITICAL 0, HIGH 1, MEDIUM 1, LOW 0, UNKNOWN 0, TOTAL 2

ID                SEVERITY  TITLE
CVE-2023-0464     HIGH      openssl: Denial of service by excessive resou...
CVE-2023-0465     MEDIUM    openssl: Invalid certificate policies in leaf...

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2023-12-13 19:16:31 +02:00 committed by GitHub
parent dbb1c3519f
commit 18aa975ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1077 additions and 135 deletions

View File

@ -141,7 +141,8 @@ func TestSearchCVECmd(t *testing.T) {
err := cmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual, "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
So(strings.TrimSpace(str), ShouldEqual, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+
"ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
So(err, ShouldBeNil)
})
@ -207,7 +208,8 @@ func TestSearchCVECmd(t *testing.T) {
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual, "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
So(strings.TrimSpace(str), ShouldEqual, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+
"ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
So(err, ShouldBeNil)
})
@ -225,7 +227,9 @@ func TestSearchCVECmd(t *testing.T) {
So(buff.String(), ShouldEqual, `{"Tag":"dummyImageName:tag","CVEList":`+
`[{"Id":"dummyCVEID","Severity":"HIGH","Title":"Title of that CVE",`+
`"Description":"Description of the CVE","PackageList":[{"Name":"packagename",`+
`"InstalledVersion":"installedver","FixedVersion":"fixedver"}]}]}`+"\n")
`"InstalledVersion":"installedver","FixedVersion":"fixedver"}]}],"Summary":`+
`{"maxSeverity":"HIGH","unknownCount":0,"lowCount":0,"mediumCount":0,"highCount":1,`+
`"criticalCount":0,"count":1}}`+"\n")
So(err, ShouldBeNil)
})
@ -243,7 +247,8 @@ func TestSearchCVECmd(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual, `--- tag: dummyImageName:tag cvelist: - id: dummyCVEID`+
` severity: HIGH title: Title of that CVE description: Description of the CVE packagelist: `+
`- name: packagename installedversion: installedver fixedversion: fixedver`)
`- name: packagename installedversion: installedver fixedversion: fixedver `+
`summary: maxseverity: HIGH unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 1 criticalcount: 0 count: 1`)
So(err, ShouldBeNil)
})
Convey("Test CVE by image name - invalid format", t, func() {
@ -508,6 +513,7 @@ func TestCVECommandGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1")
So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
})

View File

@ -634,7 +634,7 @@ func TestCVESort(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
@ -652,7 +652,7 @@ func TestCVESort(t *testing.T) {
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+
"CVE-2023-1255 LOW Input buffer over-read in AES-XTS implementat... "+
"CVE-2023-2650 MEDIUM Possible DoS translating ASN.1 object identif... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+
@ -670,7 +670,7 @@ func TestCVESort(t *testing.T) {
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldResemble,
"ID SEVERITY TITLE "+
"CRITICAL 1, HIGH 1, MEDIUM 2, LOW 1, UNKNOWN 0, TOTAL 5 ID SEVERITY TITLE "+
"CVE-2023-3817 MEDIUM Excessive time spent checking DH q parameter ... "+
"CVE-2023-3446 CRITICAL Excessive time spent checking DH keys and par... "+
"CVE-2023-2975 HIGH AES-SIV cipher implementation contains a bug ... "+

View File

@ -384,11 +384,13 @@ func TestOutputFormat(t *testing.T) {
`"lastUpdated":"0001-01-01T00:00:00Z","size":"123445","platform":{"os":"os","arch":"arch",`+
`"variant":""},"isSigned":false,"downloadCount":0,`+
`"layers":[{"size":"","digest":"sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6",`+
`"score":0}],"history":null,"vulnerabilities":{"maxSeverity":"","count":0},`+
`"score":0}],"history":null,"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,`+
`"mediumCount":0,"highCount":0,"criticalCount":0,"count":0},`+
`"referrers":null,"artifactType":"","signatureInfo":null}],"size":"123445",`+
`"downloadCount":0,"lastUpdated":"0001-01-01T00:00:00Z","description":"","isSigned":false,"licenses":"",`+
`"labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",`+
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}`+"\n")
`"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,"mediumCount":0,"highCount":0,`+
`"criticalCount":0,"count":0},"referrers":null,"signatureInfo":null}`+"\n")
So(err, ShouldBeNil)
})
@ -415,10 +417,13 @@ func TestOutputFormat(t *testing.T) {
`lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+
`issigned: false downloadcount: 0 layers: - size: "" `+
`digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+
`history: [] vulnerabilities: maxseverity: "" `+
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 `+
`referrers: [] artifacttype: "" `+
`signatureinfo: [] size: "123445" downloadcount: 0 `+
`lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+
`title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: "" `+
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 `+
`count: 0 referrers: [] signatureinfo: []`,
)
So(err, ShouldBeNil)
@ -449,11 +454,13 @@ func TestOutputFormat(t *testing.T) {
`lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+
`issigned: false downloadcount: 0 layers: - size: "" `+
`digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+
`history: [] vulnerabilities: maxseverity: "" unknowncount: 0 lowcount: 0 mediumcount: 0 `+
`highcount: 0 criticalcount: 0 count: 0 referrers: [] artifacttype: "" `+
`signatureinfo: [] size: "123445" downloadcount: 0 `+
`lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+
`title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: `+
`"" count: 0 referrers: [] signatureinfo: []`,
`title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: "" `+
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 `+
`count: 0 referrers: [] signatureinfo: []`,
)
So(err, ShouldBeNil)
})
@ -783,6 +790,7 @@ func TestImagesCommandGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1")
So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
})
@ -1342,6 +1350,15 @@ func (service mockService) getCveByImageGQL(ctx context.Context, config SearchCo
},
},
},
Summary: common.ImageVulnerabilitySummary{
Count: 1,
UnknownCount: 0,
LowCount: 0,
MediumCount: 0,
HighCount: 1,
CriticalCount: 0,
MaxSeverity: "HIGH",
},
},
}

View File

@ -379,11 +379,13 @@ func TestOutputFormatGQL(t *testing.T) {
`"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},` +
`"history":null,"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,"mediumCount":0,` +
`"highCount":0,"criticalCount":0,"count":0},` +
`"referrers":null,"artifactType":"","signatureInfo":null}],` +
`"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}` + "\n" +
`"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,"mediumCount":0,` +
`"highCount":0,"criticalCount":0,"count":0},"referrers":null,"signatureInfo":null}` + "\n" +
`{"repoName":"repo7","tag":"test:2.0",` +
`"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"mediaType":"application/vnd.oci.image.manifest.v1+json",` +
@ -392,11 +394,13 @@ func TestOutputFormatGQL(t *testing.T) {
`"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},` +
`"history":null,"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,"mediumCount":0,` +
`"highCount":0,"criticalCount":0,"count":0},` +
`"referrers":null,"artifactType":"","signatureInfo":null}],` +
`"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}` + "\n"
`"vulnerabilities":{"maxSeverity":"","unknownCount":0,"lowCount":0,"mediumCount":0,` +
`"highCount":0,"criticalCount":0,"count":0},"referrers":null,"signatureInfo":null}` + "\n"
// Output is supposed to be in json lines format, keep all spaces as is for verification
So(buff.String(), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
@ -424,10 +428,13 @@ func TestOutputFormatGQL(t *testing.T) {
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: [] ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] signatureinfo: [] ` +
`--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
@ -437,10 +444,13 @@ func TestOutputFormatGQL(t *testing.T) {
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: []`
`authors: "" vendor: "" vulnerabilities: maxseverity: "" ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] signatureinfo: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
})
@ -467,11 +477,13 @@ func TestOutputFormatGQL(t *testing.T) {
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] signatureinfo: [] ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] signatureinfo: [] ` +
`--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
@ -481,10 +493,13 @@ func TestOutputFormatGQL(t *testing.T) {
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: []`
`authors: "" vendor: "" vulnerabilities: maxseverity: "" ` +
`unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 0 criticalcount: 0 count: 0 ` +
`referrers: [] signatureinfo: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
})

View File

@ -245,6 +245,14 @@ func SearchCVEForImageGQL(config SearchConfig, image, searchedCveID string) erro
var builder strings.Builder
if config.OutputFormat == defaultOutputFormat || config.OutputFormat == "" {
imageCVESummary := cveList.Data.CVEListForImage.Summary
statsStr := fmt.Sprintf("CRITICAL %d, HIGH %d, MEDIUM %d, LOW %d, UNKNOWN %d, TOTAL %d\n\n",
imageCVESummary.CriticalCount, imageCVESummary.HighCount, imageCVESummary.MediumCount,
imageCVESummary.LowCount, imageCVESummary.UnknownCount, imageCVESummary.Count)
fmt.Fprint(config.ResultWriter, statsStr)
printCVETableHeader(&builder)
fmt.Fprint(config.ResultWriter, builder.String())
}

View File

@ -346,6 +346,15 @@ func TestSearchCVEForImageGQL(t *testing.T) {
},
},
},
Summary: common.ImageVulnerabilitySummary{
Count: 1,
UnknownCount: 0,
LowCount: 0,
MediumCount: 0,
HighCount: 1,
CriticalCount: 0,
MaxSeverity: "HIGH",
},
},
},
}, nil
@ -357,6 +366,7 @@ func TestSearchCVEForImageGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1")
So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
})

View File

@ -305,10 +305,14 @@ func (service searchService) getCveByImageGQL(ctx context.Context, config Search
query := fmt.Sprintf(`
{
CVEListForImage (image:"%s", searchedCVE:"%s", requestedPage: {sortBy: %s}) {
Tag CVEList {
Tag
CVEList {
Id Title Severity Description
PackageList {Name InstalledVersion FixedVersion}
}
Summary {
Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity
}
}
}`, imageName, searchedCVE, Flag2SortCriteria(config.SortBy))
result := &cveResult{}
@ -743,8 +747,9 @@ type cve struct {
//nolint:tagliatelle // graphQL schema
type cveListForImage struct {
Tag string `json:"Tag"`
CVEList []cve `json:"CVEList"`
Tag string `json:"Tag"`
CVEList []cve `json:"CVEList"`
Summary common.ImageVulnerabilitySummary `json:"Summary"`
}
//nolint:tagliatelle // graphQL schema

View File

@ -84,8 +84,13 @@ type Platform struct {
}
type ImageVulnerabilitySummary struct {
MaxSeverity string `json:"maxSeverity"`
Count int `json:"count"`
MaxSeverity string `json:"maxSeverity"`
UnknownCount int `json:"unknownCount"`
LowCount int `json:"lowCount"`
MediumCount int `json:"mediumCount"`
HighCount int `json:"highCount"`
CriticalCount int `json:"criticalCount"`
Count int `json:"count"`
}
type LayerSummary struct {

View File

@ -88,6 +88,11 @@ func TestCVEConvert(t *testing.T) {
So(imageSummary.Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.LowCount, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.HighCount, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
So(graphql.GetErrors(ctx), ShouldBeNil)
@ -102,20 +107,35 @@ func TestCVEConvert(t *testing.T) {
GetCVESummaryForImageMediaFn: func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
Count: 30,
UnknownCount: 1,
LowCount: 2,
MediumCount: 3,
HighCount: 10,
CriticalCount: 14,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(imageSummary.Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 1)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 30)
So(*imageSummary.Vulnerabilities.UnknownCount, ShouldEqual, 1)
So(*imageSummary.Vulnerabilities.LowCount, ShouldEqual, 2)
So(*imageSummary.Vulnerabilities.MediumCount, ShouldEqual, 3)
So(*imageSummary.Vulnerabilities.HighCount, ShouldEqual, 10)
So(*imageSummary.Vulnerabilities.CriticalCount, ShouldEqual, 14)
So(*imageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
So(graphql.GetErrors(ctx), ShouldBeNil)
So(len(imageSummary.Manifests), ShouldEqual, 1)
So(imageSummary.Manifests[0].Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Manifests[0].Vulnerabilities.Count, ShouldEqual, 1)
So(*imageSummary.Manifests[0].Vulnerabilities.Count, ShouldEqual, 30)
So(*imageSummary.Manifests[0].Vulnerabilities.UnknownCount, ShouldEqual, 1)
So(*imageSummary.Manifests[0].Vulnerabilities.LowCount, ShouldEqual, 2)
So(*imageSummary.Manifests[0].Vulnerabilities.MediumCount, ShouldEqual, 3)
So(*imageSummary.Manifests[0].Vulnerabilities.HighCount, ShouldEqual, 10)
So(*imageSummary.Manifests[0].Vulnerabilities.CriticalCount, ShouldEqual, 14)
So(*imageSummary.Manifests[0].Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
imageSummary.Vulnerabilities = nil
@ -152,8 +172,13 @@ func TestCVEConvert(t *testing.T) {
GetCVESummaryForImageMediaFn: func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
Count: 30,
UnknownCount: 1,
LowCount: 2,
MediumCount: 3,
HighCount: 10,
CriticalCount: 14,
MaxSeverity: "HIGH",
}, nil
},
},
@ -182,15 +207,25 @@ func TestCVEConvert(t *testing.T) {
GetCVESummaryForImageMediaFn: func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
Count: 30,
UnknownCount: 1,
LowCount: 2,
MediumCount: 3,
HighCount: 10,
CriticalCount: 14,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(repoSummary.NewestImage.Vulnerabilities, ShouldNotBeNil)
So(*repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 1)
So(*repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 30)
So(*repoSummary.NewestImage.Vulnerabilities.UnknownCount, ShouldEqual, 1)
So(*repoSummary.NewestImage.Vulnerabilities.LowCount, ShouldEqual, 2)
So(*repoSummary.NewestImage.Vulnerabilities.MediumCount, ShouldEqual, 3)
So(*repoSummary.NewestImage.Vulnerabilities.HighCount, ShouldEqual, 10)
So(*repoSummary.NewestImage.Vulnerabilities.CriticalCount, ShouldEqual, 14)
So(*repoSummary.NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
So(graphql.GetErrors(ctx), ShouldBeNil)
})
@ -251,15 +286,25 @@ func TestCVEConvert(t *testing.T) {
GetCVESummaryForImageMediaFn: func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
Count: 30,
UnknownCount: 1,
LowCount: 2,
MediumCount: 3,
HighCount: 10,
CriticalCount: 14,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(manifestSummary.Vulnerabilities, ShouldNotBeNil)
So(*manifestSummary.Vulnerabilities.Count, ShouldEqual, 1)
So(*manifestSummary.Vulnerabilities.Count, ShouldEqual, 30)
So(*manifestSummary.Vulnerabilities.UnknownCount, ShouldEqual, 1)
So(*manifestSummary.Vulnerabilities.LowCount, ShouldEqual, 2)
So(*manifestSummary.Vulnerabilities.MediumCount, ShouldEqual, 3)
So(*manifestSummary.Vulnerabilities.HighCount, ShouldEqual, 10)
So(*manifestSummary.Vulnerabilities.CriticalCount, ShouldEqual, 14)
So(*manifestSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
manifestSummary.Vulnerabilities = nil

View File

@ -38,8 +38,13 @@ func updateImageSummaryVulnerabilities(
imageCveSummary := cvemodel.ImageCVESummary{}
imageSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
MaxSeverity: &imageCveSummary.MaxSeverity,
UnknownCount: &imageCveSummary.UnknownCount,
LowCount: &imageCveSummary.LowCount,
MediumCount: &imageCveSummary.MediumCount,
HighCount: &imageCveSummary.HighCount,
CriticalCount: &imageCveSummary.CriticalCount,
Count: &imageCveSummary.Count,
}
// Check if vulnerability scanning is disabled
@ -61,6 +66,11 @@ func updateImageSummaryVulnerabilities(
}
imageSummary.Vulnerabilities.MaxSeverity = &imageCveSummary.MaxSeverity
imageSummary.Vulnerabilities.UnknownCount = &imageCveSummary.UnknownCount
imageSummary.Vulnerabilities.LowCount = &imageCveSummary.LowCount
imageSummary.Vulnerabilities.MediumCount = &imageCveSummary.MediumCount
imageSummary.Vulnerabilities.HighCount = &imageCveSummary.HighCount
imageSummary.Vulnerabilities.CriticalCount = &imageCveSummary.CriticalCount
imageSummary.Vulnerabilities.Count = &imageCveSummary.Count
for _, manifestSummary := range imageSummary.Manifests {
@ -82,8 +92,13 @@ func updateManifestSummaryVulnerabilities(
imageCveSummary := cvemodel.ImageCVESummary{}
manifestSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
MaxSeverity: &imageCveSummary.MaxSeverity,
UnknownCount: &imageCveSummary.UnknownCount,
LowCount: &imageCveSummary.LowCount,
MediumCount: &imageCveSummary.MediumCount,
HighCount: &imageCveSummary.HighCount,
CriticalCount: &imageCveSummary.CriticalCount,
Count: &imageCveSummary.Count,
}
// Check if vulnerability scanning is disabled
@ -105,5 +120,10 @@ func updateManifestSummaryVulnerabilities(
}
manifestSummary.Vulnerabilities.MaxSeverity = &imageCveSummary.MaxSeverity
manifestSummary.Vulnerabilities.UnknownCount = &imageCveSummary.UnknownCount
manifestSummary.Vulnerabilities.LowCount = &imageCveSummary.LowCount
manifestSummary.Vulnerabilities.MediumCount = &imageCveSummary.MediumCount
manifestSummary.Vulnerabilities.HighCount = &imageCveSummary.HighCount
manifestSummary.Vulnerabilities.CriticalCount = &imageCveSummary.CriticalCount
manifestSummary.Vulnerabilities.Count = &imageCveSummary.Count
}

View File

@ -23,8 +23,8 @@ type CveInfo interface {
GetImageListForCVE(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetImageListWithCVEFixed(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetCVEListForImage(ctx context.Context, repo, tag string, searchedCVE string, pageinput cvemodel.PageInput,
) ([]cvemodel.CVE, zcommon.PageInfo, error)
GetCVESummaryForImageMedia(ctx context.Context, repo, digest, mediaType string) (cvemodel.ImageCVESummary, error)
) ([]cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error)
GetCVESummaryForImageMedia(ctx context.Context, repo, digestStr, mediaType string) (cvemodel.ImageCVESummary, error)
}
type Scanner interface {
@ -352,75 +352,67 @@ func filterCVEList(cveMap map[string]cvemodel.CVE, searchedCVE string, pageFinde
func (cveinfo BaseCveInfo) GetCVEListForImage(ctx context.Context, repo, ref string, searchedCVE string,
pageInput cvemodel.PageInput,
) (
[]cvemodel.CVE, zcommon.PageInfo, error,
[]cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error,
) {
imageCVESummary := cvemodel.ImageCVESummary{
MaxSeverity: cvemodel.SeverityNotScanned,
}
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(repo, ref)
if !isValidImage {
cveinfo.Log.Debug().Str("image", repo+":"+ref).Err(err).Msg("image is not scanable")
return []cvemodel.CVE{}, zcommon.PageInfo{}, err
return []cvemodel.CVE{}, imageCVESummary, zcommon.PageInfo{}, err
}
image := zcommon.GetFullImageName(repo, ref)
cveMap, err := cveinfo.Scanner.ScanImage(ctx, image)
if err != nil {
return []cvemodel.CVE{}, zcommon.PageInfo{}, err
return []cvemodel.CVE{}, imageCVESummary, zcommon.PageInfo{}, err
}
imageCVESummary = initCVESummaryFromCVEMap(cveMap)
pageFinder, err := NewCvePageFinder(pageInput.Limit, pageInput.Offset, pageInput.SortBy)
if err != nil {
return []cvemodel.CVE{}, zcommon.PageInfo{}, err
return []cvemodel.CVE{}, imageCVESummary, zcommon.PageInfo{}, err
}
filterCVEList(cveMap, searchedCVE, pageFinder)
cveList, pageInfo := pageFinder.Page()
return cveList, pageInfo, nil
return cveList, imageCVESummary, pageInfo, nil
}
func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(ctx context.Context, repo, digest, mediaType string,
func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(ctx context.Context, repo, digestStr, mediaType string,
) (cvemodel.ImageCVESummary, error) {
// There are several cases, expected returned values below:
// not scanned yet - max severity "" - cve count 0 - no Errors
// not scannable - max severity "" - cve count 0 - has Errors
// scannable no issues found - max severity "NONE" - cve count 0 - no Errors
// scannable issues found - max severity from Scanner - cve count >0 - no Errors
imageCVESummary := cvemodel.ImageCVESummary{
Count: 0,
MaxSeverity: cvemodel.SeverityNotScanned,
}
// For this call we only look at the scanner cache, we skip the actual scanning to save time
if !cveinfo.Scanner.IsResultCached(digest) {
isValidImage, err := cveinfo.Scanner.IsImageMediaScannable(repo, digest, mediaType)
if !cveinfo.Scanner.IsResultCached(digestStr) {
isValidImage, err := cveinfo.Scanner.IsImageMediaScannable(repo, digestStr, mediaType)
if !isValidImage {
cveinfo.Log.Debug().Str("digest", digest).Str("mediaType", mediaType).
cveinfo.Log.Debug().Str("digest", digestStr).Str("mediaType", mediaType).
Err(err).Msg("image is not scannable")
}
// Counters are initialized with 0 by default
imageCVESummary := cvemodel.ImageCVESummary{
MaxSeverity: cvemodel.SeverityNotScanned,
}
return imageCVESummary, err
}
// We will make due with cached results
cveMap := cveinfo.Scanner.GetCachedResult(digest)
cveMap := cveinfo.Scanner.GetCachedResult(digestStr)
imageCVESummary.Count = len(cveMap)
if imageCVESummary.Count == 0 {
imageCVESummary.MaxSeverity = cvemodel.SeverityNone
return imageCVESummary, nil
}
imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
for _, cve := range cveMap {
if cvemodel.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity
}
}
return imageCVESummary, nil
return initCVESummaryFromCVEMap(cveMap), nil
}
func GetFixedTags(allTags, vulnerableTags []cvemodel.TagInfo) []cvemodel.TagInfo {
@ -517,3 +509,40 @@ func containsDescriptorInfo(slice []cvemodel.DescriptorInfo, descriptorInfo cvem
return false
}
func initCVESummaryFromCVEMap(cveMap map[string]cvemodel.CVE) cvemodel.ImageCVESummary {
// Counters are initialized with 0 by default
imageCVESummary := cvemodel.ImageCVESummary{
MaxSeverity: cvemodel.SeverityNotScanned,
}
imageCVESummary.Count = len(cveMap)
if imageCVESummary.Count == 0 {
imageCVESummary.MaxSeverity = cvemodel.SeverityNone
return imageCVESummary
}
imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
for _, cve := range cveMap {
switch cve.Severity {
case cvemodel.SeverityUnknown:
imageCVESummary.UnknownCount += 1
case cvemodel.SeverityLow:
imageCVESummary.LowCount += 1
case cvemodel.SeverityMedium:
imageCVESummary.MediumCount += 1
case cvemodel.SeverityHigh:
imageCVESummary.HighCount += 1
case cvemodel.SeverityCritical:
imageCVESummary.CriticalCount += 1
}
if cvemodel.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity
}
}
return imageCVESummary
}

View File

@ -754,6 +754,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
const repo5 = "repo5"
const repo6 = "repo6"
const repo7 = "repo7"
const repo8 = "repo8"
const repo100 = "repo100"
const repoMultiarch = "repoIndex"
@ -833,6 +834,13 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
err = metaDB.SetRepoReference(context.Background(), repo7, "1.0.0", image71.AsImageMeta())
So(err, ShouldBeNil)
// Create image with vulnerabilities of all severities
image81 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2020, 12, 1, 12, 0, 0, 0, time.UTC)}).Build()
err = metaDB.SetRepoReference(context.Background(), repo8, "1.0.0", image81.AsImageMeta())
So(err, ShouldBeNil)
// create multiarch image with vulnerabilities
multiarchImage := CreateRandomMultiarch()
@ -891,6 +899,10 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
image71Media := image71.ManifestDescriptor.MediaType
image71Name := repo7 + ":1.0.0"
imageMap[image71Name] = image71Digest
image81Digest := image81.ManifestDescriptor.Digest.String()
image81Media := image81.ManifestDescriptor.MediaType
image81Name := repo8 + ":1.0.0"
imageMap[image81Name] = image81Digest
indexDigest := multiarchImage.IndexDescriptor.Digest.String()
indexMedia := multiarchImage.IndexDescriptor.MediaType
indexName := repoMultiarch + ":tagIndex"
@ -1042,6 +1054,57 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
return result, nil
}
if repo == repo8 && ref == image81Digest {
result := map[string]cvemodel.CVE{
"CVE0": {
ID: "CVE0",
Severity: "UNKNOWN",
Title: "Title CVE0",
Description: "Description CVE0",
},
"CVE1": {
ID: "CVE1",
Severity: "MEDIUM",
Title: "Title CVE1",
Description: "Description CVE1",
},
"CVE2": {
ID: "CVE2",
Severity: "HIGH",
Title: "Title CVE2",
Description: "Description CVE2",
},
"CVE3": {
ID: "CVE3",
Severity: "LOW",
Title: "Title CVE3",
Description: "Description CVE3",
},
"CVE4": {
ID: "CVE4",
Severity: "CRITICAL",
Title: "Title CVE4",
Description: "Description CVE4",
},
"CVE5": {
ID: "CVE5",
Severity: "CRITICAL",
Title: "Title CVE5",
Description: "Description CVE5",
},
"CVE6": {
ID: "CVE6",
Severity: "LOW",
Title: "Title CVE6",
Description: "Description CVE6",
},
}
cache.Add(ref, result)
return result, nil
}
// By default the image has no vulnerabilities
result = map[string]cvemodel.CVE{}
cache.Add(ref, result)
@ -1130,14 +1193,21 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
ctx := context.Background()
// Image is found
cveList, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput)
cveList, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE1")
So(pageInfo.ItemCount, ShouldEqual, 1)
So(pageInfo.TotalCount, ShouldEqual, 1)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.0", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 3)
So(cveList[0].ID, ShouldEqual, "CVE2")
@ -1145,116 +1215,249 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveList[2].ID, ShouldEqual, "CVE3")
So(pageInfo.ItemCount, ShouldEqual, 3)
So(pageInfo.TotalCount, ShouldEqual, 3)
So(cveSummary.Count, ShouldEqual, 3)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "HIGH")
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.1", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.1", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 2)
So(cveList[0].ID, ShouldEqual, "CVE1")
So(cveList[1].ID, ShouldEqual, "CVE3")
So(pageInfo.ItemCount, ShouldEqual, 2)
So(pageInfo.TotalCount, ShouldEqual, 2)
So(cveSummary.Count, ShouldEqual, 2)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.1.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.1.0", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE3")
So(pageInfo.ItemCount, ShouldEqual, 1)
So(pageInfo.TotalCount, ShouldEqual, 1)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "LOW")
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo6, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo6, "1.0.0", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "NONE")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo8, "1.0.0", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 7)
So(pageInfo.ItemCount, ShouldEqual, 7)
So(pageInfo.TotalCount, ShouldEqual, 7)
So(cveSummary.Count, ShouldEqual, 7)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 2)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 2)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
// Image is multiarch
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repoMultiarch, "tagIndex", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repoMultiarch, "tagIndex", "", pageInput)
So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE1")
So(pageInfo.ItemCount, ShouldEqual, 1)
So(pageInfo.TotalCount, ShouldEqual, 1)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
// Image is not scannable
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo2, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo2, "1.0.0", "", pageInput)
So(err, ShouldEqual, zerr.ErrScanNotSupported)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Tag is not found
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo3, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo3, "1.0.0", "", pageInput)
So(err, ShouldEqual, zerr.ErrTagMetaNotFound)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Scan failed
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo7, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo7, "1.0.0", "", pageInput)
So(err, ShouldEqual, ErrFailedScan)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Tag is not found
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo-with-bad-tag-digest", "tag", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo-with-bad-tag-digest", "tag", "", pageInput)
So(err, ShouldEqual, zerr.ErrImageMetaNotFound)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Repo is not found
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo100, "1.0.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo100, "1.0.0", "", pageInput)
So(err, ShouldEqual, zerr.ErrRepoMetaNotFound)
So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// By this point the cache should already be pupulated by previous function calls
t.Log("\nTest GetCVESummaryForImage\n")
// Image is found
cveSummary, err := cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image11Digest, image11Media)
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image11Digest, image11Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image12Digest, image12Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 3)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "HIGH")
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image14Digest, image14Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 2)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image13Digest, image13Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "LOW")
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo6, image61Digest, image61Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "NONE")
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo8, image81Digest, image81Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 7)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 2)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 2)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
// Image is multiarch
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repoMultiarch, indexDigest, indexMedia)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 1)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
// Image is not scannable
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo2, image21Digest, image21Media)
So(err, ShouldEqual, zerr.ErrScanNotSupported)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Scan failed
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo5, image71Digest, image71Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
// Repo is not found
@ -1262,6 +1465,11 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
godigest.FromString("missing_digest").String(), ispec.MediaTypeImageManifest)
So(err, ShouldEqual, zerr.ErrRepoMetaNotFound)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
t.Log("\nTest GetImageListWithCVEFixed\n")
@ -1366,13 +1574,25 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image11Digest, image11Media)
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
cveList, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput)
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput)
So(err, ShouldNotBeNil)
So(cveList, ShouldBeEmpty)
So(pageInfo.ItemCount, ShouldEqual, 0)
So(pageInfo.TotalCount, ShouldEqual, 0)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.UnknownCount, ShouldEqual, 0)
So(cveSummary.LowCount, ShouldEqual, 0)
So(cveSummary.MediumCount, ShouldEqual, 0)
So(cveSummary.HighCount, ShouldEqual, 0)
So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "")
tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo1, "CVE1")
// CVE is not considered fixed as scan is not possible

View File

@ -7,8 +7,13 @@ import (
)
type ImageCVESummary struct {
Count int
MaxSeverity string
Count int
UnknownCount int
LowCount int
MediumCount int
HighCount int
CriticalCount int
MaxSeverity string
}
//nolint:tagliatelle // graphQL schema

View File

@ -140,22 +140,36 @@ func TestCVEPagination(t *testing.T) {
Convey("Page", func() {
Convey("defaults", func() {
// By default expect unlimitted results sorted by severity
cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{})
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 5)
So(pageInfo.ItemCount, ShouldEqual, 5)
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
previousSeverity := 4
for _, cve := range cves {
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
previousSeverity = severityToInt[cve.Severity]
}
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{})
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30)
So(pageInfo.ItemCount, ShouldEqual, 30)
So(pageInfo.TotalCount, ShouldEqual, 30)
So(cveSummary.Count, ShouldEqual, 30)
So(cveSummary.UnknownCount, ShouldEqual, 6)
So(cveSummary.LowCount, ShouldEqual, 6)
So(cveSummary.MediumCount, ShouldEqual, 6)
So(cveSummary.HighCount, ShouldEqual, 6)
So(cveSummary.CriticalCount, ShouldEqual, 6)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
previousSeverity = 4
for _, cve := range cves {
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
@ -169,44 +183,72 @@ func TestCVEPagination(t *testing.T) {
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
}
cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "",
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 5)
So(pageInfo.ItemCount, ShouldEqual, 5)
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
for i, cve := range cves {
So(cve.ID, ShouldEqual, cveIds[i])
}
sort.Strings(cveIds)
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30)
So(pageInfo.ItemCount, ShouldEqual, 30)
So(pageInfo.TotalCount, ShouldEqual, 30)
So(cveSummary.Count, ShouldEqual, 30)
So(cveSummary.UnknownCount, ShouldEqual, 6)
So(cveSummary.LowCount, ShouldEqual, 6)
So(cveSummary.MediumCount, ShouldEqual, 6)
So(cveSummary.HighCount, ShouldEqual, 6)
So(cveSummary.CriticalCount, ShouldEqual, 6)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
for i, cve := range cves {
So(cve.ID, ShouldEqual, cveIds[i])
}
sort.Sort(sort.Reverse(sort.StringSlice(cveIds)))
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticDsc})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30)
So(pageInfo.ItemCount, ShouldEqual, 30)
So(pageInfo.TotalCount, ShouldEqual, 30)
So(cveSummary.Count, ShouldEqual, 30)
So(cveSummary.UnknownCount, ShouldEqual, 6)
So(cveSummary.LowCount, ShouldEqual, 6)
So(cveSummary.MediumCount, ShouldEqual, 6)
So(cveSummary.HighCount, ShouldEqual, 6)
So(cveSummary.CriticalCount, ShouldEqual, 6)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
for i, cve := range cves {
So(cve.ID, ShouldEqual, cveIds[i])
}
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "",
cvemodel.PageInput{SortBy: cveinfo.SeverityDsc})
So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30)
So(pageInfo.ItemCount, ShouldEqual, 30)
So(pageInfo.TotalCount, ShouldEqual, 30)
So(cveSummary.Count, ShouldEqual, 30)
So(cveSummary.UnknownCount, ShouldEqual, 6)
So(cveSummary.LowCount, ShouldEqual, 6)
So(cveSummary.MediumCount, ShouldEqual, 6)
So(cveSummary.HighCount, ShouldEqual, 6)
So(cveSummary.CriticalCount, ShouldEqual, 6)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
previousSeverity := 4
for _, cve := range cves {
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
@ -220,7 +262,7 @@ func TestCVEPagination(t *testing.T) {
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
}
cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 3,
Offset: 1,
SortBy: cveinfo.AlphabeticAsc,
@ -233,8 +275,15 @@ func TestCVEPagination(t *testing.T) {
So(cves[0].ID, ShouldEqual, "CVE1") // CVE0 is first ID and is not part of the page
So(cves[1].ID, ShouldEqual, "CVE2")
So(cves[2].ID, ShouldEqual, "CVE3")
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 2,
Offset: 1,
SortBy: cveinfo.AlphabeticDsc,
@ -246,8 +295,15 @@ func TestCVEPagination(t *testing.T) {
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cves[0].ID, ShouldEqual, "CVE3")
So(cves[1].ID, ShouldEqual, "CVE2")
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 3,
Offset: 1,
SortBy: cveinfo.SeverityDsc,
@ -257,6 +313,13 @@ func TestCVEPagination(t *testing.T) {
So(len(cves), ShouldEqual, 3)
So(pageInfo.ItemCount, ShouldEqual, 3)
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
previousSeverity := 4
for _, cve := range cves {
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
@ -264,7 +327,7 @@ func TestCVEPagination(t *testing.T) {
}
sort.Strings(cveIds)
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{
Limit: 5,
Offset: 20,
SortBy: cveinfo.AlphabeticAsc,
@ -274,13 +337,20 @@ func TestCVEPagination(t *testing.T) {
So(len(cves), ShouldEqual, 5)
So(pageInfo.ItemCount, ShouldEqual, 5)
So(pageInfo.TotalCount, ShouldEqual, 30)
So(cveSummary.Count, ShouldEqual, 30)
So(cveSummary.UnknownCount, ShouldEqual, 6)
So(cveSummary.LowCount, ShouldEqual, 6)
So(cveSummary.MediumCount, ShouldEqual, 6)
So(cveSummary.HighCount, ShouldEqual, 6)
So(cveSummary.CriticalCount, ShouldEqual, 6)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
for i, cve := range cves {
So(cve.ID, ShouldEqual, cveIds[i+20])
}
})
Convey("limit > len(cves)", func() {
cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 6,
Offset: 3,
SortBy: cveinfo.AlphabeticAsc,
@ -292,8 +362,15 @@ func TestCVEPagination(t *testing.T) {
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cves[0].ID, ShouldEqual, "CVE3")
So(cves[1].ID, ShouldEqual, "CVE4")
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 6,
Offset: 3,
SortBy: cveinfo.AlphabeticDsc,
@ -305,8 +382,15 @@ func TestCVEPagination(t *testing.T) {
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cves[0].ID, ShouldEqual, "CVE1")
So(cves[1].ID, ShouldEqual, "CVE0")
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{
Limit: 6,
Offset: 3,
SortBy: cveinfo.SeverityDsc,
@ -316,6 +400,13 @@ func TestCVEPagination(t *testing.T) {
So(len(cves), ShouldEqual, 2)
So(pageInfo.ItemCount, ShouldEqual, 2)
So(pageInfo.TotalCount, ShouldEqual, 5)
So(cveSummary.Count, ShouldEqual, 5)
So(cveSummary.UnknownCount, ShouldEqual, 1)
So(cveSummary.LowCount, ShouldEqual, 1)
So(cveSummary.MediumCount, ShouldEqual, 1)
So(cveSummary.HighCount, ShouldEqual, 1)
So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
previousSeverity := 4
for _, cve := range cves {
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)

View File

@ -62,6 +62,7 @@ type ComplexityRoot struct {
CVEResultForImage struct {
CVEList func(childComplexity int) int
Page func(childComplexity int) int
Summary func(childComplexity int) int
Tag func(childComplexity int) int
}
@ -105,8 +106,13 @@ type ComplexityRoot struct {
}
ImageVulnerabilitySummary struct {
Count func(childComplexity int) int
MaxSeverity func(childComplexity int) int
Count func(childComplexity int) int
CriticalCount func(childComplexity int) int
HighCount func(childComplexity int) int
LowCount func(childComplexity int) int
MaxSeverity func(childComplexity int) int
MediumCount func(childComplexity int) int
UnknownCount func(childComplexity int) int
}
LayerHistory struct {
@ -318,6 +324,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.CVEResultForImage.Page(childComplexity), true
case "CVEResultForImage.Summary":
if e.complexity.CVEResultForImage.Summary == nil {
break
}
return e.complexity.CVEResultForImage.Summary(childComplexity), true
case "CVEResultForImage.Tag":
if e.complexity.CVEResultForImage.Tag == nil {
break
@ -542,6 +555,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ImageVulnerabilitySummary.Count(childComplexity), true
case "ImageVulnerabilitySummary.CriticalCount":
if e.complexity.ImageVulnerabilitySummary.CriticalCount == nil {
break
}
return e.complexity.ImageVulnerabilitySummary.CriticalCount(childComplexity), true
case "ImageVulnerabilitySummary.HighCount":
if e.complexity.ImageVulnerabilitySummary.HighCount == nil {
break
}
return e.complexity.ImageVulnerabilitySummary.HighCount(childComplexity), true
case "ImageVulnerabilitySummary.LowCount":
if e.complexity.ImageVulnerabilitySummary.LowCount == nil {
break
}
return e.complexity.ImageVulnerabilitySummary.LowCount(childComplexity), true
case "ImageVulnerabilitySummary.MaxSeverity":
if e.complexity.ImageVulnerabilitySummary.MaxSeverity == nil {
break
@ -549,6 +583,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ImageVulnerabilitySummary.MaxSeverity(childComplexity), true
case "ImageVulnerabilitySummary.MediumCount":
if e.complexity.ImageVulnerabilitySummary.MediumCount == nil {
break
}
return e.complexity.ImageVulnerabilitySummary.MediumCount(childComplexity), true
case "ImageVulnerabilitySummary.UnknownCount":
if e.complexity.ImageVulnerabilitySummary.UnknownCount == nil {
break
}
return e.complexity.ImageVulnerabilitySummary.UnknownCount(childComplexity), true
case "LayerHistory.HistoryDescription":
if e.complexity.LayerHistory.HistoryDescription == nil {
break
@ -1164,6 +1212,7 @@ A timestamp
"""
scalar Time
"""
Contains the tag of the image and a list of CVEs
"""
@ -1177,6 +1226,10 @@ type CVEResultForImage {
"""
CVEList: [CVE]
"""
Summary of the findings for this image
"""
Summary: ImageVulnerabilitySummary
"""
The CVE pagination information, see PageInfo object for more details
"""
Page: PageInfo
@ -1430,6 +1483,26 @@ type ImageVulnerabilitySummary {
Count of all CVEs found in this image
"""
Count: Int
"""
Coresponds to CVSS 3 score NONE
"""
UnknownCount: Int
"""
Coresponds to CVSS 3 score LOW
"""
LowCount: Int
"""
Coresponds to CVSS 3 score MEDIUM
"""
MediumCount: Int
"""
Coresponds to CVSS 3 score HIGH
"""
HighCount: Int
"""
Coresponds to CVSS 3 score CRITICAL
"""
CriticalCount: Int
}
"""
@ -2761,6 +2834,63 @@ func (ec *executionContext) fieldContext_CVEResultForImage_CVEList(ctx context.C
return fc, nil
}
func (ec *executionContext) _CVEResultForImage_Summary(ctx context.Context, field graphql.CollectedField, obj *CVEResultForImage) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_CVEResultForImage_Summary(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Summary, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*ImageVulnerabilitySummary)
fc.Result = res
return ec.marshalOImageVulnerabilitySummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageVulnerabilitySummary(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_CVEResultForImage_Summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "CVEResultForImage",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "MaxSeverity":
return ec.fieldContext_ImageVulnerabilitySummary_MaxSeverity(ctx, field)
case "Count":
return ec.fieldContext_ImageVulnerabilitySummary_Count(ctx, field)
case "UnknownCount":
return ec.fieldContext_ImageVulnerabilitySummary_UnknownCount(ctx, field)
case "LowCount":
return ec.fieldContext_ImageVulnerabilitySummary_LowCount(ctx, field)
case "MediumCount":
return ec.fieldContext_ImageVulnerabilitySummary_MediumCount(ctx, field)
case "HighCount":
return ec.fieldContext_ImageVulnerabilitySummary_HighCount(ctx, field)
case "CriticalCount":
return ec.fieldContext_ImageVulnerabilitySummary_CriticalCount(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummary", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _CVEResultForImage_Page(ctx context.Context, field graphql.CollectedField, obj *CVEResultForImage) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_CVEResultForImage_Page(ctx, field)
if err != nil {
@ -4071,6 +4201,16 @@ func (ec *executionContext) fieldContext_ImageSummary_Vulnerabilities(ctx contex
return ec.fieldContext_ImageVulnerabilitySummary_MaxSeverity(ctx, field)
case "Count":
return ec.fieldContext_ImageVulnerabilitySummary_Count(ctx, field)
case "UnknownCount":
return ec.fieldContext_ImageVulnerabilitySummary_UnknownCount(ctx, field)
case "LowCount":
return ec.fieldContext_ImageVulnerabilitySummary_LowCount(ctx, field)
case "MediumCount":
return ec.fieldContext_ImageVulnerabilitySummary_MediumCount(ctx, field)
case "HighCount":
return ec.fieldContext_ImageVulnerabilitySummary_HighCount(ctx, field)
case "CriticalCount":
return ec.fieldContext_ImageVulnerabilitySummary_CriticalCount(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummary", field.Name)
},
@ -4254,6 +4394,211 @@ func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_Count(ctx con
return fc, nil
}
func (ec *executionContext) _ImageVulnerabilitySummary_UnknownCount(ctx context.Context, field graphql.CollectedField, obj *ImageVulnerabilitySummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageVulnerabilitySummary_UnknownCount(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.UnknownCount, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int)
fc.Result = res
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_UnknownCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageVulnerabilitySummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Int does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageVulnerabilitySummary_LowCount(ctx context.Context, field graphql.CollectedField, obj *ImageVulnerabilitySummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageVulnerabilitySummary_LowCount(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.LowCount, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int)
fc.Result = res
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_LowCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageVulnerabilitySummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Int does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageVulnerabilitySummary_MediumCount(ctx context.Context, field graphql.CollectedField, obj *ImageVulnerabilitySummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageVulnerabilitySummary_MediumCount(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.MediumCount, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int)
fc.Result = res
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_MediumCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageVulnerabilitySummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Int does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageVulnerabilitySummary_HighCount(ctx context.Context, field graphql.CollectedField, obj *ImageVulnerabilitySummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageVulnerabilitySummary_HighCount(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.HighCount, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int)
fc.Result = res
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_HighCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageVulnerabilitySummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Int does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageVulnerabilitySummary_CriticalCount(ctx context.Context, field graphql.CollectedField, obj *ImageVulnerabilitySummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageVulnerabilitySummary_CriticalCount(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.CriticalCount, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int)
fc.Result = res
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageVulnerabilitySummary_CriticalCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageVulnerabilitySummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Int does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _LayerHistory_Layer(ctx context.Context, field graphql.CollectedField, obj *LayerHistory) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_LayerHistory_Layer(ctx, field)
if err != nil {
@ -4912,6 +5257,16 @@ func (ec *executionContext) fieldContext_ManifestSummary_Vulnerabilities(ctx con
return ec.fieldContext_ImageVulnerabilitySummary_MaxSeverity(ctx, field)
case "Count":
return ec.fieldContext_ImageVulnerabilitySummary_Count(ctx, field)
case "UnknownCount":
return ec.fieldContext_ImageVulnerabilitySummary_UnknownCount(ctx, field)
case "LowCount":
return ec.fieldContext_ImageVulnerabilitySummary_LowCount(ctx, field)
case "MediumCount":
return ec.fieldContext_ImageVulnerabilitySummary_MediumCount(ctx, field)
case "HighCount":
return ec.fieldContext_ImageVulnerabilitySummary_HighCount(ctx, field)
case "CriticalCount":
return ec.fieldContext_ImageVulnerabilitySummary_CriticalCount(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ImageVulnerabilitySummary", field.Name)
},
@ -5599,6 +5954,8 @@ func (ec *executionContext) fieldContext_Query_CVEListForImage(ctx context.Conte
return ec.fieldContext_CVEResultForImage_Tag(ctx, field)
case "CVEList":
return ec.fieldContext_CVEResultForImage_CVEList(ctx, field)
case "Summary":
return ec.fieldContext_CVEResultForImage_Summary(ctx, field)
case "Page":
return ec.fieldContext_CVEResultForImage_Page(ctx, field)
}
@ -9553,6 +9910,8 @@ func (ec *executionContext) _CVEResultForImage(ctx context.Context, sel ast.Sele
out.Values[i] = ec._CVEResultForImage_Tag(ctx, field, obj)
case "CVEList":
out.Values[i] = ec._CVEResultForImage_CVEList(ctx, field, obj)
case "Summary":
out.Values[i] = ec._CVEResultForImage_Summary(ctx, field, obj)
case "Page":
out.Values[i] = ec._CVEResultForImage_Page(ctx, field, obj)
default:
@ -9755,6 +10114,16 @@ func (ec *executionContext) _ImageVulnerabilitySummary(ctx context.Context, sel
out.Values[i] = ec._ImageVulnerabilitySummary_MaxSeverity(ctx, field, obj)
case "Count":
out.Values[i] = ec._ImageVulnerabilitySummary_Count(ctx, field, obj)
case "UnknownCount":
out.Values[i] = ec._ImageVulnerabilitySummary_UnknownCount(ctx, field, obj)
case "LowCount":
out.Values[i] = ec._ImageVulnerabilitySummary_LowCount(ctx, field, obj)
case "MediumCount":
out.Values[i] = ec._ImageVulnerabilitySummary_MediumCount(ctx, field, obj)
case "HighCount":
out.Values[i] = ec._ImageVulnerabilitySummary_HighCount(ctx, field, obj)
case "CriticalCount":
out.Values[i] = ec._ImageVulnerabilitySummary_CriticalCount(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}

View File

@ -41,6 +41,8 @@ type CVEResultForImage struct {
Tag *string `json:"Tag,omitempty"`
// List of CVE objects which affect this specific image:tag
CVEList []*Cve `json:"CVEList,omitempty"`
// Summary of the findings for this image
Summary *ImageVulnerabilitySummary `json:"Summary,omitempty"`
// The CVE pagination information, see PageInfo object for more details
Page *PageInfo `json:"Page,omitempty"`
}
@ -144,6 +146,16 @@ type ImageVulnerabilitySummary struct {
MaxSeverity *string `json:"MaxSeverity,omitempty"`
// Count of all CVEs found in this image
Count *int `json:"Count,omitempty"`
// Coresponds to CVSS 3 score NONE
UnknownCount *int `json:"UnknownCount,omitempty"`
// Coresponds to CVSS 3 score LOW
LowCount *int `json:"LowCount,omitempty"`
// Coresponds to CVSS 3 score MEDIUM
MediumCount *int `json:"MediumCount,omitempty"`
// Coresponds to CVSS 3 score HIGH
HighCount *int `json:"HighCount,omitempty"`
// Coresponds to CVSS 3 score CRITICAL
CriticalCount *int `json:"CriticalCount,omitempty"`
}
// Information about how/when a layer was built

View File

@ -216,7 +216,8 @@ func getCVEListForImage(
return &gql_generated.CVEResultForImage{}, gqlerror.Errorf("no reference provided")
}
cveList, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo, ref, searchedCVE, pageInput)
cveList, imageCveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo, ref,
searchedCVE, pageInput)
if err != nil {
return &gql_generated.CVEResultForImage{}, err
}
@ -259,6 +260,15 @@ func getCVEListForImage(
return &gql_generated.CVEResultForImage{
Tag: &ref,
CVEList: cveids,
Summary: &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
UnknownCount: &imageCveSummary.UnknownCount,
LowCount: &imageCveSummary.LowCount,
MediumCount: &imageCveSummary.MediumCount,
HighCount: &imageCveSummary.HighCount,
CriticalCount: &imageCveSummary.CriticalCount,
Count: &imageCveSummary.Count,
},
Page: &gql_generated.PageInfo{
TotalCount: pageInfo.TotalCount,
ItemCount: pageInfo.ItemCount,

View File

@ -10,6 +10,7 @@ A timestamp
"""
scalar Time
"""
Contains the tag of the image and a list of CVEs
"""
@ -23,6 +24,10 @@ type CVEResultForImage {
"""
CVEList: [CVE]
"""
Summary of the findings for this image
"""
Summary: ImageVulnerabilitySummary
"""
The CVE pagination information, see PageInfo object for more details
"""
Page: PageInfo
@ -276,6 +281,26 @@ type ImageVulnerabilitySummary {
Count of all CVEs found in this image
"""
Count: Int
"""
Coresponds to CVSS 3 score NONE
"""
UnknownCount: Int
"""
Coresponds to CVSS 3 score LOW
"""
LowCount: Int
"""
Coresponds to CVSS 3 score MEDIUM
"""
MediumCount: Int
"""
Coresponds to CVSS 3 score HIGH
"""
HighCount: Int
"""
Coresponds to CVSS 3 score CRITICAL
"""
CriticalCount: Int
}
"""

View File

@ -18,7 +18,6 @@ import (
"testing"
"time"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
guuid "github.com/gofrs/uuid"
regTypes "github.com/google/go-containerregistry/pkg/v1/types"
notreg "github.com/notaryproject/notation-go/registry"
@ -530,6 +529,11 @@ func TestRepoListWithNewestImage(t *testing.T) {
Tag
Vulnerabilities{
MaxSeverity
UnknownCount
LowCount
MediumCount
HighCount
CriticalCount
Count
}
}
@ -551,6 +555,11 @@ func TestRepoListWithNewestImage(t *testing.T) {
images = responseStruct.Results
So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1")
So(images[0].NewestImage.Vulnerabilities.Count, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.LowCount, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.HighCount, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(images[0].NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "")
query = `{
@ -741,6 +750,11 @@ func TestRepoListWithNewestImage(t *testing.T) {
Digest
Vulnerabilities{
MaxSeverity
UnknownCount
LowCount
MediumCount
HighCount
CriticalCount
Count
}
}
@ -765,12 +779,12 @@ func TestRepoListWithNewestImage(t *testing.T) {
So(vulnerabilities, ShouldNotBeNil)
t.Logf("Found vulnerability summary %v", vulnerabilities)
// Depends on test data, but current tested images contain hundreds
So(vulnerabilities.Count, ShouldBeGreaterThan, 1)
So(
dbTypes.CompareSeverityString(dbTypes.SeverityUnknown.String(), vulnerabilities.MaxSeverity),
ShouldBeGreaterThan,
0,
)
So(vulnerabilities.Count, ShouldEqual, 4)
So(vulnerabilities.UnknownCount, ShouldEqual, 0)
So(vulnerabilities.LowCount, ShouldEqual, 1)
So(vulnerabilities.MediumCount, ShouldEqual, 1)
So(vulnerabilities.HighCount, ShouldEqual, 1)
So(vulnerabilities.CriticalCount, ShouldEqual, 1)
So(vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
}
})
@ -3178,7 +3192,7 @@ func TestGlobalSearch(t *testing.T) {
Layer { Size Digest }
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
Vendor
Vulnerabilities { Count MaxSeverity }
@ -3199,7 +3213,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
}
Layers { Digest Size }
@ -3256,6 +3270,11 @@ func TestGlobalSearch(t *testing.T) {
// No vulnerabilities should be detected since trivy is disabled
t.Logf("Found vulnerability summary %v", repoSummary.NewestImage.Vulnerabilities)
So(repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.LowCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.HighCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "")
}
@ -3272,7 +3291,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
Repos {
Name LastUpdated Size
@ -3288,7 +3307,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
}
Layers { Digest Size }
@ -3323,6 +3342,11 @@ func TestGlobalSearch(t *testing.T) {
// 0 vulnerabilities should be detected since trivy is disabled
t.Logf("Found vulnerability summary %v", actualImageSummary.Vulnerabilities)
So(actualImageSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.LowCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.HighCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
})
@ -3500,7 +3524,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
Repos {
Name LastUpdated Size
@ -3516,7 +3540,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
}
Layers { Digest Size }
@ -3575,10 +3599,20 @@ func TestGlobalSearch(t *testing.T) {
if repoName == "repo1" { //nolint:goconst
So(repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 4)
// There are 4 vulnerabilities in the data used in tests
So(repoSummary.NewestImage.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.LowCount, ShouldEqual, 1)
So(repoSummary.NewestImage.Vulnerabilities.MediumCount, ShouldEqual, 1)
So(repoSummary.NewestImage.Vulnerabilities.HighCount, ShouldEqual, 1)
So(repoSummary.NewestImage.Vulnerabilities.CriticalCount, ShouldEqual, 1)
So(repoSummary.NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
} else {
So(repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 0)
// There are 0 vulnerabilities this data used in tests
So(repoSummary.NewestImage.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.LowCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.HighCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(repoSummary.NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "NONE")
}
}
@ -3596,7 +3630,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
Repos {
Name LastUpdated Size
@ -3612,7 +3646,7 @@ func TestGlobalSearch(t *testing.T) {
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
}
Layers { Digest Size }
@ -3647,6 +3681,11 @@ func TestGlobalSearch(t *testing.T) {
t.Logf("Found vulnerability summary %v", actualImageSummary.Vulnerabilities)
// There are 4 vulnerabilities in the data used in tests
So(actualImageSummary.Vulnerabilities.Count, ShouldEqual, 4)
So(actualImageSummary.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(actualImageSummary.Vulnerabilities.LowCount, ShouldEqual, 1)
So(actualImageSummary.Vulnerabilities.MediumCount, ShouldEqual, 1)
So(actualImageSummary.Vulnerabilities.HighCount, ShouldEqual, 1)
So(actualImageSummary.Vulnerabilities.CriticalCount, ShouldEqual, 1)
So(actualImageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
})
@ -5949,7 +5988,7 @@ func TestImageSummary(t *testing.T) {
Size
Platform { Os Arch }
Layers { Digest Size }
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
History {
HistoryDescription { Created }
Layer { Digest Size }
@ -5957,7 +5996,7 @@ func TestImageSummary(t *testing.T) {
}
LastUpdated
Size
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
Referrers {MediaType ArtifactType Digest Annotations {Key Value}}
}
}`
@ -5976,7 +6015,7 @@ func TestImageSummary(t *testing.T) {
Size
Platform { Os Arch }
Layers { Digest Size }
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
History {
HistoryDescription { Created }
Layer { Digest Size }
@ -6089,6 +6128,11 @@ func TestImageSummary(t *testing.T) {
So(imgSummary.Manifests[0].History[0].HistoryDescription.Created, ShouldEqual, createdTime)
// No vulnerabilities should be detected since trivy is disabled
So(imgSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.LowCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.MediumCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.HighCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.CriticalCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
So(len(imgSummary.Referrers), ShouldEqual, 1)
So(imgSummary.Referrers[0], ShouldResemble, zcommon.Referrer{
@ -6177,7 +6221,7 @@ func TestImageSummary(t *testing.T) {
Size
Platform { Os Arch }
Layers { Digest Size }
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
History {
HistoryDescription { Created }
Layer { Digest Size }
@ -6185,7 +6229,7 @@ func TestImageSummary(t *testing.T) {
}
LastUpdated
Size
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
}
}`
@ -6271,6 +6315,11 @@ func TestImageSummary(t *testing.T) {
So(len(imgSummary.Manifests[0].History), ShouldEqual, 1)
So(imgSummary.Manifests[0].History[0].HistoryDescription.Created, ShouldEqual, createdTime)
So(imgSummary.Vulnerabilities.Count, ShouldEqual, 4)
So(imgSummary.Vulnerabilities.UnknownCount, ShouldEqual, 0)
So(imgSummary.Vulnerabilities.LowCount, ShouldEqual, 1)
So(imgSummary.Vulnerabilities.MediumCount, ShouldEqual, 1)
So(imgSummary.Vulnerabilities.HighCount, ShouldEqual, 1)
So(imgSummary.Vulnerabilities.CriticalCount, ShouldEqual, 1)
// There are 0 vulnerabilities this data used in tests
So(imgSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
})
@ -6800,11 +6849,11 @@ func GlobalSearchGQL(query, baseURL string) *zcommon.GlobalSearchResultResp {
Layer { Size Digest }
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
Vulnerabilities {Count MaxSeverity}
Vulnerabilities {Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity}
Referrers {MediaType ArtifactType Size Digest Annotations {Key Value}}
}
Referrers {MediaType ArtifactType Size Digest Annotations {Key Value}}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
SignatureInfo {Tool IsTrusted Author}
}
Repos {
@ -6824,11 +6873,11 @@ func GlobalSearchGQL(query, baseURL string) *zcommon.GlobalSearchResultResp {
Layer { Size Digest }
HistoryDescription { Author Comment Created CreatedBy EmptyLayer }
}
Vulnerabilities {Count MaxSeverity}
Vulnerabilities {Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity}
Referrers {MediaType ArtifactType Size Digest Annotations {Key Value}}
}
Referrers {MediaType ArtifactType Size Digest Annotations {Key Value}}
Vulnerabilities { Count MaxSeverity }
Vulnerabilities { Count UnknownCount LowCount MediumCount HighCount CriticalCount MaxSeverity }
SignatureInfo {Tool IsTrusted Author}
}
}

View File

@ -11,7 +11,7 @@ type CveInfoMock struct {
GetImageListForCVEFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetImageListWithCVEFixedFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetCVEListForImageFn func(ctx context.Context, repo string, reference string, searchedCVE string,
pageInput cvemodel.PageInput) ([]cvemodel.CVE, common.PageInfo, error)
pageInput cvemodel.PageInput) ([]cvemodel.CVE, cvemodel.ImageCVESummary, common.PageInfo, error)
GetCVESummaryForImageMediaFn func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error)
}
@ -37,6 +37,7 @@ func (cveInfo CveInfoMock) GetCVEListForImage(ctx context.Context, repo string,
searchedCVE string, pageInput cvemodel.PageInput,
) (
[]cvemodel.CVE,
cvemodel.ImageCVESummary,
common.PageInfo,
error,
) {
@ -44,7 +45,7 @@ func (cveInfo CveInfoMock) GetCVEListForImage(ctx context.Context, repo string,
return cveInfo.GetCVEListForImageFn(ctx, repo, reference, searchedCVE, pageInput)
}
return []cvemodel.CVE{}, common.PageInfo{}, nil
return []cvemodel.CVE{}, cvemodel.ImageCVESummary{}, common.PageInfo{}, nil
}
func (cveInfo CveInfoMock) GetCVESummaryForImageMedia(ctx context.Context, repo, digest, mediaType string,