zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/search/cve/cve_test.go (about) 1 //go:build search 2 // +build search 3 4 //nolint:lll,gosimple 5 package cveinfo_test 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "io" 12 "net/url" 13 "os" 14 "path" 15 "strings" 16 "testing" 17 "time" 18 19 regTypes "github.com/google/go-containerregistry/pkg/v1/types" 20 godigest "github.com/opencontainers/go-digest" 21 ispec "github.com/opencontainers/image-spec/specs-go/v1" 22 . "github.com/smartystreets/goconvey/convey" 23 "gopkg.in/resty.v1" 24 25 zerr "zotregistry.dev/zot/errors" 26 "zotregistry.dev/zot/pkg/api" 27 "zotregistry.dev/zot/pkg/api/config" 28 "zotregistry.dev/zot/pkg/api/constants" 29 apiErr "zotregistry.dev/zot/pkg/api/errors" 30 zcommon "zotregistry.dev/zot/pkg/common" 31 extconf "zotregistry.dev/zot/pkg/extensions/config" 32 "zotregistry.dev/zot/pkg/extensions/monitoring" 33 cveinfo "zotregistry.dev/zot/pkg/extensions/search/cve" 34 cvecache "zotregistry.dev/zot/pkg/extensions/search/cve/cache" 35 cvemodel "zotregistry.dev/zot/pkg/extensions/search/cve/model" 36 "zotregistry.dev/zot/pkg/log" 37 "zotregistry.dev/zot/pkg/meta" 38 "zotregistry.dev/zot/pkg/meta/boltdb" 39 mTypes "zotregistry.dev/zot/pkg/meta/types" 40 "zotregistry.dev/zot/pkg/storage" 41 "zotregistry.dev/zot/pkg/storage/local" 42 test "zotregistry.dev/zot/pkg/test/common" 43 . "zotregistry.dev/zot/pkg/test/image-utils" 44 "zotregistry.dev/zot/pkg/test/mocks" 45 ociutils "zotregistry.dev/zot/pkg/test/oci-utils" 46 ) 47 48 type CveResult struct { 49 ImgList ImgList `json:"data"` 50 Errors []ErrorGQL `json:"errors"` 51 } 52 53 //nolint:tagliatelle // graphQL schema 54 type ImgListWithCVEFixed struct { 55 Images []ImageInfo `json:"ImageListWithCVEFixed"` 56 } 57 58 type ImageInfo struct { 59 RepoName string 60 LastUpdated time.Time 61 } 62 63 //nolint:tagliatelle // graphQL schema 64 type ImgList struct { 65 CVEResultForImage CVEResultForImage `json:"CVEListForImage"` 66 } 67 68 type ErrorGQL struct { 69 Message string `json:"message"` 70 Path []string `json:"path"` 71 } 72 73 //nolint:tagliatelle // graphQL schema 74 type CVEResultForImage struct { 75 Tag string `json:"Tag"` 76 CVEList []cvemodel.CVE `json:"CVEList"` 77 } 78 79 func testSetup(t *testing.T) (string, error) { 80 t.Helper() 81 dir := t.TempDir() 82 83 err := generateTestData(dir) 84 if err != nil { 85 return "", err 86 } 87 88 testStorageCtrl := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", "")) 89 90 err = WriteImageToFileSystem(CreateRandomVulnerableImage(), "zot-test", "0.0.1", testStorageCtrl) 91 if err != nil { 92 return "", err 93 } 94 95 err = WriteImageToFileSystem(CreateRandomVulnerableImage(), "zot-cve-test", "0.0.1", testStorageCtrl) 96 if err != nil { 97 return "", err 98 } 99 100 return dir, nil 101 } 102 103 func generateTestData(dbDir string) error { //nolint: gocyclo 104 // Image dir with no files 105 err := os.Mkdir(path.Join(dbDir, "zot-noindex-test"), 0o755) 106 if err != nil { 107 return err 108 } 109 110 err = os.Mkdir(path.Join(dbDir, "zot-nonreadable-test"), 0o755) 111 if err != nil { 112 return err 113 } 114 115 index := ispec.Index{} 116 index.SchemaVersion = 2 117 118 buf, err := json.Marshal(index) 119 if err != nil { 120 return err 121 } 122 123 if err = os.WriteFile(path.Join(dbDir, "zot-nonreadable-test", "index.json"), //nolint:gosec // test code 124 buf, 0o111); err != nil { 125 return err 126 } 127 128 // Image dir with invalid index.json 129 err = os.Mkdir(path.Join(dbDir, "zot-squashfs-invalid-index"), 0o755) 130 if err != nil { 131 return err 132 } 133 134 content := `{"schemaVersion": 2,"manifests"[{"mediaType": "application/vnd.oci.image.manifest.v1+json","digest": "sha256:2a9b097b4e4c613dd8185eba55163201a221909f3d430f8df87cd3639afc5929","size": 1240,"annotations": {"org.opencontainers.image.ref.name": "commit-aaa7c6e7-squashfs"},"platform": {"architecture": "amd64","os": "linux"}}]}` 135 136 err = makeTestFile(path.Join(dbDir, "zot-squashfs-invalid-index", "index.json"), content) 137 if err != nil { 138 return err 139 } 140 141 // Image dir with no blobs 142 err = os.Mkdir(path.Join(dbDir, "zot-squashfs-noblobs"), 0o755) 143 if err != nil { 144 return err 145 } 146 147 content = `{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:2a9b097b4e4c613dd8185eba55163201a221909f3d430f8df87cd3639afc5929","size":1240,"annotations":{"org.opencontainers.image.ref.name":"commit-aaa7c6e7-squashfs"},"platform":{"architecture":"amd64","os":"linux"}}]}` 148 149 err = makeTestFile(path.Join(dbDir, "zot-squashfs-noblobs", "index.json"), content) 150 if err != nil { 151 return err 152 } 153 154 // Image dir with invalid blob 155 err = os.MkdirAll(path.Join(dbDir, "zot-squashfs-invalid-blob", "blobs/sha256"), 0o755) 156 if err != nil { 157 return err 158 } 159 160 content = fmt.Sprint(`{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:2a9b097b4e4c613dd8185eba55163201a221909f3d430f8df87cd3639afc5929","size":1240,"annotations":{"org.opencontainers.image.ref.name":"commit-aaa7c6e7-squashfs"},"platform":{"architecture":"amd64","os":"linux"}}]} 161 `) 162 163 err = makeTestFile(path.Join(dbDir, "zot-squashfs-invalid-blob", "index.json"), content) 164 if err != nil { 165 return err 166 } 167 168 content = fmt.Sprint(`{"schemaVersion":2,"config"{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:4b37d4133908ac9a3032ba996020f2ad41354a616b071ca7e726a1df18a0f354","size":1691},"layers":[{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:a01a66356aace53222e92fb6fd990b23eb44ab0e58dff6f853fa9f771ecf3ac5","size":54996992},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:91c26d6934ef2b5c5c4d8458af9bfc4ca46cf90c22380193154964abc8298a7a","size":52330496},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:f281a550ca49746cfc6b8f1ac52f8086b3d5845db2ca18fde980dab62ae3bf7d","size":23343104},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:7ee02568717acdda336c9d56d4dc6ea7f3b1c553e43bb0c0ecc6fd3bbd059d1a","size":5910528},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:8fb33b130588b239235dedd560cdf49d29bbf6f2db5419ac68e4592a85c1f416","size":123269120},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:1b49f0b33d4a696bb94d84c9acab3623e2c195bfb446d446a583a2f9f27b04c3","size":113901568}],"annotations":{"com.cisco.stacker.git_version":"7-dev19-63-gaaa7c6e7","ws.tycho.stacker.git_version":"0.3.26"}} 169 `) 170 171 err = makeTestFile(path.Join(dbDir, "zot-squashfs-invalid-blob", "blobs/sha256", "2a9b097b4e4c613dd8185eba55163201a221909f3d430f8df87cd3639afc5929"), content) 172 if err != nil { 173 return err 174 } 175 176 // Create a squashfs image 177 178 err = os.MkdirAll(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256"), 0o755) 179 if err != nil { 180 return err 181 } 182 183 il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion} 184 buf, err = json.Marshal(il) 185 186 if err != nil { 187 return err 188 } 189 190 if err = os.WriteFile(path.Join(dbDir, "zot-squashfs-test", "oci-layout"), buf, 0o644); err != nil { //nolint: gosec 191 return err 192 } 193 194 err = os.Mkdir(path.Join(dbDir, "zot-squashfs-test", ".uploads"), 0o755) 195 if err != nil { 196 return err 197 } 198 199 content = fmt.Sprint(`{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.25"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:45df53588e59759a12bd3eca553cdc9063939baac9a28d7ebb6101e4ec230b76","size":873,"annotations":{"org.opencontainers.image.ref.name":"0.3.22-squashfs"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:71448405a4b89539fcfa581afb4dc7d257f98857686b8138b08a1c539f313099","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.19"},"platform":{"architecture":"amd64","os":"linux"}}]}`) 200 201 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "index.json"), content) 202 if err != nil { 203 return err 204 } 205 206 content = fmt.Sprint(`{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:c5c2fd2b07ad4cb025cf20936d6bce6085584b8377780599be4da8a91739f0e8","size":1738},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:3414b5ef0ad2f0390daaf55b63c422eeedef6191d47036a69d8ee396fabdce72","size":58993484},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:a3b04fff744c13dfa4883e01fa35e01af8daa7f72d9e9b6b7fad1f28843846b6","size":55631733},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:754f517f58f302190aa94e025c25890c18e1e811127aed1ef25c189278ec4ab0","size":113612795},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:ec004cd43488b803d6e232599e83a3164394d44fcd9f44755fed7b5791087ede","size":108889651}],"annotations":{"ws.tycho.stacker.git_version":"0.3.19"}}`) 207 208 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "71448405a4b89539fcfa581afb4dc7d257f98857686b8138b08a1c539f313099"), content) 209 if err != nil { 210 return err 211 } 212 213 content = fmt.Sprint(`{"created": "2020-04-08T05:32:49.805795564Z","author": "","architecture": "amd64","os": "linux","config": {"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]},"rootfs": {"type": "layers","diff_ids": []},"history": [{"created": "2020-04-08T05:08:43.590117872Z","created_by": "stacker umoci repack"}, {"created": "2020-04-08T05:08:53.213437118Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:12:15.999154739Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:12:31.0513552Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:20:38.068800557Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:21:01.956154957Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:32:24.582132274Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:32:49.805795564Z","created_by": "stacker build","author": "","empty_layer": true}]}`) 214 215 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "c5c2fd2b07ad4cb025cf20936d6bce6085584b8377780599be4da8a91739f0e8"), content) 216 if err != nil { 217 return err 218 } 219 220 content = fmt.Sprint(`{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a6","size":1740},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:f8b7e41ce10d9a0f614f068326c43431c2777e6fc346f729c2a643bfab24af83","size":59451113},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:9ca9274f196b56a708a7a672d3de88184c0158a30744d355dd0411f3a6850fa6","size":55685756},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:6c1ca50788f93937e9ce9341b564f86cbbcd28e367ed6a57cfc776aee4a9d050","size":113726186},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:d1a92139df86bdf00c818db75bf1ecc860857d142b426e9971a62f5f90e2aa57","size":108755643}],"annotations":{"ws.tycho.stacker.git_version":"0.3.25"}}`) 221 222 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb"), content) 223 if err != nil { 224 return err 225 } 226 227 content = fmt.Sprint(`{"created": "2020-04-08T05:32:49.805795564Z","author": "","architecture": "amd64","os": "linux","config": {"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]},"rootfs": {"type": "layers","diff_ids": []},"history": [{"created": "2020-05-11T18:17:24.516727354Z","created_by": "stacker umoci repack"}, {"created": "2020-04-08T05:08:53.213437118Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:12:15.999154739Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:12:31.0513552Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:20:38.068800557Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:21:01.956154957Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:32:24.582132274Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:32:49.805795564Z","created_by": "stacker build","author": "","empty_layer": true}]}`) 228 229 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a6"), content) 230 if err != nil { 231 return err 232 } 233 234 content = fmt.Sprint(`{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:1fc1d045b241b04fea54333d76d4f57eb1961f9a314413f02a956b76e77a99f0","size":1218},"layers":[{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:c40d72b1556293c00a3e4b6c64c46ef4c7ae4d876dc18bad942b7d1903e8e5b7","size":54745420},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:4115890e3e2563e545e03f264bfecb0097e24e02306ae3e7668dea52e00c6cc2","size":52213357},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:91859e13e0cf704d5405199d73a2d1a0718391dbb183a77c4cb85d99e923ff57","size":109479329},{"mediaType":"application/vnd.oci.image.layer.squashfs","digest":"sha256:20aef84d8098d47a0643a2f99ce05f0deed957b3a259fb708c538f23ed97cc82","size":103996238}],"annotations":{"ws.tycho.stacker.git_version":"0.3.25"}}`) 235 236 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "45df53588e59759a12bd3eca553cdc9063939baac9a28d7ebb6101e4ec230b76"), content) 237 if err != nil { 238 return err 239 } 240 241 content = fmt.Sprint(`{"created": "2020-04-08T05:32:49.805795564Z","author": "","architecture": "amd64","os": "linux","config": {"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]},"rootfs": {"type": "layers","diff_ids": []},"history": [{"created": "2020-05-11T18:17:24.516727354Z","created_by": "stacker umoci repack"}, {"created": "2020-04-08T05:08:53.213437118Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:12:15.999154739Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-05-11T19:30:02.467956112Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:20:38.068800557Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:21:01.956154957Z","created_by": "stacker build","author": "","empty_layer": true}, {"created": "2020-04-08T05:32:24.582132274Z","created_by": "stacker umoci repack","author": ""}, {"created": "2020-04-08T05:32:49.805795564Z","created_by": "stacker build","author": "","empty_layer": true}]}`) 242 243 err = makeTestFile(path.Join(dbDir, "zot-squashfs-test", "blobs/sha256", "1fc1d045b241b04fea54333d76d4f57eb1961f9a314413f02a956b76e77a99f0"), content) 244 if err != nil { 245 return err 246 } 247 248 // Create a image with invalid layer blob 249 250 err = os.MkdirAll(path.Join(dbDir, "zot-invalid-layer", "blobs/sha256"), 0o755) 251 if err != nil { 252 return err 253 } 254 255 content = fmt.Sprint(`{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.25"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:45df53588e59759a12bd3eca553cdc9063939baac9a28d7ebb6101e4ec230b76","size":873,"annotations":{"org.opencontainers.image.ref.name":"0.3.22-squashfs"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:71448405a4b89539fcfa581afb4dc7d257f98857686b8138b08a1c539f313099","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.19"},"platform":{"architecture":"amd64","os":"linux"}}]}`) 256 257 err = makeTestFile(path.Join(dbDir, "zot-invalid-layer", "index.json"), content) 258 if err != nil { 259 return err 260 } 261 262 content = fmt.Sprint(`{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a6","size":1740},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:f8b7e41ce10d9a0f614f068326c43431c2777e6fc346f729c2a643bfab24af83","size":59451113},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:9ca9274f196b56a708a7a672d3de88184c0158a30744d355dd0411f3a6850fa6","size":55685756},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:6c1ca50788f93937e9ce9341b564f86cbbcd28e367ed6a57cfc776aee4a9d050","size":113726186},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:d1a92139df86bdf00c818db75bf1ecc860857d142b426e9971a62f5f90e2aa57","size":108755643}],"annotations":{"ws.tycho.stacker.git_version":"0.3.25"}}`) 263 264 err = makeTestFile(path.Join(dbDir, "zot-invalid-layer", "blobs/sha256", "eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb"), content) 265 if err != nil { 266 return err 267 } 268 269 content = fmt.Sprint(`{"created":"2020-05-11T19:12:23.239785708Z","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","architecture":"amd64","os":"linux","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]},"rootfs":{"type":"layers","diff_ids":["sha256:8817d297aa60796f41f559ba688d29b31830854014091233575d474f3a6e808e","sha256:dd5a09481ae1f5caf8d1dbc87bc7f86a01af030796467ba25851ad69964d226d","sha256:a8bce2aaf5ce6f1a5459b72de64927a1e507a911453789bf60df06752222cacd","sha256:dc0b750a934e8f376af23de6dcab1af282967498844a6510aed2c61277f20c11"]},"history":[{"created":"2020-05-11T18:17:24.516727354Z","created_by":"stacker umoci repack"},{"created":"2020-05-11T18:17:33.111086359Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T18:18:43.147035914Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T18:19:03.346279546Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T18:27:01.623678656Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T18:27:23.420280147Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T19:11:54.886053615Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T19:12:23.239785708Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true}]`) 270 271 err = makeTestFile(path.Join(dbDir, "zot-invalid-layer", "blobs/sha256", "5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a6"), content) 272 if err != nil { 273 return err 274 } 275 276 // Create a image with no layer blob 277 278 err = os.MkdirAll(path.Join(dbDir, "zot-no-layer", "blobs/sha256"), 0o755) 279 if err != nil { 280 return err 281 } 282 283 content = fmt.Sprint(`{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.25"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:45df53588e59759a12bd3eca553cdc9063939baac9a28d7ebb6101e4ec230b76","size":873,"annotations":{"org.opencontainers.image.ref.name":"0.3.22-squashfs"},"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:71448405a4b89539fcfa581afb4dc7d257f98857686b8138b08a1c539f313099","size":886,"annotations":{"org.opencontainers.image.ref.name":"0.3.19"},"platform":{"architecture":"amd64","os":"linux"}}]}`) 284 285 err = makeTestFile(path.Join(dbDir, "zot-no-layer", "index.json"), content) 286 if err != nil { 287 return err 288 } 289 290 content = fmt.Sprint(`{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a6","size":1740},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:f8b7e41ce10d9a0f614f068326c43431c2777e6fc346f729c2a643bfab24af83","size":59451113},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:9ca9274f196b56a708a7a672d3de88184c0158a30744d355dd0411f3a6850fa6","size":55685756},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:6c1ca50788f93937e9ce9341b564f86cbbcd28e367ed6a57cfc776aee4a9d050","size":113726186},{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:d1a92139df86bdf00c818db75bf1ecc860857d142b426e9971a62f5f90e2aa57","size":108755643}],"annotations":{"ws.tycho.stacker.git_version":"0.3.25"}}`) 291 292 err = makeTestFile(path.Join(dbDir, "zot-no-layer", "blobs/sha256", "eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb"), content) 293 if err != nil { 294 return err 295 } 296 297 content = fmt.Sprint(`{"created":"2020-05-11T19:12:23.239785708Z","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","architecture":"amd64","os":"linux","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"]},"rootfs":{"type":"layers","diff_ids":["sha256:8817d297aa60796f41f559ba688d29b31830854014091233575d474f3a6e808e","sha256:dd5a09481ae1f5caf8d1dbc87bc7f86a01af030796467ba25851ad69964d226d","sha256:a8bce2aaf5ce6f1a5459b72de64927a1e507a911453789bf60df06752222cacd","sha256:dc0b750a934e8f376af23de6dcab1af282967498844a6510aed2c61277f20c11"]},"history":[{"created":"2020-05-11T18:17:24.516727354Z","created_by":"stacker umoci repack"},{"created":"2020-05-11T18:17:33.111086359Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T18:18:43.147035914Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T18:19:03.346279546Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T18:27:01.623678656Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T18:27:23.420280147Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true},{"created":"2020-05-11T19:11:54.886053615Z","created_by":"stacker umoci repack","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI"},{"created":"2020-05-11T19:12:23.239785708Z","created_by":"stacker build","author":"root@jenkinsProduction-Atom-Full-Build-c3-master-159CI","empty_layer":true}]`) 298 299 err = makeTestFile(path.Join(dbDir, "zot-no-layer", "blobs/sha256", "5f00b5570a5561a6f9b7e66e4f26e2e30c4d09b43a8d3f993f3c1c99be6137a"), content) 300 if err != nil { 301 return err 302 } 303 304 return nil 305 } 306 307 func makeTestFile(fileName, content string) error { 308 if err := os.WriteFile(fileName, []byte(content), 0o600); err != nil { 309 panic(err) 310 } 311 312 return nil 313 } 314 315 func TestImageFormat(t *testing.T) { 316 Convey("Test valid image", t, func() { 317 log := log.NewLogger("debug", "") 318 imgDir := "../../../../test/data" 319 dbDir := t.TempDir() 320 321 metrics := monitoring.NewMetricsServer(false, log) 322 defaultStore := local.NewImageStore(imgDir, false, false, log, metrics, nil, nil) 323 storeController := storage.StoreController{DefaultStore: defaultStore} 324 325 params := boltdb.DBParameters{ 326 RootDir: dbDir, 327 } 328 boltDriver, err := boltdb.GetBoltDriver(params) 329 So(err, ShouldBeNil) 330 331 metaDB, err := boltdb.New(boltDriver, log) 332 So(err, ShouldBeNil) 333 334 err = meta.ParseStorage(metaDB, storeController, log) 335 So(err, ShouldBeNil) 336 337 scanner := cveinfo.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 338 339 isValidImage, err := scanner.IsImageFormatScannable("zot-test", "") 340 So(err, ShouldNotBeNil) 341 So(isValidImage, ShouldEqual, false) 342 343 isValidImage, err = scanner.IsImageFormatScannable("zot-test", "0.0.1") 344 So(err, ShouldBeNil) 345 So(isValidImage, ShouldEqual, true) 346 347 isValidImage, err = scanner.IsImageFormatScannable("zot-test", "0.0.") 348 So(err, ShouldNotBeNil) 349 So(isValidImage, ShouldEqual, false) 350 351 isValidImage, err = scanner.IsImageFormatScannable("zot-noindex-test", "") 352 So(err, ShouldNotBeNil) 353 So(isValidImage, ShouldEqual, false) 354 355 isValidImage, err = scanner.IsImageFormatScannable("zot--tet", "") 356 So(err, ShouldNotBeNil) 357 So(isValidImage, ShouldEqual, false) 358 359 isValidImage, err = scanner.IsImageFormatScannable("zot-noindex-test", "") 360 So(err, ShouldNotBeNil) 361 So(isValidImage, ShouldEqual, false) 362 363 isValidImage, err = scanner.IsImageFormatScannable("zot-squashfs-noblobs", "") 364 So(err, ShouldNotBeNil) 365 So(isValidImage, ShouldEqual, false) 366 367 isValidImage, err = scanner.IsImageFormatScannable("zot-squashfs-invalid-index", "") 368 So(err, ShouldNotBeNil) 369 So(isValidImage, ShouldEqual, false) 370 371 isValidImage, err = scanner.IsImageFormatScannable("zot-squashfs-invalid-blob", "") 372 So(err, ShouldNotBeNil) 373 So(isValidImage, ShouldEqual, false) 374 375 isValidImage, err = scanner.IsImageFormatScannable("zot-squashfs-test:0.3.22-squashfs", "") 376 So(err, ShouldNotBeNil) 377 So(isValidImage, ShouldEqual, false) 378 379 isValidImage, err = scanner.IsImageFormatScannable("zot-nonreadable-test", "") 380 So(err, ShouldNotBeNil) 381 So(isValidImage, ShouldEqual, false) 382 }) 383 384 Convey("isIndexScanable", t, func() { 385 log := log.NewLogger("debug", "") 386 387 metaDB := &mocks.MetaDBMock{ 388 GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) { 389 return mTypes.RepoMeta{ 390 Tags: map[mTypes.Tag]mTypes.Descriptor{ 391 "tag": { 392 MediaType: ispec.MediaTypeImageIndex, 393 Digest: godigest.FromString("digest").String(), 394 }, 395 }, 396 }, nil 397 }, 398 GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) { 399 return mTypes.ImageMeta{ 400 MediaType: ispec.MediaTypeImageIndex, 401 Digest: godigest.FromString("digest"), 402 Index: &ispec.Index{}, 403 }, nil 404 }, 405 } 406 storeController := storage.StoreController{ 407 DefaultStore: mocks.MockedImageStore{}, 408 } 409 410 scanner := cveinfo.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 411 412 isScanable, err := scanner.IsImageFormatScannable("repo", "tag") 413 So(err, ShouldBeNil) 414 So(isScanable, ShouldBeTrue) 415 }) 416 } 417 418 func TestCVESearchDisabled(t *testing.T) { 419 Convey("Test with CVE search disabled", t, func() { 420 port := test.GetFreePort() 421 baseURL := test.GetBaseURL(port) 422 conf := config.New() 423 conf.HTTP.Port = port 424 username, seedUser := test.GenerateRandomString() 425 password, seedPass := test.GenerateRandomString() 426 htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password)) 427 defer os.Remove(htpasswdPath) 428 429 conf.HTTP.Auth = &config.AuthConfig{ 430 HTPasswd: config.AuthHTPasswd{ 431 Path: htpasswdPath, 432 }, 433 } 434 435 dbDir := t.TempDir() 436 437 conf.Storage.RootDirectory = dbDir 438 defaultVal := true 439 searchConfig := &extconf.SearchConfig{ 440 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 441 } 442 conf.Extensions = &extconf.ExtensionConfig{ 443 Search: searchConfig, 444 } 445 446 logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt") 447 if err != nil { 448 panic(err) 449 } 450 451 logPath := logFile.Name() 452 defer os.Remove(logPath) 453 454 writers := io.MultiWriter(os.Stdout, logFile) 455 456 ctlr := api.NewController(conf) 457 ctlr.Log.Info().Int64("seedUser", seedUser).Int64("seedPass", seedPass).Msg("random seed for username & password") 458 ctlr.Log.Logger = ctlr.Log.Output(writers) 459 ctrlManager := test.NewControllerManager(ctlr) 460 461 ctrlManager.StartAndWait(port) 462 463 // Wait for trivy db to download 464 found, err := test.ReadLogFileAndSearchString(logPath, "cve config not provided, skipping cve-db update", 90*time.Second) 465 So(err, ShouldBeNil) 466 So(found, ShouldBeTrue) 467 468 defer ctrlManager.StopServer() 469 470 resp, _ := resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 471 So(string(resp.Body()), ShouldContainSubstring, "cve search is disabled") 472 So(resp.StatusCode(), ShouldEqual, 200) 473 474 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(id:\"CVE-201-20482\"){Results{RepoName%20Tag}}}") 475 So(string(resp.Body()), ShouldContainSubstring, "cve search is disabled") 476 So(resp.StatusCode(), ShouldEqual, 200) 477 478 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + "randomId" + "\",image:\"zot-test\"){Results{RepoName%20LastUpdated}}}") 479 So(resp, ShouldNotBeNil) 480 So(string(resp.Body()), ShouldContainSubstring, "cve search is disabled") 481 So(resp.StatusCode(), ShouldEqual, 200) 482 }) 483 } 484 485 func TestCVESearch(t *testing.T) { 486 Convey("Test image vulnerability scanning", t, func() { 487 updateDuration, _ := time.ParseDuration("1h") 488 port := test.GetFreePort() 489 baseURL := test.GetBaseURL(port) 490 conf := config.New() 491 conf.HTTP.Port = port 492 username, seedUser := test.GenerateRandomString() 493 password, seedPass := test.GenerateRandomString() 494 htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password)) 495 defer os.Remove(htpasswdPath) 496 497 dbDir, err := testSetup(t) 498 So(err, ShouldBeNil) 499 500 conf.HTTP.Auth = &config.AuthConfig{ 501 HTPasswd: config.AuthHTPasswd{ 502 Path: htpasswdPath, 503 }, 504 } 505 506 conf.Storage.RootDirectory = dbDir 507 508 trivyConfig := &extconf.TrivyConfig{ 509 DBRepository: "ghcr.io/project-zot/trivy-db", 510 } 511 cveConfig := &extconf.CVEConfig{ 512 UpdateInterval: updateDuration, 513 Trivy: trivyConfig, 514 } 515 defaultVal := true 516 searchConfig := &extconf.SearchConfig{ 517 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 518 CVE: cveConfig, 519 } 520 conf.Extensions = &extconf.ExtensionConfig{ 521 Search: searchConfig, 522 } 523 524 logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt") 525 if err != nil { 526 panic(err) 527 } 528 529 logPath := logFile.Name() 530 defer os.Remove(logPath) 531 532 writers := io.MultiWriter(os.Stdout, logFile) 533 534 ctlr := api.NewController(conf) 535 ctlr.Log.Logger = ctlr.Log.Output(writers) 536 ctlr.Log.Info().Int64("seedUser", seedUser).Int64("seedPass", seedPass).Msg("random seed for username & password") 537 ctrlManager := test.NewControllerManager(ctlr) 538 539 ctrlManager.StartAndWait(port) 540 541 // trivy db download fail 542 err = os.Mkdir(dbDir+"/_trivy", 0o000) 543 So(err, ShouldBeNil) 544 found, err := test.ReadLogFileAndSearchString(logPath, "failed to download trivy-db to destination dir", 180*time.Second) 545 So(err, ShouldBeNil) 546 So(found, ShouldBeTrue) 547 548 err = os.Chmod(dbDir+"/_trivy", 0o755) 549 So(err, ShouldBeNil) 550 551 // Wait for trivy db to download 552 found, err = test.ReadLogFileAndSearchString(logPath, "cve-db update completed, next update scheduled after interval", 180*time.Second) 553 So(err, ShouldBeNil) 554 So(found, ShouldBeTrue) 555 556 defer ctrlManager.StopServer() 557 558 // without creds, should get access error 559 resp, err := resty.R().Get(baseURL + "/v2/") 560 So(err, ShouldBeNil) 561 So(resp, ShouldNotBeNil) 562 So(resp.StatusCode(), ShouldEqual, 401) 563 var apiErr apiErr.Error 564 err = json.Unmarshal(resp.Body(), &apiErr) 565 So(err, ShouldBeNil) 566 567 resp, err = resty.R().Get(baseURL + constants.FullSearchPrefix) 568 So(err, ShouldBeNil) 569 So(resp, ShouldNotBeNil) 570 So(resp.StatusCode(), ShouldEqual, 401) 571 err = json.Unmarshal(resp.Body(), &apiErr) 572 So(err, ShouldBeNil) 573 574 // with creds, should get expected status code 575 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL) 576 So(resp, ShouldNotBeNil) 577 So(resp.StatusCode(), ShouldEqual, 404) 578 579 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + "/v2/") 580 So(resp, ShouldNotBeNil) 581 So(resp.StatusCode(), ShouldEqual, 200) 582 583 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix) 584 So(resp, ShouldNotBeNil) 585 So(resp.StatusCode(), ShouldEqual, 422) 586 587 var cveResult CveResult 588 contains := false 589 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 590 err = json.Unmarshal(resp.Body(), &cveResult) 591 So(err, ShouldBeNil) 592 for _, err := range cveResult.Errors { 593 result := strings.Contains(err.Message, "no reference provided") 594 if result { 595 contains = result 596 } 597 } 598 So(contains, ShouldBeTrue) 599 600 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 601 So(resp, ShouldNotBeNil) 602 So(resp.StatusCode(), ShouldEqual, 200) 603 604 err = json.Unmarshal(resp.Body(), &cveResult) 605 So(err, ShouldBeNil) 606 So(len(cveResult.ImgList.CVEResultForImage.CVEList), ShouldNotBeZeroValue) 607 608 cveid := cveResult.ImgList.CVEResultForImage.CVEList[0].ID 609 610 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-test\"){Results{RepoName%20LastUpdated}}}") 611 So(resp, ShouldNotBeNil) 612 So(resp.StatusCode(), ShouldEqual, 200) 613 614 var imgListWithCVEFixed ImgListWithCVEFixed 615 err = json.Unmarshal(resp.Body(), &imgListWithCVEFixed) 616 So(err, ShouldBeNil) 617 So(len(imgListWithCVEFixed.Images), ShouldEqual, 0) 618 619 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-cve-test\"){Results{RepoName%20LastUpdated}}}") 620 So(resp, ShouldNotBeNil) 621 So(resp.StatusCode(), ShouldEqual, 200) 622 623 err = json.Unmarshal(resp.Body(), &imgListWithCVEFixed) 624 So(err, ShouldBeNil) 625 So(len(imgListWithCVEFixed.Images), ShouldEqual, 0) 626 627 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-test\"){Results{RepoName%20LastUpdated}}}") 628 So(resp, ShouldNotBeNil) 629 So(resp.StatusCode(), ShouldEqual, 200) 630 631 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"b/zot-squashfs-test:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 632 So(resp, ShouldNotBeNil) 633 So(resp.StatusCode(), ShouldEqual, 200) 634 635 var cveSquashFSResult CveResult 636 err = json.Unmarshal(resp.Body(), &cveSquashFSResult) 637 So(err, ShouldBeNil) 638 So(len(cveSquashFSResult.ImgList.CVEResultForImage.CVEList), ShouldBeZeroValue) 639 640 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-noindex:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 641 So(resp, ShouldNotBeNil) 642 So(resp.StatusCode(), ShouldEqual, 200) 643 644 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-squashfs-noindex\"){Results{RepoName%20LastUpdated}}}") 645 So(resp, ShouldNotBeNil) 646 So(resp.StatusCode(), ShouldEqual, 200) 647 648 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-invalid-index:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 649 So(resp, ShouldNotBeNil) 650 So(resp.StatusCode(), ShouldEqual, 200) 651 652 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-squashfs-invalid-index\"){Results{RepoName%20LastUpdated}}}") 653 So(resp, ShouldNotBeNil) 654 So(resp.StatusCode(), ShouldEqual, 200) 655 656 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-noblobs:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 657 So(resp, ShouldNotBeNil) 658 So(resp.StatusCode(), ShouldEqual, 200) 659 660 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-squashfs-noblob\"){Results{RepoName%20LastUpdated}}}") 661 So(resp, ShouldNotBeNil) 662 So(resp.StatusCode(), ShouldEqual, 200) 663 664 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-squashfs-test\"){Results{RepoName%20LastUpdated}}}") 665 So(resp, ShouldNotBeNil) 666 So(resp.StatusCode(), ShouldEqual, 200) 667 668 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-invalid-blob:commit-aaa7c6e7-squashfs\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 669 So(resp, ShouldNotBeNil) 670 So(resp.StatusCode(), ShouldEqual, 200) 671 672 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + cveid + "\",image:\"zot-squashfs-invalid-blob\"){Results{RepoName%20LastUpdated}}}") 673 So(resp, ShouldNotBeNil) 674 So(resp.StatusCode(), ShouldEqual, 200) 675 676 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-squashfs-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") 677 So(resp, ShouldNotBeNil) 678 So(resp.StatusCode(), ShouldEqual, 200) 679 680 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"cntos\"){Tag%20CVEList{Id%20Description%20Severity}}}") 681 So(resp, ShouldNotBeNil) 682 So(resp.StatusCode(), ShouldEqual, 200) 683 684 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(id:\"CVE-201-20482\"){Results{RepoName%20Tag}}}") 685 So(resp, ShouldNotBeNil) 686 So(resp.StatusCode(), ShouldEqual, 200) 687 688 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description}}}") 689 So(resp, ShouldNotBeNil) 690 So(resp.StatusCode(), ShouldEqual, 200) 691 692 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Tag}}") 693 So(resp, ShouldNotBeNil) 694 So(resp.StatusCode(), ShouldEqual, 200) 695 696 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description%20Severity}}}") 697 So(resp, ShouldNotBeNil) 698 So(resp.StatusCode(), ShouldEqual, 200) 699 700 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description%20Severity}}}") 701 So(resp, ShouldNotBeNil) 702 So(resp.StatusCode(), ShouldEqual, 200) 703 704 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Severity}}}") 705 So(resp, ShouldNotBeNil) 706 So(resp.StatusCode(), ShouldEqual, 200) 707 708 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id%20Description}}}") 709 So(resp, ShouldNotBeNil) 710 So(resp.StatusCode(), ShouldEqual, 200) 711 712 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Id}}}") 713 So(resp, ShouldNotBeNil) 714 So(resp.StatusCode(), ShouldEqual, 200) 715 716 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){CVEList{Description}}}") 717 So(resp, ShouldNotBeNil) 718 So(resp.StatusCode(), ShouldEqual, 200) 719 720 // Testing Invalid Search URL 721 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test:0.0.1\"){Ta%20CVEList{Id%20Description%20Severity}}}") 722 So(resp, ShouldNotBeNil) 723 So(resp.StatusCode(), ShouldEqual, 422) 724 725 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(tet:\"CVE-2018-20482\"){Results{RepoName%20Tag}}}") 726 So(resp, ShouldNotBeNil) 727 So(resp.StatusCode(), ShouldEqual, 422) 728 729 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageistForCVE(id:\"CVE-2018-20482\"){Results{RepoName%20Tag}}}") 730 So(resp, ShouldNotBeNil) 731 So(resp.StatusCode(), ShouldEqual, 422) 732 733 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(id:\"CVE-2018-20482\"){ame%20Tags}}") 734 So(resp, ShouldNotBeNil) 735 So(resp.StatusCode(), ShouldEqual, 422) 736 737 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(reo:\"zot-test:1.0.0\"){Tag%20CVEList{Id%20Description%20Severity}}}") 738 So(resp, ShouldNotBeNil) 739 So(resp.StatusCode(), ShouldEqual, 422) 740 741 resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(id:\"" + cveid + "\"){Results{RepoName%20Tag}}}") 742 So(resp, ShouldNotBeNil) 743 So(resp.StatusCode(), ShouldEqual, 200) 744 }) 745 } 746 747 func TestCVEStruct(t *testing.T) { //nolint:gocyclo 748 Convey("Unit test the CVE struct", t, func() { 749 const repo1 = "repo1" 750 const repo2 = "repo2" 751 const repo3 = "repo3" 752 const repo4 = "repo4" 753 const repo5 = "repo5" 754 const repo6 = "repo6" 755 const repo7 = "repo7" 756 const repo8 = "repo8" 757 const repo100 = "repo100" 758 const repoMultiarch = "repoIndex" 759 760 params := boltdb.DBParameters{ 761 RootDir: t.TempDir(), 762 } 763 boltDriver, err := boltdb.GetBoltDriver(params) 764 So(err, ShouldBeNil) 765 766 metaDB, err := boltdb.New(boltDriver, log.NewLogger("debug", "")) 767 So(err, ShouldBeNil) 768 769 // Create metadb data for scannable image with vulnerabilities 770 image11 := CreateImageWith().DefaultLayers(). 771 ImageConfig(ispec.Image{Created: DateRef(2008, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 772 773 err = metaDB.SetRepoReference(context.Background(), repo1, "0.1.0", image11.AsImageMeta()) 774 So(err, ShouldBeNil) 775 776 image12 := CreateImageWith().DefaultLayers(). 777 ImageConfig(ispec.Image{Created: DateRef(2009, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 778 779 err = metaDB.SetRepoReference(context.Background(), repo1, "1.0.0", image12.AsImageMeta()) 780 So(err, ShouldBeNil) 781 782 image13 := CreateImageWith().DefaultLayers(). 783 ImageConfig(ispec.Image{Created: DateRef(2010, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 784 785 err = metaDB.SetRepoReference(context.Background(), repo1, "1.1.0", image13.AsImageMeta()) 786 So(err, ShouldBeNil) 787 788 image14 := CreateImageWith().DefaultLayers(). 789 ImageConfig(ispec.Image{Created: DateRef(2011, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 790 791 err = metaDB.SetRepoReference(context.Background(), repo1, "1.0.1", image14.AsImageMeta()) 792 So(err, ShouldBeNil) 793 794 // Create metadb data for scannable image with no vulnerabilities 795 image61 := CreateImageWith().DefaultLayers(). 796 ImageConfig(ispec.Image{Created: DateRef(2016, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 797 798 err = metaDB.SetRepoReference(context.Background(), repo6, "1.0.0", image61.AsImageMeta()) 799 So(err, ShouldBeNil) 800 801 // Create metadb data for image not supporting scanning 802 image21 := CreateImageWith().Layers([]Layer{{ 803 MediaType: ispec.MediaTypeImageLayerNonDistributableGzip, //nolint:staticcheck 804 Blob: []byte{10, 10, 10}, 805 Digest: godigest.FromBytes([]byte{10, 10, 10}), 806 }}).ImageConfig(ispec.Image{Created: DateRef(2009, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 807 808 err = metaDB.SetRepoReference(context.Background(), repo2, "1.0.0", image21.AsImageMeta()) 809 So(err, ShouldBeNil) 810 811 // Create metadb data for invalid images/negative tests 812 image := CreateRandomImage() 813 err = metaDB.SetRepoReference(context.Background(), repo3, "invalid-manifest", image.AsImageMeta()) 814 So(err, ShouldBeNil) 815 816 image41 := CreateImageWith().DefaultLayers(). 817 CustomConfigBlob([]byte("invalid config blob"), ispec.MediaTypeImageConfig).Build() 818 819 err = metaDB.SetRepoReference(context.Background(), repo4, "invalid-config", image41.AsImageMeta()) 820 So(err, ShouldBeNil) 821 822 digest51 := godigest.FromString("abc8") 823 randomImgData := CreateRandomImage().AsImageMeta() 824 randomImgData.Digest = digest51 825 randomImgData.Manifests[0].Digest = digest51 826 err = metaDB.SetRepoReference(context.Background(), repo5, "nonexitent-manifest", randomImgData) 827 So(err, ShouldBeNil) 828 829 // Create metadb data for scannable image which errors during scan 830 image71 := CreateImageWith().DefaultLayers(). 831 ImageConfig(ispec.Image{Created: DateRef(2000, 1, 1, 12, 0, 0, 0, time.UTC)}).Build() 832 833 err = metaDB.SetRepoReference(context.Background(), repo7, "1.0.0", image71.AsImageMeta()) 834 So(err, ShouldBeNil) 835 836 // Create image with vulnerabilities of all severities 837 image81 := CreateImageWith().DefaultLayers(). 838 ImageConfig(ispec.Image{Created: DateRef(2020, 12, 1, 12, 0, 0, 0, time.UTC)}).Build() 839 840 err = metaDB.SetRepoReference(context.Background(), repo8, "1.0.0", image81.AsImageMeta()) 841 So(err, ShouldBeNil) 842 843 // create multiarch image with vulnerabilities 844 multiarchImage := CreateRandomMultiarch() 845 846 err = metaDB.SetRepoReference(context.Background(), repoMultiarch, multiarchImage.Images[0].DigestStr(), 847 multiarchImage.Images[0].AsImageMeta()) 848 So(err, ShouldBeNil) 849 850 err = metaDB.SetRepoReference(context.Background(), repoMultiarch, multiarchImage.Images[1].DigestStr(), 851 multiarchImage.Images[1].AsImageMeta()) 852 So(err, ShouldBeNil) 853 854 err = metaDB.SetRepoReference(context.Background(), repoMultiarch, multiarchImage.Images[2].DigestStr(), 855 multiarchImage.Images[2].AsImageMeta()) 856 So(err, ShouldBeNil) 857 858 err = metaDB.SetRepoReference(context.Background(), repoMultiarch, "tagIndex", multiarchImage.AsImageMeta()) 859 So(err, ShouldBeNil) 860 861 err = metaDB.SetRepoMeta("repo-with-bad-tag-digest", mTypes.RepoMeta{ 862 Name: "repo-with-bad-tag-digest", 863 Tags: map[mTypes.Tag]mTypes.Descriptor{ 864 "tag": {MediaType: ispec.MediaTypeImageManifest, Digest: godigest.FromString("1").String()}, 865 }, 866 }) 867 So(err, ShouldBeNil) 868 // Keep a record of all the image references / digest pairings 869 // This is normally done in MetaDB, but we want to verify 870 // the whole flow, including MetaDB 871 imageMap := map[string]string{} 872 873 image11Digest := image11.ManifestDescriptor.Digest.String() 874 image11Media := image11.ManifestDescriptor.MediaType 875 image11Name := repo1 + ":0.1.0" 876 imageMap[image11Name] = image11Digest 877 image12Digest := image12.ManifestDescriptor.Digest.String() 878 image12Media := image12.ManifestDescriptor.MediaType 879 image12Name := repo1 + ":1.0.0" 880 imageMap[image12Name] = image12Digest 881 image13Digest := image13.ManifestDescriptor.Digest.String() 882 image13Media := image13.ManifestDescriptor.MediaType 883 image13Name := repo1 + ":1.1.0" 884 imageMap[image13Name] = image13Digest 885 image14Digest := image14.ManifestDescriptor.Digest.String() 886 image14Media := image14.ManifestDescriptor.MediaType 887 image14Name := repo1 + ":1.0.1" 888 imageMap[image14Name] = image14Digest 889 image21Digest := image21.ManifestDescriptor.Digest.String() 890 image21Media := image21.ManifestDescriptor.MediaType 891 image21Name := repo2 + ":1.0.0" 892 imageMap[image21Name] = image21Digest 893 image61Digest := image61.ManifestDescriptor.Digest.String() 894 image61Media := image61.ManifestDescriptor.MediaType 895 image61Name := repo6 + ":1.0.0" 896 imageMap[image61Name] = image61Digest 897 image71Digest := image71.ManifestDescriptor.Digest.String() 898 image71Media := image71.ManifestDescriptor.MediaType 899 image71Name := repo7 + ":1.0.0" 900 imageMap[image71Name] = image71Digest 901 image81Digest := image81.ManifestDescriptor.Digest.String() 902 image81Media := image81.ManifestDescriptor.MediaType 903 image81Name := repo8 + ":1.0.0" 904 imageMap[image81Name] = image81Digest 905 indexDigest := multiarchImage.IndexDescriptor.Digest.String() 906 indexMedia := multiarchImage.IndexDescriptor.MediaType 907 indexName := repoMultiarch + ":tagIndex" 908 imageMap[indexName] = indexDigest 909 indexM1Digest := multiarchImage.Images[0].ManifestDescriptor.Digest.String() 910 indexM1Name := "repoIndex@" + indexM1Digest 911 imageMap[indexM1Name] = indexM1Digest 912 indexM2Digest := multiarchImage.Images[1].ManifestDescriptor.Digest.String() 913 indexM2Name := "repoIndex@" + indexM2Digest 914 imageMap[indexM2Name] = indexM2Digest 915 indexM3Digest := multiarchImage.Images[2].ManifestDescriptor.Digest.String() 916 indexM3Name := "repoIndex@" + indexM3Digest 917 imageMap[indexM3Name] = indexM3Digest 918 919 log := log.NewLogger("debug", "") 920 921 // Initialize a test CVE cache 922 cache := cvecache.NewCveCache(100, log) 923 924 // MetaDB loaded with initial data, now mock the scanner 925 // Setup test CVE data in mock scanner 926 scanner := mocks.CveScannerMock{ 927 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 928 result := cache.Get(image) 929 // Will not match sending the repo:tag as a parameter, but we don't care 930 if result != nil { 931 return result, nil 932 } 933 934 repo, ref, isTag := zcommon.GetImageDirAndReference(image) 935 if isTag { 936 foundRef, ok := imageMap[image] 937 if !ok { 938 return nil, ErrBadTest 939 } 940 ref = foundRef 941 } 942 943 defer func() { 944 t.Logf("ScanImageFn cached for image %s digest %s: %v", image, ref, cache.Get(ref)) 945 }() 946 947 // Images in chronological order 948 if repo == repo1 && ref == image11Digest { 949 result := map[string]cvemodel.CVE{ 950 "CVE1": { 951 ID: "CVE1", 952 Severity: "MEDIUM", 953 Title: "Title CVE1", 954 Description: "Description CVE1", 955 }, 956 } 957 958 cache.Add(ref, result) 959 960 return result, nil 961 } 962 963 if repo == repo1 && zcommon.Contains([]string{image12Digest, image21Digest}, ref) { 964 result := map[string]cvemodel.CVE{ 965 "CVE1": { 966 ID: "CVE1", 967 Severity: "MEDIUM", 968 Title: "Title CVE1", 969 Description: "Description CVE1", 970 }, 971 "CVE2": { 972 ID: "CVE2", 973 Severity: "HIGH", 974 Title: "Title CVE2", 975 Description: "Description CVE2", 976 }, 977 "CVE3": { 978 ID: "CVE3", 979 Severity: "LOW", 980 Title: "Title CVE3", 981 Description: "Description CVE3", 982 }, 983 } 984 985 cache.Add(ref, result) 986 987 return result, nil 988 } 989 990 if repo == repo1 && ref == image13Digest { 991 result := map[string]cvemodel.CVE{ 992 "CVE3": { 993 ID: "CVE3", 994 Severity: "LOW", 995 Title: "Title CVE3", 996 Description: "Description CVE3", 997 }, 998 } 999 1000 cache.Add(ref, result) 1001 1002 return result, nil 1003 } 1004 1005 // As a minor release on 1.0.0 banch 1006 // does not include all fixes published in 1.1.0 1007 if repo == repo1 && ref == image14Digest { 1008 result := map[string]cvemodel.CVE{ 1009 "CVE1": { 1010 ID: "CVE1", 1011 Severity: "MEDIUM", 1012 Title: "Title CVE1", 1013 Description: "Description CVE1", 1014 }, 1015 "CVE3": { 1016 ID: "CVE3", 1017 Severity: "LOW", 1018 Title: "Title CVE3", 1019 Description: "Description CVE3", 1020 }, 1021 } 1022 1023 cache.Add(ref, result) 1024 1025 return result, nil 1026 } 1027 1028 // Unexpected error while scanning 1029 if repo == repo7 { 1030 return map[string]cvemodel.CVE{}, ErrFailedScan 1031 } 1032 1033 if (repo == repoMultiarch && ref == indexDigest) || 1034 (repo == repoMultiarch && ref == indexM1Digest) { 1035 result := map[string]cvemodel.CVE{ 1036 "CVE1": { 1037 ID: "CVE1", 1038 Severity: "MEDIUM", 1039 Title: "Title CVE1", 1040 Description: "Description CVE1", 1041 }, 1042 } 1043 1044 // Simulate scanning an index results in scanning its manifests 1045 if ref == indexDigest { 1046 cache.Add(indexM1Digest, result) 1047 cache.Add(indexM2Digest, map[string]cvemodel.CVE{}) 1048 cache.Add(indexM3Digest, map[string]cvemodel.CVE{}) 1049 } 1050 1051 cache.Add(ref, result) 1052 1053 return result, nil 1054 } 1055 1056 if repo == repo8 && ref == image81Digest { 1057 result := map[string]cvemodel.CVE{ 1058 "CVE0": { 1059 ID: "CVE0", 1060 Severity: "UNKNOWN", 1061 Title: "Title CVE0", 1062 Description: "Description CVE0", 1063 }, 1064 "CVE1": { 1065 ID: "CVE1", 1066 Severity: "MEDIUM", 1067 Title: "Title CVE1", 1068 Description: "Description CVE1", 1069 }, 1070 "CVE2": { 1071 ID: "CVE2", 1072 Severity: "HIGH", 1073 Title: "Title CVE2", 1074 Description: "Description CVE2", 1075 }, 1076 "CVE3": { 1077 ID: "CVE3", 1078 Severity: "LOW", 1079 Title: "Title CVE3", 1080 Description: "Description CVE3", 1081 }, 1082 "CVE4": { 1083 ID: "CVE4", 1084 Severity: "CRITICAL", 1085 Title: "Title CVE4", 1086 Description: "Description CVE4", 1087 }, 1088 "CVE5": { 1089 ID: "CVE5", 1090 Severity: "CRITICAL", 1091 Title: "Title CVE5", 1092 Description: "Description CVE5", 1093 }, 1094 "CVE6": { 1095 ID: "CVE6", 1096 Severity: "LOW", 1097 Title: "Title CVE6", 1098 Description: "Description CVE6", 1099 }, 1100 } 1101 1102 cache.Add(ref, result) 1103 1104 return result, nil 1105 } 1106 1107 // By default the image has no vulnerabilities 1108 result = map[string]cvemodel.CVE{} 1109 cache.Add(ref, result) 1110 1111 return result, nil 1112 }, 1113 IsImageFormatScannableFn: func(repo string, reference string) (bool, error) { 1114 if repo == repoMultiarch { 1115 return true, nil 1116 } 1117 1118 // Almost same logic compared to actual Trivy specific implementation 1119 imageDir, inputTag := repo, reference 1120 1121 repoMeta, err := metaDB.GetRepoMeta(context.Background(), imageDir) 1122 if err != nil { 1123 return false, err 1124 } 1125 1126 manifestDigestStr := reference 1127 1128 if zcommon.IsTag(reference) { 1129 var ok bool 1130 1131 descriptor, ok := repoMeta.Tags[inputTag] 1132 if !ok { 1133 return false, zerr.ErrTagMetaNotFound 1134 } 1135 1136 manifestDigestStr = descriptor.Digest 1137 } 1138 1139 manifestDigest, err := godigest.Parse(manifestDigestStr) 1140 if err != nil { 1141 return false, err 1142 } 1143 1144 manifestData, err := metaDB.GetImageMeta(manifestDigest) 1145 if err != nil { 1146 return false, err 1147 } 1148 1149 for _, imageLayer := range manifestData.Manifests[0].Manifest.Layers { 1150 switch imageLayer.MediaType { 1151 case ispec.MediaTypeImageLayerGzip, ispec.MediaTypeImageLayer, string(regTypes.DockerLayer): 1152 1153 return true, nil 1154 default: 1155 1156 return false, zerr.ErrScanNotSupported 1157 } 1158 } 1159 1160 return false, nil 1161 }, 1162 IsImageMediaScannableFn: func(repo, digest, mediaType string) (bool, error) { 1163 if repo == repo2 && digest == image21Digest { 1164 return false, zerr.ErrScanNotSupported 1165 } 1166 if repo == repo100 { 1167 return false, zerr.ErrRepoMetaNotFound 1168 } 1169 1170 return true, nil 1171 }, 1172 IsResultCachedFn: func(digest string) bool { 1173 t.Logf("IsResultCachedFn found in cache for digest %s: %v", digest, cache.Get(digest)) 1174 1175 return cache.Contains(digest) 1176 }, 1177 GetCachedResultFn: func(digest string) map[string]cvemodel.CVE { 1178 t.Logf("GetCachedResultFn found in cache for digest %s: %v", digest, cache.Get(digest)) 1179 1180 return cache.Get(digest) 1181 }, 1182 } 1183 1184 cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, MetaDB: metaDB} 1185 1186 t.Log("\nTest GetCVEListForImage\n") 1187 1188 pageInput := cvemodel.PageInput{ 1189 SortBy: cveinfo.SeverityDsc, 1190 } 1191 1192 ctx := context.Background() 1193 1194 // Image is found 1195 cveList, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", "", "", pageInput) 1196 So(err, ShouldBeNil) 1197 So(len(cveList), ShouldEqual, 1) 1198 So(cveList[0].ID, ShouldEqual, "CVE1") 1199 So(pageInfo.ItemCount, ShouldEqual, 1) 1200 So(pageInfo.TotalCount, ShouldEqual, 1) 1201 So(cveSummary.Count, ShouldEqual, 1) 1202 So(cveSummary.UnknownCount, ShouldEqual, 0) 1203 So(cveSummary.LowCount, ShouldEqual, 0) 1204 So(cveSummary.MediumCount, ShouldEqual, 1) 1205 So(cveSummary.HighCount, ShouldEqual, 0) 1206 So(cveSummary.CriticalCount, ShouldEqual, 0) 1207 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1208 1209 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.0", "", "", "", pageInput) 1210 So(err, ShouldBeNil) 1211 So(len(cveList), ShouldEqual, 3) 1212 So(cveList[0].ID, ShouldEqual, "CVE2") 1213 So(cveList[1].ID, ShouldEqual, "CVE1") 1214 So(cveList[2].ID, ShouldEqual, "CVE3") 1215 So(pageInfo.ItemCount, ShouldEqual, 3) 1216 So(pageInfo.TotalCount, ShouldEqual, 3) 1217 So(cveSummary.Count, ShouldEqual, 3) 1218 So(cveSummary.UnknownCount, ShouldEqual, 0) 1219 So(cveSummary.LowCount, ShouldEqual, 1) 1220 So(cveSummary.MediumCount, ShouldEqual, 1) 1221 So(cveSummary.HighCount, ShouldEqual, 1) 1222 So(cveSummary.CriticalCount, ShouldEqual, 0) 1223 So(cveSummary.MaxSeverity, ShouldEqual, "HIGH") 1224 1225 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.1", "", "", "", pageInput) 1226 So(err, ShouldBeNil) 1227 So(len(cveList), ShouldEqual, 2) 1228 So(cveList[0].ID, ShouldEqual, "CVE1") 1229 So(cveList[1].ID, ShouldEqual, "CVE3") 1230 So(pageInfo.ItemCount, ShouldEqual, 2) 1231 So(pageInfo.TotalCount, ShouldEqual, 2) 1232 So(cveSummary.Count, ShouldEqual, 2) 1233 So(cveSummary.UnknownCount, ShouldEqual, 0) 1234 So(cveSummary.LowCount, ShouldEqual, 1) 1235 So(cveSummary.MediumCount, ShouldEqual, 1) 1236 So(cveSummary.HighCount, ShouldEqual, 0) 1237 So(cveSummary.CriticalCount, ShouldEqual, 0) 1238 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1239 1240 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.1.0", "", "", "", pageInput) 1241 So(err, ShouldBeNil) 1242 So(len(cveList), ShouldEqual, 1) 1243 So(cveList[0].ID, ShouldEqual, "CVE3") 1244 So(pageInfo.ItemCount, ShouldEqual, 1) 1245 So(pageInfo.TotalCount, ShouldEqual, 1) 1246 So(cveSummary.Count, ShouldEqual, 1) 1247 So(cveSummary.UnknownCount, ShouldEqual, 0) 1248 So(cveSummary.LowCount, ShouldEqual, 1) 1249 So(cveSummary.MediumCount, ShouldEqual, 0) 1250 So(cveSummary.HighCount, ShouldEqual, 0) 1251 So(cveSummary.CriticalCount, ShouldEqual, 0) 1252 So(cveSummary.MaxSeverity, ShouldEqual, "LOW") 1253 1254 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo6, "1.0.0", "", "", "", pageInput) 1255 So(err, ShouldBeNil) 1256 So(len(cveList), ShouldEqual, 0) 1257 So(pageInfo.ItemCount, ShouldEqual, 0) 1258 So(pageInfo.TotalCount, ShouldEqual, 0) 1259 So(cveSummary.Count, ShouldEqual, 0) 1260 So(cveSummary.UnknownCount, ShouldEqual, 0) 1261 So(cveSummary.LowCount, ShouldEqual, 0) 1262 So(cveSummary.MediumCount, ShouldEqual, 0) 1263 So(cveSummary.HighCount, ShouldEqual, 0) 1264 So(cveSummary.CriticalCount, ShouldEqual, 0) 1265 So(cveSummary.MaxSeverity, ShouldEqual, "NONE") 1266 1267 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo8, "1.0.0", "", "", "", pageInput) 1268 So(err, ShouldBeNil) 1269 So(len(cveList), ShouldEqual, 7) 1270 So(pageInfo.ItemCount, ShouldEqual, 7) 1271 So(pageInfo.TotalCount, ShouldEqual, 7) 1272 So(cveSummary.Count, ShouldEqual, 7) 1273 So(cveSummary.UnknownCount, ShouldEqual, 1) 1274 So(cveSummary.LowCount, ShouldEqual, 2) 1275 So(cveSummary.MediumCount, ShouldEqual, 1) 1276 So(cveSummary.HighCount, ShouldEqual, 1) 1277 So(cveSummary.CriticalCount, ShouldEqual, 2) 1278 So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") 1279 1280 _, _, _, err = cveInfo.GetCVEDiffListForImages(ctx, "repo8:1.0.0", "repo1@"+image13Digest, "", "", pageInput) 1281 So(err, ShouldBeNil) 1282 _, _, _, err = cveInfo.GetCVEDiffListForImages(ctx, "repo8:1.0.0", "repo1:0.1.0", "", "", pageInput) 1283 So(err, ShouldBeNil) 1284 1285 // Image is multiarch 1286 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repoMultiarch, "tagIndex", "", "", "", pageInput) 1287 So(err, ShouldBeNil) 1288 So(len(cveList), ShouldEqual, 1) 1289 So(cveList[0].ID, ShouldEqual, "CVE1") 1290 So(pageInfo.ItemCount, ShouldEqual, 1) 1291 So(pageInfo.TotalCount, ShouldEqual, 1) 1292 So(cveSummary.Count, ShouldEqual, 1) 1293 So(cveSummary.UnknownCount, ShouldEqual, 0) 1294 So(cveSummary.LowCount, ShouldEqual, 0) 1295 So(cveSummary.MediumCount, ShouldEqual, 1) 1296 So(cveSummary.HighCount, ShouldEqual, 0) 1297 So(cveSummary.CriticalCount, ShouldEqual, 0) 1298 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1299 1300 // Image is not scannable 1301 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo2, "1.0.0", "", "", "", pageInput) 1302 So(err, ShouldEqual, zerr.ErrScanNotSupported) 1303 So(len(cveList), ShouldEqual, 0) 1304 So(pageInfo.ItemCount, ShouldEqual, 0) 1305 So(pageInfo.TotalCount, ShouldEqual, 0) 1306 So(cveSummary.Count, ShouldEqual, 0) 1307 So(cveSummary.UnknownCount, ShouldEqual, 0) 1308 So(cveSummary.LowCount, ShouldEqual, 0) 1309 So(cveSummary.MediumCount, ShouldEqual, 0) 1310 So(cveSummary.HighCount, ShouldEqual, 0) 1311 So(cveSummary.CriticalCount, ShouldEqual, 0) 1312 So(cveSummary.MaxSeverity, ShouldEqual, "") 1313 1314 // Tag is not found 1315 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo3, "1.0.0", "", "", "", pageInput) 1316 So(err, ShouldEqual, zerr.ErrTagMetaNotFound) 1317 So(len(cveList), ShouldEqual, 0) 1318 So(pageInfo.ItemCount, ShouldEqual, 0) 1319 So(pageInfo.TotalCount, ShouldEqual, 0) 1320 So(cveSummary.Count, ShouldEqual, 0) 1321 So(cveSummary.UnknownCount, ShouldEqual, 0) 1322 So(cveSummary.LowCount, ShouldEqual, 0) 1323 So(cveSummary.MediumCount, ShouldEqual, 0) 1324 So(cveSummary.HighCount, ShouldEqual, 0) 1325 So(cveSummary.CriticalCount, ShouldEqual, 0) 1326 So(cveSummary.MaxSeverity, ShouldEqual, "") 1327 1328 // Scan failed 1329 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo7, "1.0.0", "", "", "", pageInput) 1330 So(err, ShouldEqual, ErrFailedScan) 1331 So(len(cveList), ShouldEqual, 0) 1332 So(pageInfo.ItemCount, ShouldEqual, 0) 1333 So(pageInfo.TotalCount, ShouldEqual, 0) 1334 So(cveSummary.Count, ShouldEqual, 0) 1335 So(cveSummary.UnknownCount, ShouldEqual, 0) 1336 So(cveSummary.LowCount, ShouldEqual, 0) 1337 So(cveSummary.MediumCount, ShouldEqual, 0) 1338 So(cveSummary.HighCount, ShouldEqual, 0) 1339 So(cveSummary.CriticalCount, ShouldEqual, 0) 1340 So(cveSummary.MaxSeverity, ShouldEqual, "") 1341 1342 // Tag is not found 1343 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo-with-bad-tag-digest", "tag", "", "", "", pageInput) 1344 So(err, ShouldEqual, zerr.ErrImageMetaNotFound) 1345 So(len(cveList), ShouldEqual, 0) 1346 So(pageInfo.ItemCount, ShouldEqual, 0) 1347 So(pageInfo.TotalCount, ShouldEqual, 0) 1348 So(cveSummary.Count, ShouldEqual, 0) 1349 So(cveSummary.UnknownCount, ShouldEqual, 0) 1350 So(cveSummary.LowCount, ShouldEqual, 0) 1351 So(cveSummary.MediumCount, ShouldEqual, 0) 1352 So(cveSummary.HighCount, ShouldEqual, 0) 1353 So(cveSummary.CriticalCount, ShouldEqual, 0) 1354 So(cveSummary.MaxSeverity, ShouldEqual, "") 1355 1356 // Repo is not found 1357 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo100, "1.0.0", "", "", "", pageInput) 1358 So(err, ShouldEqual, zerr.ErrRepoMetaNotFound) 1359 So(len(cveList), ShouldEqual, 0) 1360 So(pageInfo.ItemCount, ShouldEqual, 0) 1361 So(pageInfo.TotalCount, ShouldEqual, 0) 1362 So(cveSummary.Count, ShouldEqual, 0) 1363 So(cveSummary.UnknownCount, ShouldEqual, 0) 1364 So(cveSummary.LowCount, ShouldEqual, 0) 1365 So(cveSummary.MediumCount, ShouldEqual, 0) 1366 So(cveSummary.HighCount, ShouldEqual, 0) 1367 So(cveSummary.CriticalCount, ShouldEqual, 0) 1368 So(cveSummary.MaxSeverity, ShouldEqual, "") 1369 1370 // By this point the cache should already be pupulated by previous function calls 1371 t.Log("\nTest GetCVESummaryForImage\n") 1372 1373 // Image is found 1374 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image11Digest, image11Media) 1375 So(err, ShouldBeNil) 1376 So(cveSummary.Count, ShouldEqual, 1) 1377 So(cveSummary.UnknownCount, ShouldEqual, 0) 1378 So(cveSummary.LowCount, ShouldEqual, 0) 1379 So(cveSummary.MediumCount, ShouldEqual, 1) 1380 So(cveSummary.HighCount, ShouldEqual, 0) 1381 So(cveSummary.CriticalCount, ShouldEqual, 0) 1382 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1383 1384 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image12Digest, image12Media) 1385 So(err, ShouldBeNil) 1386 So(cveSummary.Count, ShouldEqual, 3) 1387 So(cveSummary.UnknownCount, ShouldEqual, 0) 1388 So(cveSummary.LowCount, ShouldEqual, 1) 1389 So(cveSummary.MediumCount, ShouldEqual, 1) 1390 So(cveSummary.HighCount, ShouldEqual, 1) 1391 So(cveSummary.CriticalCount, ShouldEqual, 0) 1392 So(cveSummary.MaxSeverity, ShouldEqual, "HIGH") 1393 1394 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image14Digest, image14Media) 1395 So(err, ShouldBeNil) 1396 So(cveSummary.Count, ShouldEqual, 2) 1397 So(cveSummary.UnknownCount, ShouldEqual, 0) 1398 So(cveSummary.LowCount, ShouldEqual, 1) 1399 So(cveSummary.MediumCount, ShouldEqual, 1) 1400 So(cveSummary.HighCount, ShouldEqual, 0) 1401 So(cveSummary.CriticalCount, ShouldEqual, 0) 1402 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1403 1404 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image13Digest, image13Media) 1405 So(err, ShouldBeNil) 1406 So(cveSummary.Count, ShouldEqual, 1) 1407 So(cveSummary.UnknownCount, ShouldEqual, 0) 1408 So(cveSummary.LowCount, ShouldEqual, 1) 1409 So(cveSummary.MediumCount, ShouldEqual, 0) 1410 So(cveSummary.HighCount, ShouldEqual, 0) 1411 So(cveSummary.CriticalCount, ShouldEqual, 0) 1412 So(cveSummary.MaxSeverity, ShouldEqual, "LOW") 1413 1414 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo6, image61Digest, image61Media) 1415 So(err, ShouldBeNil) 1416 So(cveSummary.Count, ShouldEqual, 0) 1417 So(cveSummary.UnknownCount, ShouldEqual, 0) 1418 So(cveSummary.LowCount, ShouldEqual, 0) 1419 So(cveSummary.MediumCount, ShouldEqual, 0) 1420 So(cveSummary.HighCount, ShouldEqual, 0) 1421 So(cveSummary.CriticalCount, ShouldEqual, 0) 1422 So(cveSummary.MaxSeverity, ShouldEqual, "NONE") 1423 1424 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo8, image81Digest, image81Media) 1425 So(err, ShouldBeNil) 1426 So(cveSummary.Count, ShouldEqual, 7) 1427 So(cveSummary.UnknownCount, ShouldEqual, 1) 1428 So(cveSummary.LowCount, ShouldEqual, 2) 1429 So(cveSummary.MediumCount, ShouldEqual, 1) 1430 So(cveSummary.HighCount, ShouldEqual, 1) 1431 So(cveSummary.CriticalCount, ShouldEqual, 2) 1432 So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") 1433 1434 // Image is multiarch 1435 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repoMultiarch, indexDigest, indexMedia) 1436 So(err, ShouldBeNil) 1437 So(cveSummary.Count, ShouldEqual, 1) 1438 So(cveSummary.UnknownCount, ShouldEqual, 0) 1439 So(cveSummary.LowCount, ShouldEqual, 0) 1440 So(cveSummary.MediumCount, ShouldEqual, 1) 1441 So(cveSummary.HighCount, ShouldEqual, 0) 1442 So(cveSummary.CriticalCount, ShouldEqual, 0) 1443 So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") 1444 1445 // Image is not scannable 1446 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo2, image21Digest, image21Media) 1447 So(err, ShouldEqual, zerr.ErrScanNotSupported) 1448 So(cveSummary.Count, ShouldEqual, 0) 1449 So(cveSummary.UnknownCount, ShouldEqual, 0) 1450 So(cveSummary.LowCount, ShouldEqual, 0) 1451 So(cveSummary.MediumCount, ShouldEqual, 0) 1452 So(cveSummary.HighCount, ShouldEqual, 0) 1453 So(cveSummary.CriticalCount, ShouldEqual, 0) 1454 So(cveSummary.MaxSeverity, ShouldEqual, "") 1455 1456 // Scan failed 1457 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo5, image71Digest, image71Media) 1458 So(err, ShouldBeNil) 1459 So(cveSummary.Count, ShouldEqual, 0) 1460 So(cveSummary.UnknownCount, ShouldEqual, 0) 1461 So(cveSummary.LowCount, ShouldEqual, 0) 1462 So(cveSummary.MediumCount, ShouldEqual, 0) 1463 So(cveSummary.HighCount, ShouldEqual, 0) 1464 So(cveSummary.CriticalCount, ShouldEqual, 0) 1465 So(cveSummary.MaxSeverity, ShouldEqual, "") 1466 1467 // Repo is not found 1468 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo100, 1469 godigest.FromString("missing_digest").String(), ispec.MediaTypeImageManifest) 1470 So(err, ShouldEqual, zerr.ErrRepoMetaNotFound) 1471 So(cveSummary.Count, ShouldEqual, 0) 1472 So(cveSummary.UnknownCount, ShouldEqual, 0) 1473 So(cveSummary.LowCount, ShouldEqual, 0) 1474 So(cveSummary.MediumCount, ShouldEqual, 0) 1475 So(cveSummary.HighCount, ShouldEqual, 0) 1476 So(cveSummary.CriticalCount, ShouldEqual, 0) 1477 So(cveSummary.MaxSeverity, ShouldEqual, "") 1478 1479 t.Log("\nTest GetImageListWithCVEFixed\n") 1480 1481 // Image is found 1482 tagList, err := cveInfo.GetImageListWithCVEFixed(ctx, repo1, "CVE1") 1483 So(err, ShouldBeNil) 1484 So(len(tagList), ShouldEqual, 1) 1485 So(tagList[0].Tag, ShouldEqual, "1.1.0") 1486 1487 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo1, "CVE2") 1488 So(err, ShouldBeNil) 1489 So(len(tagList), ShouldEqual, 2) 1490 expectedTags := []string{"1.0.1", "1.1.0"} 1491 So(expectedTags, ShouldContain, tagList[0].Tag) 1492 So(expectedTags, ShouldContain, tagList[1].Tag) 1493 1494 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo1, "CVE3") 1495 So(err, ShouldBeNil) 1496 // CVE3 is not present in 0.1.0, but that is older than all other 1497 // images where it is present. The rest of the images explicitly have it. 1498 // This means we consider it not fixed in any image. 1499 So(len(tagList), ShouldEqual, 0) 1500 1501 // Image doesn't have any CVEs in the first place 1502 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo6, "CVE1") 1503 So(err, ShouldBeNil) 1504 So(len(tagList), ShouldEqual, 1) 1505 So(tagList[0].Tag, ShouldEqual, "1.0.0") 1506 1507 // Image is not scannable 1508 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo2, "CVE100") 1509 // CVE is not considered fixed as scan is not possible 1510 // but do not return an error 1511 So(err, ShouldBeNil) 1512 So(len(tagList), ShouldEqual, 0) 1513 1514 // Repo is not found, there could potentially be unaffected tags in the repo 1515 // but we can't access their data 1516 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo100, "CVE100") 1517 So(err, ShouldEqual, zerr.ErrRepoMetaNotFound) 1518 So(len(tagList), ShouldEqual, 0) 1519 1520 t.Log("\nTest GetImageListForCVE\n") 1521 1522 // Image is found 1523 tagList, err = cveInfo.GetImageListForCVE(ctx, repo1, "CVE1") 1524 So(err, ShouldBeNil) 1525 So(len(tagList), ShouldEqual, 3) 1526 expectedTags = []string{"0.1.0", "1.0.0", "1.0.1"} 1527 So(expectedTags, ShouldContain, tagList[0].Tag) 1528 So(expectedTags, ShouldContain, tagList[1].Tag) 1529 So(expectedTags, ShouldContain, tagList[2].Tag) 1530 1531 tagList, err = cveInfo.GetImageListForCVE(ctx, repo1, "CVE2") 1532 So(err, ShouldBeNil) 1533 So(len(tagList), ShouldEqual, 1) 1534 So(tagList[0].Tag, ShouldEqual, "1.0.0") 1535 1536 tagList, err = cveInfo.GetImageListForCVE(ctx, repo1, "CVE3") 1537 So(err, ShouldBeNil) 1538 So(len(tagList), ShouldEqual, 3) 1539 expectedTags = []string{"1.0.0", "1.0.1", "1.1.0"} 1540 So(expectedTags, ShouldContain, tagList[0].Tag) 1541 So(expectedTags, ShouldContain, tagList[1].Tag) 1542 So(expectedTags, ShouldContain, tagList[2].Tag) 1543 1544 // Image/repo doesn't have the CVE at all 1545 tagList, err = cveInfo.GetImageListForCVE(ctx, repo6, "CVE1") 1546 So(err, ShouldBeNil) 1547 So(len(tagList), ShouldEqual, 0) 1548 1549 // Image is not scannable 1550 tagList, err = cveInfo.GetImageListForCVE(ctx, repo2, "CVE100") 1551 // Image is not considered affected with CVE as scan is not possible 1552 // but do not return an error 1553 So(err, ShouldBeNil) 1554 So(len(tagList), ShouldEqual, 0) 1555 1556 // Tag is not found, but we should not error 1557 tagList, err = cveInfo.GetImageListForCVE(ctx, repo3, "CVE101") 1558 So(err, ShouldBeNil) 1559 So(len(tagList), ShouldEqual, 0) 1560 1561 // Repo is not found, assume it is affected by the CVE 1562 // But we don't have enough of its data to actually return it 1563 tagList, err = cveInfo.GetImageListForCVE(ctx, repo100, "CVE100") 1564 So(err, ShouldEqual, zerr.ErrRepoMetaNotFound) 1565 So(len(tagList), ShouldEqual, 0) 1566 1567 t.Log("\nTest errors while scanning\n") 1568 1569 faultyScanner := mocks.CveScannerMock{ 1570 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 1571 // Could be any type of error, let's reuse this one 1572 return nil, zerr.ErrScanNotSupported 1573 }, 1574 } 1575 1576 cveInfo = cveinfo.BaseCveInfo{Log: log, Scanner: faultyScanner, MetaDB: metaDB} 1577 1578 cveSummary, err = cveInfo.GetCVESummaryForImageMedia(ctx, repo1, image11Digest, image11Media) 1579 So(err, ShouldBeNil) 1580 So(cveSummary.Count, ShouldEqual, 0) 1581 So(cveSummary.UnknownCount, ShouldEqual, 0) 1582 So(cveSummary.LowCount, ShouldEqual, 0) 1583 So(cveSummary.MediumCount, ShouldEqual, 0) 1584 So(cveSummary.HighCount, ShouldEqual, 0) 1585 So(cveSummary.CriticalCount, ShouldEqual, 0) 1586 So(cveSummary.MaxSeverity, ShouldEqual, "") 1587 1588 cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", "", "", pageInput) 1589 So(err, ShouldNotBeNil) 1590 So(cveList, ShouldBeEmpty) 1591 So(pageInfo.ItemCount, ShouldEqual, 0) 1592 So(pageInfo.TotalCount, ShouldEqual, 0) 1593 So(cveSummary.Count, ShouldEqual, 0) 1594 So(cveSummary.UnknownCount, ShouldEqual, 0) 1595 So(cveSummary.LowCount, ShouldEqual, 0) 1596 So(cveSummary.MediumCount, ShouldEqual, 0) 1597 So(cveSummary.HighCount, ShouldEqual, 0) 1598 So(cveSummary.CriticalCount, ShouldEqual, 0) 1599 So(cveSummary.MaxSeverity, ShouldEqual, "") 1600 1601 tagList, err = cveInfo.GetImageListWithCVEFixed(ctx, repo1, "CVE1") 1602 // CVE is not considered fixed as scan is not possible 1603 // but do not return an error 1604 So(err, ShouldBeNil) 1605 So(len(tagList), ShouldEqual, 0) 1606 1607 tagList, err = cveInfo.GetImageListForCVE(ctx, repo1, "CVE1") 1608 // Image is not considered affected with CVE as scan is not possible 1609 // but do not return an error 1610 So(err, ShouldBeNil) 1611 So(len(tagList), ShouldEqual, 0) 1612 1613 cveInfo = cveinfo.BaseCveInfo{Log: log, Scanner: mocks.CveScannerMock{ 1614 IsImageFormatScannableFn: func(repo, reference string) (bool, error) { 1615 return false, nil 1616 }, 1617 }, MetaDB: metaDB} 1618 1619 _, err = cveInfo.GetImageListForCVE(ctx, repoMultiarch, "CVE1") 1620 So(err, ShouldBeNil) 1621 1622 cveInfo = cveinfo.BaseCveInfo{Log: log, Scanner: mocks.CveScannerMock{ 1623 IsImageFormatScannableFn: func(repo, reference string) (bool, error) { 1624 return true, nil 1625 }, 1626 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 1627 return nil, zerr.ErrTypeAssertionFailed 1628 }, 1629 }, MetaDB: metaDB} 1630 1631 _, err = cveInfo.GetImageListForCVE(ctx, repoMultiarch, "CVE1") 1632 So(err, ShouldBeNil) 1633 1634 cveInfo = cveinfo.BaseCveInfo{Log: log, Scanner: mocks.CveScannerMock{ 1635 IsImageFormatScannableFn: func(repo, reference string) (bool, error) { 1636 return true, nil 1637 }, 1638 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 1639 return nil, zerr.ErrTypeAssertionFailed 1640 }, 1641 }, MetaDB: metaDB} 1642 _, _, _, err = cveInfo.GetCVEDiffListForImages(ctx, "repo8:1.0.0", "repo1:0.1.0", "", "", pageInput) 1643 So(err, ShouldNotBeNil) 1644 1645 try := 0 1646 cveInfo = cveinfo.BaseCveInfo{Log: log, Scanner: mocks.CveScannerMock{ 1647 IsImageFormatScannableFn: func(repo, reference string) (bool, error) { 1648 return true, nil 1649 }, 1650 ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) { 1651 if try == 1 { 1652 return nil, zerr.ErrTypeAssertionFailed 1653 } 1654 1655 try++ 1656 1657 return make(map[string]cvemodel.CVE), nil 1658 }, 1659 }, MetaDB: metaDB} 1660 _, _, _, err = cveInfo.GetCVEDiffListForImages(ctx, "repo8:1.0.0", "repo6:0.1.0", "", "", pageInput) 1661 So(err, ShouldNotBeNil) 1662 }) 1663 } 1664 1665 func getTags() ([]cvemodel.TagInfo, []cvemodel.TagInfo) { 1666 tags := make([]cvemodel.TagInfo, 0) 1667 1668 firstTag := cvemodel.TagInfo{ 1669 Tag: "1.0.0", 1670 Descriptor: cvemodel.Descriptor{ 1671 Digest: "sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb", 1672 MediaType: ispec.MediaTypeImageManifest, 1673 }, 1674 Timestamp: time.Now(), 1675 } 1676 secondTag := cvemodel.TagInfo{ 1677 Tag: "1.0.1", 1678 Descriptor: cvemodel.Descriptor{ 1679 Digest: "sha256:eca04f027f414362596f2632746d8a179362170b9ac9af772011fedcc3877ebb", 1680 MediaType: ispec.MediaTypeImageManifest, 1681 }, 1682 Timestamp: time.Now(), 1683 } 1684 thirdTag := cvemodel.TagInfo{ 1685 Tag: "1.0.2", 1686 Descriptor: cvemodel.Descriptor{ 1687 Digest: "sha256:eca04f027f414362596f2632746d8a170362170b9ac9af772011fedcc3877ebb", 1688 MediaType: ispec.MediaTypeImageManifest, 1689 }, 1690 Timestamp: time.Now(), 1691 } 1692 fourthTag := cvemodel.TagInfo{ 1693 Tag: "1.0.3", 1694 Descriptor: cvemodel.Descriptor{ 1695 Digest: "sha256:eca04f027f414362596f2632746d8a171362170b9ac9af772011fedcc3877ebb", 1696 MediaType: ispec.MediaTypeImageManifest, 1697 }, 1698 Timestamp: time.Now(), 1699 } 1700 1701 tags = append(tags, firstTag, secondTag, thirdTag, fourthTag) 1702 1703 vulnerableTags := make([]cvemodel.TagInfo, 0) 1704 vulnerableTags = append(vulnerableTags, secondTag) 1705 1706 return tags, vulnerableTags 1707 } 1708 1709 func TestFixedTags(t *testing.T) { 1710 Convey("Test fixed tags", t, func() { 1711 allTags, vulnerableTags := getTags() 1712 1713 fixedTags := cveinfo.GetFixedTags(allTags, vulnerableTags) 1714 So(len(fixedTags), ShouldEqual, 2) 1715 1716 fixedTags = cveinfo.GetFixedTags(allTags, append(vulnerableTags, cvemodel.TagInfo{ 1717 Tag: "taginfo", 1718 Descriptor: cvemodel.Descriptor{ 1719 MediaType: ispec.MediaTypeImageManifest, 1720 Digest: "sha256:eca04f027f414362596f2632746d8a179362170b9ac9af772011fedcc3877ebb", 1721 }, 1722 Timestamp: time.Date(2000, time.July, 20, 10, 10, 10, 10, time.UTC), 1723 })) 1724 So(len(fixedTags), ShouldEqual, 3) 1725 }) 1726 } 1727 1728 func TestFixedTagsWithIndex(t *testing.T) { 1729 Convey("Test fixed tags", t, func() { 1730 tempDir := t.TempDir() 1731 port := test.GetFreePort() 1732 baseURL := test.GetBaseURL(port) 1733 conf := config.New() 1734 conf.HTTP.Port = port 1735 defaultVal := true 1736 conf.Storage.RootDirectory = tempDir 1737 conf.Extensions = &extconf.ExtensionConfig{ 1738 Search: &extconf.SearchConfig{ 1739 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 1740 CVE: &extconf.CVEConfig{ 1741 UpdateInterval: 24 * time.Hour, 1742 Trivy: &extconf.TrivyConfig{ 1743 DBRepository: "ghcr.io/project-zot/trivy-db", 1744 }, 1745 }, 1746 }, 1747 } 1748 1749 logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt") 1750 So(err, ShouldBeNil) 1751 1752 logPath := logFile.Name() 1753 defer os.Remove(logPath) 1754 1755 writers := io.MultiWriter(os.Stdout, logFile) 1756 1757 ctlr := api.NewController(conf) 1758 ctlr.Log.Logger = ctlr.Log.Output(writers) 1759 1760 cm := test.NewControllerManager(ctlr) 1761 cm.StartAndWait(port) 1762 defer cm.StopServer() 1763 // push index with 2 manifests: one with vulns and one without 1764 vulnManifestCreated := time.Date(2010, 1, 1, 1, 1, 1, 1, time.UTC) 1765 vulnImageConfig := GetDefaultConfig() 1766 vulnImageConfig.Created = &vulnManifestCreated 1767 vulnImageConfig.Platform = ispec.Platform{OS: "linux", Architecture: "amd64"} 1768 vulnSingleArchImage := CreateImageWith().VulnerableLayers().VulnerableConfig(vulnImageConfig).Build() 1769 1770 fixedManifestCreated := time.Date(2010, 1, 1, 1, 1, 1, 1, time.UTC) 1771 fixedImageConfig := GetDefaultConfig() 1772 fixedImageConfig.Created = &fixedManifestCreated 1773 fixedImageConfig.Platform = ispec.Platform{OS: "windows", Architecture: "amd64"} 1774 fixedSingleArchImage := CreateImageWith().DefaultLayers().ImageConfig(fixedImageConfig).Build() 1775 1776 multiArchImage := CreateMultiarchWith().Images([]Image{vulnSingleArchImage, fixedSingleArchImage}).Build() 1777 1778 err = UploadMultiarchImage(multiArchImage, baseURL, "repo", "multi-arch-tag") 1779 So(err, ShouldBeNil) 1780 1781 // oldest vulnerability 1782 simpleVulnCreated := time.Date(2005, 1, 1, 1, 1, 1, 1, time.UTC) 1783 singleVulnImageConfig := GetDefaultConfig() 1784 singleVulnImageConfig.Created = &simpleVulnCreated 1785 singleVulnImageConfig.Platform = ispec.Platform{OS: "windows", Architecture: "amd64"} 1786 simpleVulnImage := CreateImageWith().VulnerableLayers().VulnerableConfig(singleVulnImageConfig).Build() 1787 1788 err = UploadImage(simpleVulnImage, baseURL, "repo", "vuln-img") 1789 So(err, ShouldBeNil) 1790 1791 // Wait for trivy db to download 1792 found, err := test.ReadLogFileAndSearchString(logPath, "cve-db update completed, next update scheduled after interval", 180*time.Second) 1793 So(err, ShouldBeNil) 1794 So(found, ShouldBeTrue) 1795 1796 cveInfo := cveinfo.NewCVEInfo(ctlr.CveScanner, ctlr.MetaDB, ctlr.Log) 1797 1798 tagsInfo, err := cveInfo.GetImageListWithCVEFixed(context.Background(), "repo", Vulnerability1ID) 1799 So(err, ShouldBeNil) 1800 So(len(tagsInfo), ShouldEqual, 1) 1801 So(len(tagsInfo[0].Manifests), ShouldEqual, 1) 1802 So(tagsInfo[0].Manifests[0].Digest, ShouldResemble, fixedSingleArchImage.ManifestDescriptor.Digest) 1803 1804 const query = ` 1805 { 1806 ImageListWithCVEFixed(id:"%s",image:"%s"){ 1807 Results{ 1808 RepoName 1809 Manifests {Digest} 1810 } 1811 } 1812 }` 1813 1814 resp, _ := resty.R().Get(baseURL + constants.FullSearchPrefix + "?query=" + 1815 url.QueryEscape(fmt.Sprintf(query, Vulnerability1ID, "repo"))) 1816 So(resp, ShouldNotBeNil) 1817 So(resp.StatusCode(), ShouldEqual, 200) 1818 1819 responseStruct := &zcommon.ImageListWithCVEFixedResponse{} 1820 err = json.Unmarshal(resp.Body(), &responseStruct) 1821 So(err, ShouldBeNil) 1822 So(len(responseStruct.Results), ShouldEqual, 1) 1823 So(len(responseStruct.Results[0].Manifests), ShouldEqual, 1) 1824 fixedManifestResp := responseStruct.Results[0].Manifests[0] 1825 So(fixedManifestResp.Digest, ShouldResemble, fixedSingleArchImage.ManifestDescriptor.Digest.String()) 1826 }) 1827 } 1828 1829 func TestGetCVESummaryForImageMediaErrors(t *testing.T) { 1830 Convey("Errors", t, func() { 1831 storeController := storage.StoreController{} 1832 storeController.DefaultStore = mocks.MockedImageStore{} 1833 1834 metaDB := mocks.MetaDBMock{} 1835 log := log.NewLogger("debug", "") 1836 1837 Convey("IsImageMediaScannable returns false", func() { 1838 scanner := mocks.CveScannerMock{ 1839 IsImageMediaScannableFn: func(repo, digest, mediaType string) (bool, error) { 1840 return false, zerr.ErrScanNotSupported 1841 }, 1842 } 1843 1844 cveInfo := cveinfo.NewCVEInfo(scanner, metaDB, log) 1845 1846 _, err := cveInfo.GetCVESummaryForImageMedia(context.Background(), "repo", "digest", ispec.MediaTypeImageManifest) 1847 So(err, ShouldNotBeNil) 1848 }) 1849 }) 1850 }