zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/search/cve/trivy/scanner_test.go (about) 1 //go:build search 2 3 package trivy_test 4 5 import ( 6 "context" 7 "errors" 8 "path/filepath" 9 "testing" 10 "time" 11 12 godigest "github.com/opencontainers/go-digest" 13 ispec "github.com/opencontainers/image-spec/specs-go/v1" 14 . "github.com/smartystreets/goconvey/convey" 15 16 "zotregistry.dev/zot/pkg/api" 17 "zotregistry.dev/zot/pkg/api/config" 18 extconf "zotregistry.dev/zot/pkg/extensions/config" 19 "zotregistry.dev/zot/pkg/extensions/monitoring" 20 "zotregistry.dev/zot/pkg/extensions/search/cve/trivy" 21 "zotregistry.dev/zot/pkg/log" 22 "zotregistry.dev/zot/pkg/meta" 23 "zotregistry.dev/zot/pkg/meta/boltdb" 24 "zotregistry.dev/zot/pkg/meta/types" 25 "zotregistry.dev/zot/pkg/storage" 26 "zotregistry.dev/zot/pkg/storage/local" 27 . "zotregistry.dev/zot/pkg/test/common" 28 . "zotregistry.dev/zot/pkg/test/image-utils" 29 "zotregistry.dev/zot/pkg/test/mocks" 30 ) 31 32 var ErrTestError = errors.New("test error") 33 34 func TestScanBigTestFile(t *testing.T) { 35 Convey("Scan zot-test", t, func() { 36 projRootDir, err := GetProjectRootDir() 37 So(err, ShouldBeNil) 38 39 testImage := filepath.Join(projRootDir, "test/data/zot-test") 40 41 tempDir := t.TempDir() 42 port := GetFreePort() 43 conf := config.New() 44 conf.HTTP.Port = port 45 defaultVal := true 46 conf.Storage.RootDirectory = tempDir 47 conf.Extensions = &extconf.ExtensionConfig{ 48 Search: &extconf.SearchConfig{ 49 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 50 }, 51 } 52 ctlr := api.NewController(conf) 53 So(ctlr, ShouldNotBeNil) 54 55 err = CopyFiles(testImage, filepath.Join(tempDir, "zot-test")) 56 So(err, ShouldBeNil) 57 58 cm := NewControllerManager(ctlr) 59 cm.StartAndWait(port) 60 defer cm.StopServer() 61 // scan 62 scanner := trivy.NewScanner(ctlr.StoreController, ctlr.MetaDB, "ghcr.io/project-zot/trivy-db", "", ctlr.Log) 63 64 err = scanner.UpdateDB(context.Background()) 65 So(err, ShouldBeNil) 66 67 cveMap, err := scanner.ScanImage(context.Background(), "zot-test:0.0.1") 68 So(err, ShouldBeNil) 69 So(cveMap, ShouldNotBeNil) 70 }) 71 } 72 73 func TestScanningByDigest(t *testing.T) { 74 Convey("Scan the individual manifests inside an index", t, func() { 75 // start server 76 tempDir := t.TempDir() 77 port := GetFreePort() 78 baseURL := GetBaseURL(port) 79 conf := config.New() 80 conf.HTTP.Port = port 81 defaultVal := true 82 conf.Storage.RootDirectory = tempDir 83 conf.Extensions = &extconf.ExtensionConfig{ 84 Search: &extconf.SearchConfig{ 85 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 86 }, 87 } 88 ctlr := api.NewController(conf) 89 So(ctlr, ShouldNotBeNil) 90 91 cm := NewControllerManager(ctlr) 92 cm.StartAndWait(port) 93 defer cm.StopServer() 94 // push index with 2 manifests: one with vulns and one without 95 vulnImage := CreateDefaultVulnerableImage() 96 97 simpleImage := CreateRandomImage() 98 99 multiArch := CreateMultiarchWith().Images([]Image{simpleImage, //nolint:staticcheck 100 vulnImage}).Build() 101 102 err := UploadMultiarchImage(multiArch, baseURL, "multi-arch", "multi-arch-tag") 103 So(err, ShouldBeNil) 104 105 // scan 106 scanner := trivy.NewScanner(ctlr.StoreController, ctlr.MetaDB, "ghcr.io/project-zot/trivy-db", "", ctlr.Log) 107 108 ctx := context.Background() 109 110 err = scanner.UpdateDB(ctx) 111 So(err, ShouldBeNil) 112 113 cveMap, err := scanner.ScanImage(ctx, "multi-arch@"+vulnImage.DigestStr()) 114 So(err, ShouldBeNil) 115 So(cveMap, ShouldContainKey, Vulnerability1ID) 116 So(cveMap, ShouldContainKey, Vulnerability2ID) 117 So(cveMap, ShouldContainKey, Vulnerability3ID) 118 119 cveMap, err = scanner.ScanImage(ctx, "multi-arch@"+simpleImage.DigestStr()) 120 So(err, ShouldBeNil) 121 So(cveMap, ShouldBeEmpty) 122 123 cveMap, err = scanner.ScanImage(ctx, "multi-arch@"+multiArch.DigestStr()) 124 So(err, ShouldBeNil) 125 So(cveMap, ShouldContainKey, Vulnerability1ID) 126 So(cveMap, ShouldContainKey, Vulnerability2ID) 127 So(cveMap, ShouldContainKey, Vulnerability3ID) 128 129 cveMap, err = scanner.ScanImage(ctx, "multi-arch:multi-arch-tag") 130 So(err, ShouldBeNil) 131 So(cveMap, ShouldContainKey, Vulnerability1ID) 132 So(cveMap, ShouldContainKey, Vulnerability2ID) 133 So(cveMap, ShouldContainKey, Vulnerability3ID) 134 }) 135 } 136 137 func TestVulnerableLayer(t *testing.T) { 138 Convey("Vulnerable layer", t, func() { 139 vulnerableLayer, err := GetLayerWithVulnerability() 140 So(err, ShouldBeNil) 141 142 created, err := time.Parse(time.RFC3339, "2023-03-29T18:19:24Z") 143 So(err, ShouldBeNil) 144 145 config := ispec.Image{ 146 Created: &created, 147 Platform: ispec.Platform{ 148 Architecture: "amd64", 149 OS: "linux", 150 }, 151 Config: ispec.ImageConfig{ 152 Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, 153 Cmd: []string{"/bin/sh"}, 154 }, 155 RootFS: ispec.RootFS{ 156 Type: "layers", 157 DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"}, 158 }, 159 } 160 161 img := CreateImageWith(). 162 LayerBlobs([][]byte{vulnerableLayer}). 163 ImageConfig(config). 164 Build() 165 166 tempDir := t.TempDir() 167 168 log := log.NewLogger("debug", "") 169 imageStore := local.NewImageStore(tempDir, false, false, 170 log, monitoring.NewMetricsServer(false, log), nil, nil) 171 172 storeController := storage.StoreController{ 173 DefaultStore: imageStore, 174 } 175 176 err = WriteImageToFileSystem(img, "repo", img.DigestStr(), storeController) 177 So(err, ShouldBeNil) 178 179 params := boltdb.DBParameters{ 180 RootDir: tempDir, 181 } 182 boltDriver, err := boltdb.GetBoltDriver(params) 183 So(err, ShouldBeNil) 184 185 metaDB, err := boltdb.New(boltDriver, log) 186 So(err, ShouldBeNil) 187 188 err = meta.ParseStorage(metaDB, storeController, log) 189 So(err, ShouldBeNil) 190 191 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 192 193 err = scanner.UpdateDB(context.Background()) 194 So(err, ShouldBeNil) 195 196 cveMap, err := scanner.ScanImage(context.Background(), "repo@"+img.DigestStr()) 197 So(err, ShouldBeNil) 198 t.Logf("cveMap: %v", cveMap) 199 // As of September 17 2023 there are 5 CVEs: 200 // CVE-2023-1255, CVE-2023-2650, CVE-2023-2975, CVE-2023-3817, CVE-2023-3446 201 // There may be more discovered in the future 202 So(len(cveMap), ShouldBeGreaterThanOrEqualTo, 5) 203 So(cveMap, ShouldContainKey, "CVE-2023-1255") 204 So(cveMap, ShouldContainKey, "CVE-2023-2650") 205 So(cveMap, ShouldContainKey, "CVE-2023-2975") 206 So(cveMap, ShouldContainKey, "CVE-2023-3817") 207 So(cveMap, ShouldContainKey, "CVE-2023-3446") 208 }) 209 210 Convey("Vulnerable layer with vulnerability in language-specific file", t, func() { 211 vulnerableLayer, err := GetLayerWithLanguageFileVulnerability() 212 So(err, ShouldBeNil) 213 214 created, err := time.Parse(time.RFC3339, "2024-02-15T09:56:01.500079786Z") 215 So(err, ShouldBeNil) 216 217 config := ispec.Image{ 218 Created: &created, 219 Platform: ispec.Platform{ 220 Architecture: "amd64", 221 OS: "linux", 222 }, 223 Config: ispec.ImageConfig{ 224 Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, 225 }, 226 RootFS: ispec.RootFS{ 227 Type: "layers", 228 DiffIDs: []godigest.Digest{"sha256:d789b0723f3e6e5064d612eb3c84071cc84a7cf7921d549642252c3295e5f937"}, 229 }, 230 } 231 232 img := CreateImageWith(). 233 LayerBlobs([][]byte{vulnerableLayer}). 234 ImageConfig(config). 235 Build() 236 237 tempDir := t.TempDir() 238 239 log := log.NewLogger("debug", "") 240 imageStore := local.NewImageStore(tempDir, false, false, 241 log, monitoring.NewMetricsServer(false, log), nil, nil) 242 243 storeController := storage.StoreController{ 244 DefaultStore: imageStore, 245 } 246 247 err = WriteImageToFileSystem(img, "repo", img.DigestStr(), storeController) 248 So(err, ShouldBeNil) 249 250 params := boltdb.DBParameters{ 251 RootDir: tempDir, 252 } 253 boltDriver, err := boltdb.GetBoltDriver(params) 254 So(err, ShouldBeNil) 255 256 metaDB, err := boltdb.New(boltDriver, log) 257 So(err, ShouldBeNil) 258 259 err = meta.ParseStorage(metaDB, storeController, log) 260 So(err, ShouldBeNil) 261 262 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", 263 "ghcr.io/aquasecurity/trivy-java-db", log) 264 265 err = scanner.UpdateDB(context.Background()) 266 So(err, ShouldBeNil) 267 268 cveMap, err := scanner.ScanImage(context.Background(), "repo@"+img.DigestStr()) 269 So(err, ShouldBeNil) 270 t.Logf("cveMap: %v", cveMap) 271 272 // As of Feb 15 2024, there is 1 CVE in this layer: 273 So(len(cveMap), ShouldBeGreaterThanOrEqualTo, 1) 274 So(cveMap, ShouldContainKey, "CVE-2016-1000027") 275 276 cveData := cveMap["CVE-2016-1000027"] 277 vulnerablePackages := cveData.PackageList 278 279 // There is only 1 vulnerable package in this layer 280 So(len(vulnerablePackages), ShouldEqual, 1) 281 vulnerableSpringWebPackage := vulnerablePackages[0] 282 So(vulnerableSpringWebPackage.Name, ShouldEqual, "org.springframework:spring-web") 283 So(vulnerableSpringWebPackage.InstalledVersion, ShouldEqual, "5.3.31") 284 So(vulnerableSpringWebPackage.FixedVersion, ShouldEqual, "6.0.0") 285 So(vulnerableSpringWebPackage.PackagePath, ShouldEqual, "usr/local/artifacts/spring-web-5.3.31.jar") 286 }) 287 } 288 289 func TestScannerErrors(t *testing.T) { 290 Convey("Errors", t, func() { 291 storeController := storage.StoreController{} 292 metaDB := mocks.MetaDBMock{} 293 log := log.NewLogger("debug", "") 294 295 Convey("IsImageFormatScannable", func() { 296 storeController.DefaultStore = mocks.MockedImageStore{} 297 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 298 return types.ImageMeta{}, ErrTestError 299 } 300 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 301 302 _, err := scanner.IsImageFormatScannable("repo", godigest.FromString("dig").String()) 303 So(err, ShouldNotBeNil) 304 }) 305 Convey("IsImageMediaScannable", func() { 306 storeController.DefaultStore = mocks.MockedImageStore{} 307 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 308 return types.ImageMeta{}, ErrTestError 309 } 310 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 311 312 Convey("Manifest", func() { 313 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageManifest) 314 So(err, ShouldNotBeNil) 315 }) 316 Convey("Index", func() { 317 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 318 So(err, ShouldNotBeNil) 319 }) 320 Convey("Index with nil index", func() { 321 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 322 return types.ImageMeta{}, nil 323 } 324 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 325 326 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 327 So(err, ShouldNotBeNil) 328 }) 329 Convey("Index with good index", func() { 330 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 331 return types.ImageMeta{ 332 Index: &ispec.Index{ 333 Manifests: []ispec.Descriptor{{MediaType: ispec.MediaTypeImageLayer}}, 334 }, 335 Manifests: []types.ManifestMeta{{Manifest: ispec.Manifest{ 336 Layers: []ispec.Descriptor{{MediaType: ispec.MediaTypeImageLayer}}, 337 }}}, 338 }, nil 339 } 340 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 341 342 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 343 So(err, ShouldBeNil) 344 }) 345 }) 346 Convey("ScanImage", func() { 347 storeController.DefaultStore = mocks.MockedImageStore{} 348 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 349 return types.ImageMeta{}, ErrTestError 350 } 351 352 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 353 354 _, err := scanner.ScanImage(context.Background(), "image@"+godigest.FromString("digest").String()) 355 So(err, ShouldNotBeNil) 356 }) 357 }) 358 }