diff --git a/common/common.go b/common/common.go index f5bcb55b6..a43ca40e1 100644 --- a/common/common.go +++ b/common/common.go @@ -29,6 +29,29 @@ type CVSS struct { Score float64 } +const ( + // APKPackageFile is the legacy APK installed database path. + APKPackageFile = "lib/apk/db/installed" + // APKPackageFileUsrmerge is the Wolfi/Chainguard path after usrmerge. + APKPackageFileUsrmerge = "usr/lib/apk/db/installed" +) + +func APKPackageFiles() []string { + return []string{ + APKPackageFile, + APKPackageFileUsrmerge, + } +} + +func IsAPKPackageFile(filename string) bool { + switch filename { + case APKPackageFile, APKPackageFileUsrmerge: + return true + default: + return false + } +} + // database format type KeyVersion struct { diff --git a/common/db.go b/common/db.go index 3b55f807b..b949ea42c 100644 --- a/common/db.go +++ b/common/db.go @@ -50,6 +50,8 @@ const ( DBSuse DBPhoton DBRocky + DBWolfi + DBChainguard DBMax ) @@ -67,16 +69,18 @@ type dbSpace struct { var DBS dbSpace = dbSpace{ Buffers: [DBMax]dbBuffer{ - DBUbuntu: {Name: "ubuntu"}, - DBDebian: {Name: "debian"}, - DBCentos: {Name: "centos"}, - DBAlpine: {Name: "alpine"}, - DBAmazon: {Name: "amazon"}, - DBOracle: {Name: "oracle"}, - DBMariner: {Name: "mariner"}, - DBPhoton: {Name: "photon"}, - DBSuse: {Name: "suse"}, - DBRocky: {Name: "rocky"}, + DBUbuntu: {Name: "ubuntu"}, + DBDebian: {Name: "debian"}, + DBCentos: {Name: "centos"}, + DBAlpine: {Name: "alpine"}, + DBAmazon: {Name: "amazon"}, + DBOracle: {Name: "oracle"}, + DBMariner: {Name: "mariner"}, + DBPhoton: {Name: "photon"}, + DBSuse: {Name: "suse"}, + DBRocky: {Name: "rocky"}, + DBWolfi: {Name: "wolfi"}, + DBChainguard: {Name: "chainguard"}, }, } @@ -765,6 +769,10 @@ var fileList = []string{ "oracle_full.tb", "suse_index.tb", "suse_full.tb", + "wolfi_index.tb", + "wolfi_full.tb", + "chainguard_index.tb", + "chainguard_full.tb", "apps.tb", RHELCpeMapFile, } diff --git a/common/db_test.go b/common/db_test.go index 23c290029..66ff54a7c 100644 --- a/common/db_test.go +++ b/common/db_test.go @@ -24,30 +24,7 @@ import ( ) var ( - files = []string{ - "ubuntu_index.tb", - "ubuntu_full.tb", - "debian_index.tb", - "debian_full.tb", - "centos_index.tb", - "centos_full.tb", - "alpine_index.tb", - "alpine_full.tb", - "amazon_index.tb", - "amazon_full.tb", - "mariner_full.tb", - "mariner_index.tb", - "photon_full.tb", - "photon_index.tb", - "oracle_index.tb", - "oracle_full.tb", - "suse_index.tb", - "suse_full.tb", - "apps.tb", - "rhel-cpe.map", - "rocky_index.tb", - "rocky_full.tb", - } + files = append([]string(nil), fileList[1:]...) defaultVer = "1.000" wrongKey = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31} fakeTime = time.Date(2025, 12, 12, 0, 0, 0, 0, time.UTC) diff --git a/cvetools/cvesearch.go b/cvetools/cvesearch.go index a41805d95..118186254 100644 --- a/cvetools/cvesearch.go +++ b/cvetools/cvesearch.go @@ -932,6 +932,12 @@ func os2DB(ns *detectors.Namespace) (string, int) { majorVersion := majorVersion(r[2]) nsName = "rocky:" + majorVersion db = common.DBRocky + case "wolfi": + nsName = "wolfi:rolling" + db = common.DBWolfi + case "chainguard": + nsName = "chainguard:rolling" + db = common.DBChainguard } } return nsName, db diff --git a/cvetools/cvetools_test.go b/cvetools/cvetools_test.go index 2f8a83450..d5973a566 100644 --- a/cvetools/cvetools_test.go +++ b/cvetools/cvetools_test.go @@ -17,18 +17,20 @@ func TestSelectDB(t *testing.T) { } tests := map[string]result{ - "alpine:3.4.6": {"alpine:3.4", common.DBAlpine}, - "rhel:8.3": {"centos:8", common.DBCentos}, - "mariner:1.0": {"mariner:1.0", common.DBMariner}, - "opensuse-leap:15.2": {"sles:l15.2", common.DBSuse}, - "ol:7.8.2": {"oracle:7", common.DBOracle}, - "ubuntu:7.1": {"ubuntu:7.1", common.DBUbuntu}, - "debian:3.1": {"debian:3.1", common.DBDebian}, - "server:5.4": {"centos:5", common.DBCentos}, - "centos:5.4": {"centos:5", common.DBCentos}, - "amzn:1.8": {"amzn:1", common.DBAmazon}, - "sles:2.7": {"sles:2.7", common.DBSuse}, - "opensuse-leap:2.7": {"sles:l2.7", common.DBSuse}, + "alpine:3.4.6": {"alpine:3.4", common.DBAlpine}, + "rhel:8.3": {"centos:8", common.DBCentos}, + "mariner:1.0": {"mariner:1.0", common.DBMariner}, + "opensuse-leap:15.2": {"sles:l15.2", common.DBSuse}, + "ol:7.8.2": {"oracle:7", common.DBOracle}, + "ubuntu:7.1": {"ubuntu:7.1", common.DBUbuntu}, + "debian:3.1": {"debian:3.1", common.DBDebian}, + "server:5.4": {"centos:5", common.DBCentos}, + "centos:5.4": {"centos:5", common.DBCentos}, + "amzn:1.8": {"amzn:1", common.DBAmazon}, + "sles:2.7": {"sles:2.7", common.DBSuse}, + "opensuse-leap:2.7": {"sles:l2.7", common.DBSuse}, + "wolfi:20230201": {"wolfi:rolling", common.DBWolfi}, + "chainguard:20230201": {"chainguard:rolling", common.DBChainguard}, } for os, r := range tests { diff --git a/cvetools/image.go b/cvetools/image.go index 0b1ce1a06..501ebac8a 100644 --- a/cvetools/image.go +++ b/cvetools/image.go @@ -24,6 +24,7 @@ import ( "github.com/neuvector/neuvector/share/scan/registry" "github.com/neuvector/neuvector/share/scan/secrets" "github.com/neuvector/neuvector/share/utils" + "github.com/neuvector/scanner/common" "github.com/quay/clair/v2/pkg/tarutil" ) @@ -445,25 +446,22 @@ func getImageLayerIterate( } pathMap, err := selectiveFilesFromPath(layerPath, maxFileSize, func(path, fullpath string) bool { - if scan.OSPkgFiles.Contains(path) || scan.IsAppsPkgFile(path, fullpath) { + switch { + case scan.OSPkgFiles.Contains(path), common.IsAPKPackageFile(path), scan.IsAppsPkgFile(path, fullpath): return true - } - if isBitNami(path) { + case isBitNami(path): return true - } - if strings.HasPrefix(path, scan.DpkgStatusDir) { + case strings.HasPrefix(path, scan.DpkgStatusDir): return true - } - if strings.HasPrefix(path, contentManifest) && strings.HasSuffix(path, ".json") { + case strings.HasPrefix(path, contentManifest) && strings.HasSuffix(path, ".json"): return true - } - if strings.HasPrefix(path, dockerfile) { + case strings.HasPrefix(path, dockerfile): return true - } - if scan.IsRlangPackage(path) { + case scan.IsRlangPackage(path): return true + default: + return false } - return false }) if err != nil { return nil, share.ScanErrorCode_ScanErrPackage @@ -485,7 +483,7 @@ func getImageLayerIterate( if err != nil { continue } - } else if filename == "lib/apk/db/installed" { + } else if common.IsAPKPackageFile(filename) { data, err = getApkPackages(fullpath) if err != nil { continue diff --git a/detectors/features.go b/detectors/features.go index bdc04225f..0e7fc20c9 100644 --- a/detectors/features.go +++ b/detectors/features.go @@ -83,11 +83,20 @@ func DetectFeatures(namespace string, data map[string]*FeatureFile, path string) // -- apk -const apkPackageFile = "lib/apk/db/installed" - func detectAPK(namespace string, files map[string]*FeatureFile, path string) ([]FeatureVersion, error) { - f, hasFile := files[apkPackageFile] - if !hasFile { + var f *FeatureFile + + // APK images expose an installed package database at a well-known path. + // APKPackageFiles() returns paths in priority order (lib/apk/db/installed before usr/lib/apk/db/installed), + // so we take the first match and stop. + for _, apkPath := range common.APKPackageFiles() { + if apkFile, exists := files[apkPath]; exists { + f = apkFile + break + } + } + + if f == nil { return []FeatureVersion{}, nil }