0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

feat(cve): better distinguish max severity on an image (#918)

Values returned now by GetCVESummaryForImage
// not scannable / error during scan  - max severity ""            - cve count 0   - 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

Before this change the max severity in case #1 and #2 was "UNKNOWN" which is also possible value
for case #3. To better distinguish them return different max severities.

This feature would be consumed by the UI.

Signed-off-by: Andrei Aaron <andaaron@cisco.com>
This commit is contained in:
Andrei Aaron 2022-10-24 22:27:26 +03:00 committed by GitHub
parent 1d9c88c313
commit 92afd86cbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 5 deletions

View file

@ -177,9 +177,13 @@ func (cveinfo BaseCveInfo) GetCVEListForImage(image string) (map[string]cvemodel
} }
func (cveinfo BaseCveInfo) GetCVESummaryForImage(image string) (ImageCVESummary, error) { func (cveinfo BaseCveInfo) GetCVESummaryForImage(image string) (ImageCVESummary, error) {
// There are several cases, expected returned values below:
// not scannable / error during scan - max severity "" - cve count 0 - 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 := ImageCVESummary{ imageCVESummary := ImageCVESummary{
Count: 0, Count: 0,
MaxSeverity: "UNKNOWN", MaxSeverity: "",
} }
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(image) isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(image)
@ -192,12 +196,20 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(image string) (ImageCVESummary,
return imageCVESummary, err return imageCVESummary, err
} }
imageCVESummary.Count = len(cveMap)
if imageCVESummary.Count == 0 {
imageCVESummary.MaxSeverity = "NONE"
return imageCVESummary, nil
}
imageCVESummary.MaxSeverity = "UNKNOWN"
for _, cve := range cveMap { for _, cve := range cveMap {
if cveinfo.Scanner.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 { if cveinfo.Scanner.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity imageCVESummary.MaxSeverity = cve.Severity
} }
} }
imageCVESummary.Count = len(cveMap)
return imageCVESummary, nil return imageCVESummary, nil
} }

View file

@ -829,6 +829,20 @@ func TestCVEStruct(t *testing.T) {
}, nil }, nil
} }
// Image with no CVEs
if repo == "repo4" { //nolint: goconst
return []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.manifest.v1+json",
Size: int64(0),
Annotations: map[string]string{
ispec.AnnotationRefName: "1.0.0",
},
Digest: godigest.FromString("abc"),
},
}, nil
}
// By default the image is not found // By default the image is not found
return nil, errors.ErrRepoNotFound return nil, errors.ErrRepoNotFound
}, },
@ -870,6 +884,17 @@ func TestCVEStruct(t *testing.T) {
}, nil }, nil
} }
// Image with no vulnerabilities, repo3 is for tests on missing images
if repo == "repo4" { //nolint: goconst
return []common.TagInfo{
{
Name: "1.0.0",
Digest: godigest.FromString("abc"),
Timestamp: time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC),
},
}, nil
}
// By default do not return any tags // By default do not return any tags
return []common.TagInfo{}, errors.ErrRepoNotFound return []common.TagInfo{}, errors.ErrRepoNotFound
}, },
@ -900,6 +925,19 @@ func TestCVEStruct(t *testing.T) {
}, nil }, nil
} }
// Image with no CVEs
if imageDir == "repo4" { //nolint: goconst
return ispec.Manifest{
Layers: []ispec.Descriptor{
{
MediaType: string(ispec.MediaTypeImageLayer),
Size: 0,
Digest: godigest.Digest(""),
},
},
}, nil
}
return ispec.Manifest{}, errors.ErrBlobNotFound return ispec.Manifest{}, errors.ErrBlobNotFound
}, },
} }
@ -1054,13 +1092,19 @@ func TestCVEStruct(t *testing.T) {
cveSummary, err = cveInfo.GetCVESummaryForImage("repo2:1.0.0") cveSummary, err = cveInfo.GetCVESummaryForImage("repo2:1.0.0")
So(err, ShouldEqual, errors.ErrScanNotSupported) So(err, ShouldEqual, errors.ErrScanNotSupported)
So(cveSummary.Count, ShouldEqual, 0) So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "UNKNOWN") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Image is not found // Image is not found
cveSummary, err = cveInfo.GetCVESummaryForImage("repo3:1.0.0") cveSummary, err = cveInfo.GetCVESummaryForImage("repo3:1.0.0")
So(err, ShouldEqual, errors.ErrRepoNotFound) So(err, ShouldEqual, errors.ErrRepoNotFound)
So(cveSummary.Count, ShouldEqual, 0) So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "UNKNOWN") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Image has no vulnerabilities
cveSummary, err = cveInfo.GetCVESummaryForImage("repo4:1.0.0")
So(err, ShouldBeNil)
So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "NONE")
}) })
Convey("Test GetCVEListForImage", func() { Convey("Test GetCVEListForImage", func() {
@ -1104,6 +1148,11 @@ func TestCVEStruct(t *testing.T) {
cveMap, err = cveInfo.GetCVEListForImage("repo3:1.0.0") cveMap, err = cveInfo.GetCVEListForImage("repo3:1.0.0")
So(err, ShouldEqual, errors.ErrRepoNotFound) So(err, ShouldEqual, errors.ErrRepoNotFound)
So(len(cveMap), ShouldEqual, 0) So(len(cveMap), ShouldEqual, 0)
// Image has no vulnerabilities
cveMap, err = cveInfo.GetCVEListForImage("repo4:1.0.0")
So(err, ShouldBeNil)
So(len(cveMap), ShouldEqual, 0)
}) })
Convey("Test GetImageListWithCVEFixed", func() { Convey("Test GetImageListWithCVEFixed", func() {
@ -1139,6 +1188,12 @@ func TestCVEStruct(t *testing.T) {
tagList, err = cveInfo.GetImageListWithCVEFixed("repo3", "CVE101") tagList, err = cveInfo.GetImageListWithCVEFixed("repo3", "CVE101")
So(err, ShouldEqual, errors.ErrRepoNotFound) So(err, ShouldEqual, errors.ErrRepoNotFound)
So(len(tagList), ShouldEqual, 0) So(len(tagList), ShouldEqual, 0)
// Image has no vulnerabilities
tagList, err = cveInfo.GetImageListWithCVEFixed("repo4", "CVE101")
So(err, ShouldBeNil)
So(len(tagList), ShouldEqual, 1)
So(tagList[0].Name, ShouldEqual, "1.0.0")
}) })
Convey("Test GetImageListForCVE", func() { Convey("Test GetImageListForCVE", func() {
@ -1175,6 +1230,11 @@ func TestCVEStruct(t *testing.T) {
imageInfoByCveList, err = cveInfo.GetImageListForCVE("repo3", "CVE101") imageInfoByCveList, err = cveInfo.GetImageListForCVE("repo3", "CVE101")
So(err, ShouldEqual, errors.ErrRepoNotFound) So(err, ShouldEqual, errors.ErrRepoNotFound)
So(len(imageInfoByCveList), ShouldEqual, 0) So(len(imageInfoByCveList), ShouldEqual, 0)
// Image/repo is not vulnerable
imageInfoByCveList, err = cveInfo.GetImageListForCVE("repo4", "CVE101")
So(err, ShouldBeNil)
So(len(imageInfoByCveList), ShouldEqual, 0)
}) })
Convey("Test errors while scanning", func() { Convey("Test errors while scanning", func() {
@ -1190,7 +1250,7 @@ func TestCVEStruct(t *testing.T) {
cveSummary, err := cveInfo.GetCVESummaryForImage("repo1:0.1.0") cveSummary, err := cveInfo.GetCVESummaryForImage("repo1:0.1.0")
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
So(cveSummary.Count, ShouldEqual, 0) So(cveSummary.Count, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "UNKNOWN") So(cveSummary.MaxSeverity, ShouldEqual, "")
cveMap, err := cveInfo.GetCVEListForImage("repo1:0.1.0") cveMap, err := cveInfo.GetCVEListForImage("repo1:0.1.0")
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)