2023-02-05 02:21:45 -05:00
|
|
|
//go:build search
|
|
|
|
// +build search
|
|
|
|
|
2023-01-24 18:03:10 -05:00
|
|
|
package cveinfo_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
|
|
|
|
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
|
|
|
|
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
|
|
|
|
"zotregistry.io/zot/pkg/log"
|
2023-03-28 12:20:09 -05:00
|
|
|
"zotregistry.io/zot/pkg/meta/bolt"
|
2023-01-24 18:03:10 -05:00
|
|
|
"zotregistry.io/zot/pkg/meta/repodb"
|
2023-03-28 12:20:09 -05:00
|
|
|
boltdb_wrapper "zotregistry.io/zot/pkg/meta/repodb/boltdb-wrapper"
|
2023-01-24 18:03:10 -05:00
|
|
|
"zotregistry.io/zot/pkg/test/mocks"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCVEPagination(t *testing.T) {
|
|
|
|
Convey("CVE Pagination", t, func() {
|
2023-03-28 12:20:09 -05:00
|
|
|
params := bolt.DBParameters{
|
2023-01-24 18:03:10 -05:00
|
|
|
RootDir: t.TempDir(),
|
2023-03-28 12:20:09 -05:00
|
|
|
}
|
|
|
|
boltDriver, err := bolt.GetBoltDriver(params)
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
repoDB, err := boltdb_wrapper.NewBoltDBWrapper(boltDriver, log.NewLogger("debug", ""))
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
// Create repodb data for scannable image with vulnerabilities
|
|
|
|
timeStamp11 := time.Date(2008, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
|
|
|
|
configBlob11, err := json.Marshal(ispec.Image{
|
|
|
|
Created: &timeStamp11,
|
|
|
|
})
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
manifestBlob11, err := json.Marshal(ispec.Manifest{
|
|
|
|
Config: ispec.Descriptor{
|
|
|
|
MediaType: ispec.MediaTypeImageConfig,
|
|
|
|
Size: 0,
|
|
|
|
Digest: godigest.FromBytes(configBlob11),
|
|
|
|
},
|
|
|
|
Layers: []ispec.Descriptor{
|
|
|
|
{
|
|
|
|
MediaType: ispec.MediaTypeImageLayerGzip,
|
|
|
|
Size: 0,
|
|
|
|
Digest: godigest.NewDigestFromEncoded(godigest.SHA256, "digest"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
repoMeta11 := repodb.ManifestMetadata{
|
|
|
|
ManifestBlob: manifestBlob11,
|
|
|
|
ConfigBlob: configBlob11,
|
|
|
|
}
|
|
|
|
|
|
|
|
digest11 := godigest.FromBytes(manifestBlob11)
|
|
|
|
err = repoDB.SetManifestMeta("repo1", digest11, repoMeta11)
|
|
|
|
So(err, ShouldBeNil)
|
2023-03-09 13:41:48 -05:00
|
|
|
err = repoDB.SetRepoReference("repo1", "0.1.0", digest11, ispec.MediaTypeImageManifest)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
timeStamp12 := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
|
|
|
|
configBlob12, err := json.Marshal(ispec.Image{
|
|
|
|
Created: &timeStamp12,
|
|
|
|
})
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
manifestBlob12, err := json.Marshal(ispec.Manifest{
|
|
|
|
Config: ispec.Descriptor{
|
|
|
|
MediaType: ispec.MediaTypeImageConfig,
|
|
|
|
Size: 0,
|
|
|
|
Digest: godigest.FromBytes(configBlob12),
|
|
|
|
},
|
|
|
|
Layers: []ispec.Descriptor{
|
|
|
|
{
|
|
|
|
MediaType: ispec.MediaTypeImageLayerGzip,
|
|
|
|
Size: 0,
|
|
|
|
Digest: godigest.NewDigestFromEncoded(godigest.SHA256, "digest"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
repoMeta12 := repodb.ManifestMetadata{
|
|
|
|
ManifestBlob: manifestBlob12,
|
|
|
|
ConfigBlob: configBlob12,
|
|
|
|
}
|
|
|
|
|
|
|
|
digest12 := godigest.FromBytes(manifestBlob12)
|
|
|
|
err = repoDB.SetManifestMeta("repo1", digest12, repoMeta12)
|
|
|
|
So(err, ShouldBeNil)
|
2023-03-09 13:41:48 -05:00
|
|
|
err = repoDB.SetRepoReference("repo1", "1.0.0", digest12, ispec.MediaTypeImageManifest)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
// RepoDB loaded with initial data, mock the scanner
|
|
|
|
severityToInt := map[string]int{
|
|
|
|
"UNKNOWN": 0,
|
|
|
|
"LOW": 1,
|
|
|
|
"MEDIUM": 2,
|
|
|
|
"HIGH": 3,
|
|
|
|
"CRITICAL": 4,
|
|
|
|
}
|
|
|
|
|
|
|
|
intToSeverity := make(map[int]string, len(severityToInt))
|
|
|
|
for k, v := range severityToInt {
|
|
|
|
intToSeverity[v] = k
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup test CVE data in mock scanner
|
|
|
|
scanner := mocks.CveScannerMock{
|
|
|
|
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
|
|
|
|
cveMap := map[string]cvemodel.CVE{}
|
|
|
|
|
|
|
|
if image == "repo1:0.1.0" {
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
cveMap[fmt.Sprintf("CVE%d", i)] = cvemodel.CVE{
|
|
|
|
ID: fmt.Sprintf("CVE%d", i),
|
|
|
|
Severity: intToSeverity[i%5],
|
|
|
|
Title: fmt.Sprintf("Title for CVE%d", i),
|
|
|
|
Description: fmt.Sprintf("Description for CVE%d", i),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if image == "repo1:1.0.0" {
|
|
|
|
for i := 0; i < 30; i++ {
|
|
|
|
cveMap[fmt.Sprintf("CVE%d", i)] = cvemodel.CVE{
|
|
|
|
ID: fmt.Sprintf("CVE%d", i),
|
|
|
|
Severity: intToSeverity[i%5],
|
|
|
|
Title: fmt.Sprintf("Title for CVE%d", i),
|
|
|
|
Description: fmt.Sprintf("Description for CVE%d", i),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// By default the image has no vulnerabilities
|
|
|
|
return cveMap, nil
|
|
|
|
},
|
|
|
|
CompareSeveritiesFn: func(severity1, severity2 string) int {
|
|
|
|
return severityToInt[severity2] - severityToInt[severity1]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
log := log.NewLogger("debug", "")
|
|
|
|
cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, RepoDB: repoDB}
|
|
|
|
|
|
|
|
Convey("create new paginator errors", func() {
|
|
|
|
paginator, err := cveinfo.NewCvePageFinder(-1, 10, cveinfo.AlphabeticAsc, cveInfo)
|
|
|
|
So(paginator, ShouldBeNil)
|
|
|
|
So(err, ShouldNotBeNil)
|
|
|
|
|
|
|
|
paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc, cveInfo)
|
|
|
|
So(paginator, ShouldBeNil)
|
|
|
|
So(err, ShouldNotBeNil)
|
|
|
|
|
|
|
|
paginator, err = cveinfo.NewCvePageFinder(2, 1, "wrong sorting criteria", cveInfo)
|
|
|
|
So(paginator, ShouldBeNil)
|
|
|
|
So(err, ShouldNotBeNil)
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("Reset", func() {
|
|
|
|
paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc, cveInfo)
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(paginator, ShouldNotBeNil)
|
|
|
|
|
|
|
|
paginator.Add(cvemodel.CVE{})
|
|
|
|
paginator.Add(cvemodel.CVE{})
|
|
|
|
paginator.Add(cvemodel.CVE{})
|
|
|
|
|
|
|
|
paginator.Reset()
|
|
|
|
|
|
|
|
result, _ := paginator.Page()
|
|
|
|
So(result, ShouldBeEmpty)
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("Page", func() {
|
|
|
|
Convey("defaults", func() {
|
|
|
|
// By default expect unlimitted results sorted by severity
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 5)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
previousSeverity := 4
|
|
|
|
for _, cve := range cves {
|
|
|
|
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
|
|
|
|
previousSeverity = severityToInt[cve.Severity]
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "", cveinfo.PageInput{})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 30)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 30)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 30)
|
|
|
|
previousSeverity = 4
|
|
|
|
for _, cve := range cves {
|
|
|
|
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
|
|
|
|
previousSeverity = severityToInt[cve.Severity]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("no limit or offset", func() {
|
|
|
|
cveIds := []string{}
|
|
|
|
for i := 0; i < 30; i++ {
|
|
|
|
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "",
|
2023-02-27 14:23:18 -05:00
|
|
|
cveinfo.PageInput{SortBy: cveinfo.AlphabeticAsc})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 5)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
for i, cve := range cves {
|
|
|
|
So(cve.ID, ShouldEqual, cveIds[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(cveIds)
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
|
|
|
cveinfo.PageInput{SortBy: cveinfo.AlphabeticAsc})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 30)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 30)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 30)
|
|
|
|
for i, cve := range cves {
|
|
|
|
So(cve.ID, ShouldEqual, cveIds[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(sort.Reverse(sort.StringSlice(cveIds)))
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
|
|
|
cveinfo.PageInput{SortBy: cveinfo.AlphabeticDsc})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 30)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 30)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 30)
|
|
|
|
for i, cve := range cves {
|
|
|
|
So(cve.ID, ShouldEqual, cveIds[i])
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
|
|
|
cveinfo.PageInput{SortBy: cveinfo.SeverityDsc})
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 30)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 30)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 30)
|
|
|
|
previousSeverity := 4
|
|
|
|
for _, cve := range cves {
|
|
|
|
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
|
|
|
|
previousSeverity = severityToInt[cve.Severity]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("limit < len(cves)", func() {
|
|
|
|
cveIds := []string{}
|
|
|
|
for i := 0; i < 30; i++ {
|
|
|
|
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
|
|
|
|
}
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 3,
|
|
|
|
Offset: 1,
|
|
|
|
SortBy: cveinfo.AlphabeticAsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 3)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 3)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
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")
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 2,
|
|
|
|
Offset: 1,
|
|
|
|
SortBy: cveinfo.AlphabeticDsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 2)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
So(cves[0].ID, ShouldEqual, "CVE3")
|
|
|
|
So(cves[1].ID, ShouldEqual, "CVE2")
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 3,
|
|
|
|
Offset: 1,
|
|
|
|
SortBy: cveinfo.SeverityDsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 3)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 3)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
previousSeverity := 4
|
|
|
|
for _, cve := range cves {
|
|
|
|
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
|
|
|
|
previousSeverity = severityToInt[cve.Severity]
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(cveIds)
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 5,
|
|
|
|
Offset: 20,
|
|
|
|
SortBy: cveinfo.AlphabeticAsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 5)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 30)
|
|
|
|
for i, cve := range cves {
|
|
|
|
So(cve.ID, ShouldEqual, cveIds[i+20])
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("limit > len(cves)", func() {
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 6,
|
|
|
|
Offset: 3,
|
|
|
|
SortBy: cveinfo.AlphabeticAsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 2)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
So(cves[0].ID, ShouldEqual, "CVE3")
|
|
|
|
So(cves[1].ID, ShouldEqual, "CVE4")
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 6,
|
|
|
|
Offset: 3,
|
|
|
|
SortBy: cveinfo.AlphabeticDsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 2)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
So(cves[0].ID, ShouldEqual, "CVE1")
|
|
|
|
So(cves[1].ID, ShouldEqual, "CVE0")
|
|
|
|
|
2023-03-16 14:13:07 -05:00
|
|
|
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
2023-01-24 18:03:10 -05:00
|
|
|
Limit: 6,
|
|
|
|
Offset: 3,
|
|
|
|
SortBy: cveinfo.SeverityDsc,
|
2023-03-16 14:13:07 -05:00
|
|
|
},
|
|
|
|
)
|
2023-01-24 18:03:10 -05:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(len(cves), ShouldEqual, 2)
|
|
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
previousSeverity := 4
|
|
|
|
for _, cve := range cves {
|
|
|
|
So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity)
|
|
|
|
previousSeverity = severityToInt[cve.Severity]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|