zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/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.io/zot/pkg/api" 17 "zotregistry.io/zot/pkg/api/config" 18 extconf "zotregistry.io/zot/pkg/extensions/config" 19 "zotregistry.io/zot/pkg/extensions/monitoring" 20 "zotregistry.io/zot/pkg/extensions/search/cve/trivy" 21 "zotregistry.io/zot/pkg/log" 22 "zotregistry.io/zot/pkg/meta" 23 "zotregistry.io/zot/pkg/meta/boltdb" 24 "zotregistry.io/zot/pkg/meta/types" 25 "zotregistry.io/zot/pkg/storage" 26 "zotregistry.io/zot/pkg/storage/local" 27 . "zotregistry.io/zot/pkg/test/common" 28 "zotregistry.io/zot/pkg/test/deprecated" 29 . "zotregistry.io/zot/pkg/test/image-utils" 30 "zotregistry.io/zot/pkg/test/mocks" 31 ) 32 33 var ErrTestError = errors.New("test error") 34 35 func TestScanBigTestFile(t *testing.T) { 36 Convey("Scan zot-test", t, func() { 37 projRootDir, err := GetProjectRootDir() 38 So(err, ShouldBeNil) 39 40 testImage := filepath.Join(projRootDir, "test/data/zot-test") 41 42 tempDir := t.TempDir() 43 port := GetFreePort() 44 conf := config.New() 45 conf.HTTP.Port = port 46 defaultVal := true 47 conf.Storage.RootDirectory = tempDir 48 conf.Extensions = &extconf.ExtensionConfig{ 49 Search: &extconf.SearchConfig{ 50 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 51 }, 52 } 53 ctlr := api.NewController(conf) 54 So(ctlr, ShouldNotBeNil) 55 56 err = CopyFiles(testImage, filepath.Join(tempDir, "zot-test")) 57 So(err, ShouldBeNil) 58 59 cm := NewControllerManager(ctlr) 60 cm.StartAndWait(port) 61 defer cm.StopServer() 62 // scan 63 scanner := trivy.NewScanner(ctlr.StoreController, ctlr.MetaDB, "ghcr.io/project-zot/trivy-db", "", ctlr.Log) 64 65 err = scanner.UpdateDB(context.Background()) 66 So(err, ShouldBeNil) 67 68 cveMap, err := scanner.ScanImage(context.Background(), "zot-test:0.0.1") 69 So(err, ShouldBeNil) 70 So(cveMap, ShouldNotBeNil) 71 }) 72 } 73 74 func TestScanningByDigest(t *testing.T) { 75 Convey("Scan the individual manifests inside an index", t, func() { 76 // start server 77 tempDir := t.TempDir() 78 port := GetFreePort() 79 baseURL := GetBaseURL(port) 80 conf := config.New() 81 conf.HTTP.Port = port 82 defaultVal := true 83 conf.Storage.RootDirectory = tempDir 84 conf.Extensions = &extconf.ExtensionConfig{ 85 Search: &extconf.SearchConfig{ 86 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 87 }, 88 } 89 ctlr := api.NewController(conf) 90 So(ctlr, ShouldNotBeNil) 91 92 cm := NewControllerManager(ctlr) 93 cm.StartAndWait(port) 94 defer cm.StopServer() 95 // push index with 2 manifests: one with vulns and one without 96 vulnImage := CreateDefaultVulnerableImage() 97 98 simpleImage := CreateRandomImage() 99 100 multiArch := deprecated.GetMultiarchImageForImages([]Image{simpleImage, //nolint:staticcheck 101 vulnImage}) 102 103 err := UploadMultiarchImage(multiArch, baseURL, "multi-arch", "multi-arch-tag") 104 So(err, ShouldBeNil) 105 106 // scan 107 scanner := trivy.NewScanner(ctlr.StoreController, ctlr.MetaDB, "ghcr.io/project-zot/trivy-db", "", ctlr.Log) 108 109 ctx := context.Background() 110 111 err = scanner.UpdateDB(ctx) 112 So(err, ShouldBeNil) 113 114 cveMap, err := scanner.ScanImage(ctx, "multi-arch@"+vulnImage.DigestStr()) 115 So(err, ShouldBeNil) 116 So(cveMap, ShouldContainKey, Vulnerability1ID) 117 So(cveMap, ShouldContainKey, Vulnerability2ID) 118 So(cveMap, ShouldContainKey, Vulnerability3ID) 119 120 cveMap, err = scanner.ScanImage(ctx, "multi-arch@"+simpleImage.DigestStr()) 121 So(err, ShouldBeNil) 122 So(cveMap, ShouldBeEmpty) 123 124 cveMap, err = scanner.ScanImage(ctx, "multi-arch@"+multiArch.DigestStr()) 125 So(err, ShouldBeNil) 126 So(cveMap, ShouldContainKey, Vulnerability1ID) 127 So(cveMap, ShouldContainKey, Vulnerability2ID) 128 So(cveMap, ShouldContainKey, Vulnerability3ID) 129 130 cveMap, err = scanner.ScanImage(ctx, "multi-arch:multi-arch-tag") 131 So(err, ShouldBeNil) 132 So(cveMap, ShouldContainKey, Vulnerability1ID) 133 So(cveMap, ShouldContainKey, Vulnerability2ID) 134 So(cveMap, ShouldContainKey, Vulnerability3ID) 135 }) 136 } 137 138 func TestVulnerableLayer(t *testing.T) { 139 Convey("Vulnerable layer", t, func() { 140 vulnerableLayer, err := GetLayerWithVulnerability() 141 So(err, ShouldBeNil) 142 143 created, err := time.Parse(time.RFC3339, "2023-03-29T18:19:24Z") 144 So(err, ShouldBeNil) 145 146 config := ispec.Image{ 147 Created: &created, 148 Platform: ispec.Platform{ 149 Architecture: "amd64", 150 OS: "linux", 151 }, 152 Config: ispec.ImageConfig{ 153 Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, 154 Cmd: []string{"/bin/sh"}, 155 }, 156 RootFS: ispec.RootFS{ 157 Type: "layers", 158 DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"}, 159 }, 160 } 161 162 img := CreateImageWith(). 163 LayerBlobs([][]byte{vulnerableLayer}). 164 ImageConfig(config). 165 Build() 166 167 tempDir := t.TempDir() 168 169 log := log.NewLogger("debug", "") 170 imageStore := local.NewImageStore(tempDir, false, false, 171 log, monitoring.NewMetricsServer(false, log), nil, nil) 172 173 storeController := storage.StoreController{ 174 DefaultStore: imageStore, 175 } 176 177 err = WriteImageToFileSystem(img, "repo", img.DigestStr(), storeController) 178 So(err, ShouldBeNil) 179 180 params := boltdb.DBParameters{ 181 RootDir: tempDir, 182 } 183 boltDriver, err := boltdb.GetBoltDriver(params) 184 So(err, ShouldBeNil) 185 186 metaDB, err := boltdb.New(boltDriver, log) 187 So(err, ShouldBeNil) 188 189 err = meta.ParseStorage(metaDB, storeController, log) 190 So(err, ShouldBeNil) 191 192 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 193 194 err = scanner.UpdateDB(context.Background()) 195 So(err, ShouldBeNil) 196 197 cveMap, err := scanner.ScanImage(context.Background(), "repo@"+img.DigestStr()) 198 So(err, ShouldBeNil) 199 t.Logf("cveMap: %v", cveMap) 200 // As of September 17 2023 there are 5 CVEs: 201 // CVE-2023-1255, CVE-2023-2650, CVE-2023-2975, CVE-2023-3817, CVE-2023-3446 202 // There may be more discovered in the future 203 So(len(cveMap), ShouldBeGreaterThanOrEqualTo, 5) 204 So(cveMap, ShouldContainKey, "CVE-2023-1255") 205 So(cveMap, ShouldContainKey, "CVE-2023-2650") 206 So(cveMap, ShouldContainKey, "CVE-2023-2975") 207 So(cveMap, ShouldContainKey, "CVE-2023-3817") 208 So(cveMap, ShouldContainKey, "CVE-2023-3446") 209 }) 210 } 211 212 func TestScannerErrors(t *testing.T) { 213 Convey("Errors", t, func() { 214 storeController := storage.StoreController{} 215 metaDB := mocks.MetaDBMock{} 216 log := log.NewLogger("debug", "") 217 218 Convey("IsImageFormatScannable", func() { 219 storeController.DefaultStore = mocks.MockedImageStore{} 220 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 221 return types.ImageMeta{}, ErrTestError 222 } 223 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 224 225 _, err := scanner.IsImageFormatScannable("repo", godigest.FromString("dig").String()) 226 So(err, ShouldNotBeNil) 227 }) 228 Convey("IsImageMediaScannable", func() { 229 storeController.DefaultStore = mocks.MockedImageStore{} 230 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 231 return types.ImageMeta{}, ErrTestError 232 } 233 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 234 235 Convey("Manifest", func() { 236 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageManifest) 237 So(err, ShouldNotBeNil) 238 }) 239 Convey("Index", func() { 240 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 241 So(err, ShouldNotBeNil) 242 }) 243 Convey("Index with nil index", func() { 244 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 245 return types.ImageMeta{}, nil 246 } 247 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 248 249 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 250 So(err, ShouldNotBeNil) 251 }) 252 Convey("Index with good index", func() { 253 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 254 return types.ImageMeta{ 255 Index: &ispec.Index{ 256 Manifests: []ispec.Descriptor{{MediaType: ispec.MediaTypeImageLayer}}, 257 }, 258 Manifests: []types.ManifestMeta{{Manifest: ispec.Manifest{ 259 Layers: []ispec.Descriptor{{MediaType: ispec.MediaTypeImageLayer}}, 260 }}}, 261 }, nil 262 } 263 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 264 265 _, err := scanner.IsImageMediaScannable("repo", godigest.FromString("dig").String(), ispec.MediaTypeImageIndex) 266 So(err, ShouldBeNil) 267 }) 268 }) 269 Convey("ScanImage", func() { 270 storeController.DefaultStore = mocks.MockedImageStore{} 271 metaDB.GetImageMetaFn = func(digest godigest.Digest) (types.ImageMeta, error) { 272 return types.ImageMeta{}, ErrTestError 273 } 274 275 scanner := trivy.NewScanner(storeController, metaDB, "ghcr.io/project-zot/trivy-db", "", log) 276 277 _, err := scanner.ScanImage(context.Background(), "image@"+godigest.FromString("digest").String()) 278 So(err, ShouldNotBeNil) 279 }) 280 }) 281 }