zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/extensions/search/cve/pagination_test.go (about) 1 //go:build search 2 // +build search 3 4 package cveinfo_test 5 6 import ( 7 "context" 8 "fmt" 9 "sort" 10 "testing" 11 "time" 12 13 ispec "github.com/opencontainers/image-spec/specs-go/v1" 14 . "github.com/smartystreets/goconvey/convey" 15 16 cveinfo "zotregistry.io/zot/pkg/extensions/search/cve" 17 cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model" 18 "zotregistry.io/zot/pkg/log" 19 "zotregistry.io/zot/pkg/meta/boltdb" 20 . "zotregistry.io/zot/pkg/test/image-utils" 21 "zotregistry.io/zot/pkg/test/mocks" 22 ) 23 24 func TestCVEPagination(t *testing.T) { 25 Convey("CVE Pagination", t, func() { 26 params := boltdb.DBParameters{ 27 RootDir: t.TempDir(), 28 } 29 boltDriver, err := boltdb.GetBoltDriver(params) 30 So(err, ShouldBeNil) 31 32 metaDB, err := boltdb.New(boltDriver, log.NewLogger("debug", "")) 33 So(err, ShouldBeNil) 34 35 // Create metadb data for scannable image with vulnerabilities 36 timeStamp11 := time.Date(2008, 1, 1, 12, 0, 0, 0, time.UTC) 37 38 image := CreateImageWith(). 39 Layers([]Layer{{ 40 MediaType: ispec.MediaTypeImageLayerGzip, 41 Digest: ispec.DescriptorEmptyJSON.Digest, 42 Blob: ispec.DescriptorEmptyJSON.Data, 43 }}).ImageConfig(ispec.Image{Created: &timeStamp11}).Build() 44 45 err = metaDB.SetRepoReference(context.Background(), "repo1", "0.1.0", image.AsImageMeta()) 46 So(err, ShouldBeNil) 47 48 timeStamp12 := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC) 49 50 image2 := CreateImageWith(). 51 Layers([]Layer{{ 52 MediaType: ispec.MediaTypeImageLayerGzip, 53 Digest: ispec.DescriptorEmptyJSON.Digest, 54 Blob: ispec.DescriptorEmptyJSON.Data, 55 }}).ImageConfig(ispec.Image{Created: &timeStamp12}).Build() 56 57 err = metaDB.SetRepoReference(context.Background(), "repo1", "1.0.0", image2.AsImageMeta()) 58 So(err, ShouldBeNil) 59 60 // MetaDB loaded with initial data, mock the scanner 61 severityToInt := map[string]int{ 62 "UNKNOWN": 0, 63 "LOW": 1, 64 "MEDIUM": 2, 65 "HIGH": 3, 66 "CRITICAL": 4, 67 } 68 69 intToSeverity := make(map[int]string, len(severityToInt)) 70 for k, v := range severityToInt { 71 intToSeverity[v] = k 72 } 73 74 // Setup test CVE data in mock scanner 75 scanner := mocks.CveScannerMock{ 76 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 77 cveMap := map[string]cvemodel.CVE{} 78 79 if image == "repo1:0.1.0" { 80 for i := 0; i < 5; i++ { 81 cveMap[fmt.Sprintf("CVE%d", i)] = cvemodel.CVE{ 82 ID: fmt.Sprintf("CVE%d", i), 83 Severity: intToSeverity[i%5], 84 Title: fmt.Sprintf("Title for CVE%d", i), 85 Description: fmt.Sprintf("Description for CVE%d", i), 86 } 87 } 88 } 89 90 if image == "repo1:1.0.0" { 91 for i := 0; i < 30; i++ { 92 cveMap[fmt.Sprintf("CVE%d", i)] = cvemodel.CVE{ 93 ID: fmt.Sprintf("CVE%d", i), 94 Severity: intToSeverity[i%5], 95 Title: fmt.Sprintf("Title for CVE%d", i), 96 Description: fmt.Sprintf("Description for CVE%d", i), 97 } 98 } 99 } 100 101 // By default the image has no vulnerabilities 102 return cveMap, nil 103 }, 104 } 105 106 log := log.NewLogger("debug", "") 107 cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, MetaDB: metaDB} 108 109 ctx := context.Background() 110 111 Convey("create new paginator errors", func() { 112 paginator, err := cveinfo.NewCvePageFinder(-1, 10, cveinfo.AlphabeticAsc) 113 So(paginator, ShouldBeNil) 114 So(err, ShouldNotBeNil) 115 116 paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc) 117 So(paginator, ShouldBeNil) 118 So(err, ShouldNotBeNil) 119 120 paginator, err = cveinfo.NewCvePageFinder(2, 1, "wrong sorting criteria") 121 So(paginator, ShouldBeNil) 122 So(err, ShouldNotBeNil) 123 }) 124 125 Convey("Reset", func() { 126 paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc) 127 So(err, ShouldBeNil) 128 So(paginator, ShouldNotBeNil) 129 130 paginator.Add(cvemodel.CVE{}) 131 paginator.Add(cvemodel.CVE{}) 132 paginator.Add(cvemodel.CVE{}) 133 134 paginator.Reset() 135 136 result, _ := paginator.Page() 137 So(result, ShouldBeEmpty) 138 }) 139 140 Convey("Page", func() { 141 Convey("defaults", func() { 142 // By default expect unlimitted results sorted by severity 143 cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{}) 144 So(err, ShouldBeNil) 145 So(len(cves), ShouldEqual, 5) 146 So(pageInfo.ItemCount, ShouldEqual, 5) 147 So(pageInfo.TotalCount, ShouldEqual, 5) 148 previousSeverity := 4 149 for _, cve := range cves { 150 So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity) 151 previousSeverity = severityToInt[cve.Severity] 152 } 153 154 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{}) 155 So(err, ShouldBeNil) 156 So(len(cves), ShouldEqual, 30) 157 So(pageInfo.ItemCount, ShouldEqual, 30) 158 So(pageInfo.TotalCount, ShouldEqual, 30) 159 previousSeverity = 4 160 for _, cve := range cves { 161 So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity) 162 previousSeverity = severityToInt[cve.Severity] 163 } 164 }) 165 166 Convey("no limit or offset", func() { 167 cveIds := []string{} 168 for i := 0; i < 30; i++ { 169 cveIds = append(cveIds, fmt.Sprintf("CVE%d", i)) 170 } 171 172 cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", 173 cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc}) 174 So(err, ShouldBeNil) 175 So(len(cves), ShouldEqual, 5) 176 So(pageInfo.ItemCount, ShouldEqual, 5) 177 So(pageInfo.TotalCount, ShouldEqual, 5) 178 for i, cve := range cves { 179 So(cve.ID, ShouldEqual, cveIds[i]) 180 } 181 182 sort.Strings(cveIds) 183 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", 184 cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc}) 185 So(err, ShouldBeNil) 186 So(len(cves), ShouldEqual, 30) 187 So(pageInfo.ItemCount, ShouldEqual, 30) 188 So(pageInfo.TotalCount, ShouldEqual, 30) 189 for i, cve := range cves { 190 So(cve.ID, ShouldEqual, cveIds[i]) 191 } 192 193 sort.Sort(sort.Reverse(sort.StringSlice(cveIds))) 194 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", 195 cvemodel.PageInput{SortBy: cveinfo.AlphabeticDsc}) 196 So(err, ShouldBeNil) 197 So(len(cves), ShouldEqual, 30) 198 So(pageInfo.ItemCount, ShouldEqual, 30) 199 So(pageInfo.TotalCount, ShouldEqual, 30) 200 for i, cve := range cves { 201 So(cve.ID, ShouldEqual, cveIds[i]) 202 } 203 204 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", 205 cvemodel.PageInput{SortBy: cveinfo.SeverityDsc}) 206 So(err, ShouldBeNil) 207 So(len(cves), ShouldEqual, 30) 208 So(pageInfo.ItemCount, ShouldEqual, 30) 209 So(pageInfo.TotalCount, ShouldEqual, 30) 210 previousSeverity := 4 211 for _, cve := range cves { 212 So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity) 213 previousSeverity = severityToInt[cve.Severity] 214 } 215 }) 216 217 Convey("limit < len(cves)", func() { 218 cveIds := []string{} 219 for i := 0; i < 30; i++ { 220 cveIds = append(cveIds, fmt.Sprintf("CVE%d", i)) 221 } 222 223 cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 224 Limit: 3, 225 Offset: 1, 226 SortBy: cveinfo.AlphabeticAsc, 227 }, 228 ) 229 So(err, ShouldBeNil) 230 So(len(cves), ShouldEqual, 3) 231 So(pageInfo.ItemCount, ShouldEqual, 3) 232 So(pageInfo.TotalCount, ShouldEqual, 5) 233 So(cves[0].ID, ShouldEqual, "CVE1") // CVE0 is first ID and is not part of the page 234 So(cves[1].ID, ShouldEqual, "CVE2") 235 So(cves[2].ID, ShouldEqual, "CVE3") 236 237 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 238 Limit: 2, 239 Offset: 1, 240 SortBy: cveinfo.AlphabeticDsc, 241 }, 242 ) 243 So(err, ShouldBeNil) 244 So(len(cves), ShouldEqual, 2) 245 So(pageInfo.ItemCount, ShouldEqual, 2) 246 So(pageInfo.TotalCount, ShouldEqual, 5) 247 So(cves[0].ID, ShouldEqual, "CVE3") 248 So(cves[1].ID, ShouldEqual, "CVE2") 249 250 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 251 Limit: 3, 252 Offset: 1, 253 SortBy: cveinfo.SeverityDsc, 254 }, 255 ) 256 So(err, ShouldBeNil) 257 So(len(cves), ShouldEqual, 3) 258 So(pageInfo.ItemCount, ShouldEqual, 3) 259 So(pageInfo.TotalCount, ShouldEqual, 5) 260 previousSeverity := 4 261 for _, cve := range cves { 262 So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity) 263 previousSeverity = severityToInt[cve.Severity] 264 } 265 266 sort.Strings(cveIds) 267 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{ 268 Limit: 5, 269 Offset: 20, 270 SortBy: cveinfo.AlphabeticAsc, 271 }, 272 ) 273 So(err, ShouldBeNil) 274 So(len(cves), ShouldEqual, 5) 275 So(pageInfo.ItemCount, ShouldEqual, 5) 276 So(pageInfo.TotalCount, ShouldEqual, 30) 277 for i, cve := range cves { 278 So(cve.ID, ShouldEqual, cveIds[i+20]) 279 } 280 }) 281 282 Convey("limit > len(cves)", func() { 283 cves, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 284 Limit: 6, 285 Offset: 3, 286 SortBy: cveinfo.AlphabeticAsc, 287 }, 288 ) 289 So(err, ShouldBeNil) 290 So(len(cves), ShouldEqual, 2) 291 So(pageInfo.ItemCount, ShouldEqual, 2) 292 So(pageInfo.TotalCount, ShouldEqual, 5) 293 So(cves[0].ID, ShouldEqual, "CVE3") 294 So(cves[1].ID, ShouldEqual, "CVE4") 295 296 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 297 Limit: 6, 298 Offset: 3, 299 SortBy: cveinfo.AlphabeticDsc, 300 }, 301 ) 302 So(err, ShouldBeNil) 303 So(len(cves), ShouldEqual, 2) 304 So(pageInfo.ItemCount, ShouldEqual, 2) 305 So(pageInfo.TotalCount, ShouldEqual, 5) 306 So(cves[0].ID, ShouldEqual, "CVE1") 307 So(cves[1].ID, ShouldEqual, "CVE0") 308 309 cves, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ 310 Limit: 6, 311 Offset: 3, 312 SortBy: cveinfo.SeverityDsc, 313 }, 314 ) 315 So(err, ShouldBeNil) 316 So(len(cves), ShouldEqual, 2) 317 So(pageInfo.ItemCount, ShouldEqual, 2) 318 So(pageInfo.TotalCount, ShouldEqual, 5) 319 previousSeverity := 4 320 for _, cve := range cves { 321 So(severityToInt[cve.Severity], ShouldBeLessThanOrEqualTo, previousSeverity) 322 previousSeverity = severityToInt[cve.Severity] 323 } 324 }) 325 }) 326 }) 327 }