@cdxgen/cdxgen 13.4.0 → 13.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +29 -5
- package/bin/evinse.js +8 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/bin/validate.js +6 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/license-aliases.json +24 -2
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +407 -1
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +120 -6
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +182 -6
- package/lib/cli/jsBom.js +153 -64
- package/lib/cli/jvmBom.js +363 -66
- package/lib/cli/managedBom.js +853 -87
- package/lib/cli/nativeBom.js +267 -33
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +38 -8
- package/lib/core/fs.js +40 -19
- package/lib/core/httpClient.js +172 -46
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +5 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/core/toolRegistry.js +170 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +858 -244
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +438 -64
- package/lib/ecosystems/parsers-python.js +100 -12
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/rubyutils.js +544 -34
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +4 -1
- package/lib/ecosystems/scalasem.js +798 -151
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +472 -61
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +413 -93
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +110 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +41 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +639 -63
- package/lib/inventory/dotnetRuntime.js +217 -0
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +475 -18
- package/lib/inventory/evidenceUtils.js +447 -23
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +32 -3
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +271 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +438 -0
- package/lib/inventory/phpHostProbe.js +185 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +256 -5
- package/lib/inventory/spdx.js +144 -0
- package/lib/inventory/traceRunner.js +5 -1
- package/lib/inventory/tracedCrypto.js +129 -0
- package/lib/managers/binary.js +866 -186
- package/lib/managers/docker.js +245 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +62 -13
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +198 -2
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +5 -5
- package/lib/stages/postgen/ruleEngine.js +71 -6
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +231 -62
- package/lib/validator/bomValidator.js +32 -6
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +27 -8
- package/lib/validator/generated/validate-1.7.mjs +32 -13
- package/lib/validator/generated/validate-2.0.mjs +31 -12
- package/lib/validator/index.js +1 -1
- package/package.json +19 -18
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +22 -3
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +1 -17
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +20 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/boundedFile.d.ts +70 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +22 -2
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +2 -0
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +57 -21
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +85 -13
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +14 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +144 -12
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +88 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +33 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +2 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +162 -7
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- package/types/lib/inventory/dynamic.d.ts +6 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +92 -8
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +29 -10
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/gtfobins.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +44 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +38 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +15 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +4 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +22 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +24 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +17 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +8 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +26 -9
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
|
|
4
|
-
import { build } from "@cdxgen/cdx-purl";
|
|
5
4
|
import { globSync } from "glob";
|
|
6
5
|
|
|
7
6
|
import { DEBUG_MODE, recordSymlinkResolution } from "../core/activity.js";
|
|
8
7
|
import { safeExistsSync, safeSpawnSync } from "../core/fs.js";
|
|
9
8
|
import { thoughtLog } from "../core/logger.js";
|
|
10
9
|
import { getDistroInfo } from "./osinfo.js";
|
|
10
|
+
import { buildOsPackagePurl } from "./osPackagePurl.js";
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Caches
|
|
@@ -48,58 +48,34 @@ function parseAlpinePackage(pkgStr) {
|
|
|
48
48
|
// ---------------------------------------------------------------------------
|
|
49
49
|
// Build a PackageURL string from resolved package info.
|
|
50
50
|
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
51
|
+
// deb, rpm, apk and Homebrew packages are named by buildOsPackagePurl, the
|
|
52
|
+
// function the OBOM rows use as well, so a file and the package that owns it
|
|
53
|
+
// share one identity with the package an OBOM or a container scan lists. The
|
|
54
|
+
// distro qualifiers come from /etc/os-release via getDistroInfo(), never from
|
|
55
|
+
// a hard-coded table.
|
|
53
56
|
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
57
|
+
// Homebrew has no official purl type yet, but cdx-purl builds pkg:brew, as the
|
|
58
|
+
// OBOM does for its homebrew_packages rows. A brew purl carries no qualifiers:
|
|
59
|
+
// this used to pass an `arch` that the generic type it fell back to does not
|
|
60
|
+
// allow, so build() threw and a file of a Homebrew package got no purl at all.
|
|
56
61
|
// ---------------------------------------------------------------------------
|
|
57
62
|
function buildPurl(pkgInfo) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (pkgInfo.arch) {
|
|
63
|
-
qualifiers.arch = pkgInfo.arch;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (purlType === "deb" || purlType === "apk" || purlType === "rpm") {
|
|
67
|
-
const di = getDistroInfo();
|
|
68
|
-
namespace = di.namespace;
|
|
69
|
-
|
|
70
|
-
// distro qualifier: ID-VERSION_ID (e.g. "fedora-25", "alpine-3.17")
|
|
71
|
-
if (di.distroId) {
|
|
72
|
-
qualifiers.distro = di.distroId;
|
|
73
|
-
}
|
|
74
|
-
// distro_name qualifier: codename (e.g. "jammy", "bookworm")
|
|
75
|
-
if (di.distroName) {
|
|
76
|
-
qualifiers.distro_name = di.distroName;
|
|
77
|
-
}
|
|
78
|
-
} else if (purlType === "brew") {
|
|
79
|
-
// No official brew purl type — fall back to generic. This used to add a
|
|
80
|
-
// `package_manager=homebrew` qualifier, which generic does not allow, so
|
|
81
|
-
// build() threw and the catch below swallowed it: Homebrew packages silently
|
|
82
|
-
// received no purl at all on macOS. The provenance is instead recorded as a
|
|
83
|
-
// property by the caller.
|
|
84
|
-
purlType = "generic";
|
|
85
|
-
}
|
|
63
|
+
const distro =
|
|
64
|
+
pkgInfo.type === "brew" || !pkgInfo.type ? undefined : getDistroInfo();
|
|
65
|
+
return buildOsPackagePurl(pkgInfo, distro) || "";
|
|
66
|
+
}
|
|
86
67
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
// apk names an architecture its own way, which is not the way node does.
|
|
69
|
+
const APK_ARCHITECTURES = {
|
|
70
|
+
x64: "x86_64",
|
|
71
|
+
arm64: "aarch64",
|
|
72
|
+
ia32: "x86",
|
|
73
|
+
ppc64: "ppc64le",
|
|
74
|
+
loong64: "loongarch64",
|
|
75
|
+
};
|
|
90
76
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
type: purlType,
|
|
94
|
-
namespace: namespace || undefined || null,
|
|
95
|
-
name: pkgInfo.name,
|
|
96
|
-
version: pkgInfo.version || undefined || null,
|
|
97
|
-
qualifiers: finalQualifiers || null,
|
|
98
|
-
subpath: undefined || null,
|
|
99
|
-
});
|
|
100
|
-
} catch (_e) {
|
|
101
|
-
return "";
|
|
102
|
-
}
|
|
77
|
+
function apkArchitecture() {
|
|
78
|
+
return APK_ARCHITECTURES[process.arch] || process.arch;
|
|
103
79
|
}
|
|
104
80
|
|
|
105
81
|
// ---------------------------------------------------------------------------
|
|
@@ -111,7 +87,7 @@ function buildPurl(pkgInfo) {
|
|
|
111
87
|
* correctly computed purl with distro qualifiers derived from /etc/os-release.
|
|
112
88
|
*
|
|
113
89
|
* @param {string} filePath - Absolute path to the library file
|
|
114
|
-
* @returns {{ name: string, version: string, arch: string, type: string, purl: string } | undefined}
|
|
90
|
+
* @returns {{ name: string, version: string, arch: string, type: string, epoch?: string, purl: string } | undefined}
|
|
115
91
|
*/
|
|
116
92
|
export function resolvePackageForFile(filePath) {
|
|
117
93
|
if (!filePath) {
|
|
@@ -190,27 +166,30 @@ function _resolveLinux(filePath) {
|
|
|
190
166
|
return {
|
|
191
167
|
name,
|
|
192
168
|
version,
|
|
193
|
-
arch:
|
|
169
|
+
arch: apkArchitecture(),
|
|
194
170
|
type: "apk",
|
|
195
171
|
};
|
|
196
172
|
}
|
|
197
173
|
}
|
|
198
174
|
|
|
199
|
-
// 3. RPM-based (rpm -qf)
|
|
175
|
+
// 3. RPM-based (rpm -qf). The epoch is part of an rpm's identity; %{EPOCH} is
|
|
176
|
+
// (none) for a package without one, so the conditional makes that a 0, which
|
|
177
|
+
// means the same.
|
|
200
178
|
const rpmRes = safeSpawnSync("rpm", [
|
|
201
179
|
"-qf",
|
|
202
180
|
"--qf",
|
|
203
|
-
"%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\n",
|
|
181
|
+
"%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION}-%{RELEASE} %{ARCH}\n",
|
|
204
182
|
filePath,
|
|
205
183
|
]);
|
|
206
184
|
if (rpmRes && rpmRes.status === 0 && rpmRes.stdout) {
|
|
207
185
|
const line = rpmRes.stdout.split("\n")[0].trim();
|
|
208
186
|
const parts = line.split(" ");
|
|
209
|
-
if (parts.length >=
|
|
187
|
+
if (parts.length >= 4) {
|
|
210
188
|
return {
|
|
211
189
|
name: parts[0],
|
|
212
|
-
|
|
213
|
-
|
|
190
|
+
epoch: parts[1],
|
|
191
|
+
version: parts[2],
|
|
192
|
+
arch: parts[3],
|
|
214
193
|
type: "rpm",
|
|
215
194
|
};
|
|
216
195
|
}
|
|
@@ -233,7 +212,7 @@ function _resolveDarwin(filePath) {
|
|
|
233
212
|
name: segments[0],
|
|
234
213
|
version: segments[1],
|
|
235
214
|
arch: process.arch,
|
|
236
|
-
type: "brew",
|
|
215
|
+
type: "brew",
|
|
237
216
|
};
|
|
238
217
|
}
|
|
239
218
|
}
|
|
@@ -379,6 +358,9 @@ function shouldPreferUsrMergedExecutablePath(file, existingFile) {
|
|
|
379
358
|
* @param ldConf {String} Config file used by ldconfig to locate additional paths
|
|
380
359
|
* @param ldConfDirPattern {String} Config directory that can contain more .conf files for ldconfig
|
|
381
360
|
* @param excludePaths {Array[String]} Container-relative paths that should be excluded from the result set
|
|
361
|
+
* @param extraLibGlobs {Array[String]} Extra container-relative globs for files
|
|
362
|
+
* the default paths miss, such as libphp*.so under Apache's modules
|
|
363
|
+
* directory. The existing globs are unchanged; this adds candidates only.
|
|
382
364
|
*
|
|
383
365
|
* @return {Array[String]} List of executables
|
|
384
366
|
*/
|
|
@@ -388,6 +370,7 @@ export function collectSharedLibs(
|
|
|
388
370
|
ldConf,
|
|
389
371
|
ldConfDirPattern,
|
|
390
372
|
excludePaths = [],
|
|
373
|
+
extraLibGlobs = [],
|
|
391
374
|
) {
|
|
392
375
|
if (!libPaths) {
|
|
393
376
|
return [];
|
|
@@ -399,6 +382,37 @@ export function collectSharedLibs(
|
|
|
399
382
|
const ignoreList = [
|
|
400
383
|
"**/*.{h,c,cpp,hpp,man,txt,md,htm,html,jar,ear,war,zip,tar,egg,keepme,gitignore,json,js,py,pyc}",
|
|
401
384
|
];
|
|
385
|
+
const addCandidate = (file) => {
|
|
386
|
+
let resolvedFile = file;
|
|
387
|
+
try {
|
|
388
|
+
resolvedFile = relative(basePath, realpathSync(join(basePath, file)));
|
|
389
|
+
recordSymlinkResolution(join(basePath, file), resolvedFile, {
|
|
390
|
+
basePath,
|
|
391
|
+
metadata: {
|
|
392
|
+
resolutionKind: "shared-library",
|
|
393
|
+
},
|
|
394
|
+
reason: `Resolved shared library candidate ${file} to ${resolvedFile}.`,
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
recordSymlinkResolution(join(basePath, file), undefined, {
|
|
398
|
+
basePath,
|
|
399
|
+
errorCode: err?.code || err?.name,
|
|
400
|
+
metadata: {
|
|
401
|
+
resolutionKind: "shared-library",
|
|
402
|
+
},
|
|
403
|
+
reason: `Failed to resolve shared library candidate ${file}.`,
|
|
404
|
+
status: "failed",
|
|
405
|
+
});
|
|
406
|
+
// Broken symlinks or permission errors can prevent realpath resolution.
|
|
407
|
+
}
|
|
408
|
+
if (
|
|
409
|
+
excludedPathSet.has(file.replace(/^\/+/, "").replace(/\\/g, "/")) ||
|
|
410
|
+
excludedPathSet.has(resolvedFile.replace(/^\/+/, "").replace(/\\/g, "/"))
|
|
411
|
+
) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
sharedLibs.push(file);
|
|
415
|
+
};
|
|
402
416
|
const allLdConfDirs = ldConfDirPattern ? [ldConfDirPattern] : [];
|
|
403
417
|
collectAllLdConfs(basePath, ldConf, allLdConfDirs, libPaths);
|
|
404
418
|
if (allLdConfDirs.length) {
|
|
@@ -428,37 +442,25 @@ export function collectSharedLibs(
|
|
|
428
442
|
ignore: ignoreList,
|
|
429
443
|
});
|
|
430
444
|
for (const file of files) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
});
|
|
451
|
-
// Broken symlinks or permission errors can prevent realpath resolution.
|
|
452
|
-
}
|
|
453
|
-
if (
|
|
454
|
-
excludedPathSet.has(file.replace(/^\/+/, "").replace(/\\/g, "/")) ||
|
|
455
|
-
excludedPathSet.has(
|
|
456
|
-
resolvedFile.replace(/^\/+/, "").replace(/\\/g, "/"),
|
|
457
|
-
)
|
|
458
|
-
) {
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
sharedLibs.push(file);
|
|
445
|
+
addCandidate(file);
|
|
446
|
+
}
|
|
447
|
+
} catch (_err) {
|
|
448
|
+
// ignore
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const pattern of extraLibGlobs) {
|
|
452
|
+
try {
|
|
453
|
+
const files = globSync(pattern, {
|
|
454
|
+
cwd: basePath,
|
|
455
|
+
absolute: false,
|
|
456
|
+
nocase: true,
|
|
457
|
+
nodir: true,
|
|
458
|
+
dot: true,
|
|
459
|
+
follow: true,
|
|
460
|
+
ignore: ignoreList,
|
|
461
|
+
});
|
|
462
|
+
for (const file of files) {
|
|
463
|
+
addCandidate(file);
|
|
462
464
|
}
|
|
463
465
|
} catch (_err) {
|
|
464
466
|
// ignore
|
|
@@ -491,3 +493,185 @@ function collectAllLdConfs(basePath, ldConf, allLdConfDirs, libPaths) {
|
|
|
491
493
|
}
|
|
492
494
|
}
|
|
493
495
|
}
|
|
496
|
+
|
|
497
|
+
// Native modules that language package managers install outside the library
|
|
498
|
+
// directories the loader searches: CPython and PyPy extension modules, PHP
|
|
499
|
+
// extensions, Ruby gem extensions, Node.js addons, and the native half of a
|
|
500
|
+
// .NET runtime. A Brotli decoder compiled into one of them is invisible unless
|
|
501
|
+
// the file is read.
|
|
502
|
+
const NATIVE_EXTENSION_GLOBS = [
|
|
503
|
+
"**/site-packages/**/*.{so,pyd}",
|
|
504
|
+
"**/dist-packages/**/*.so",
|
|
505
|
+
"**/lib/php/**/*.so",
|
|
506
|
+
"**/lib/php*/modules/*.so",
|
|
507
|
+
"**/php*/ext/php_*.dll",
|
|
508
|
+
"**/gems/*/lib/**/*.{so,bundle}",
|
|
509
|
+
"**/gems/*/extensions/**/*.{so,bundle}",
|
|
510
|
+
"**/bundle/extensions/**/*.{so,bundle}",
|
|
511
|
+
"**/node_modules/**/*.node",
|
|
512
|
+
"**/dotnet/shared/*/*/*.so",
|
|
513
|
+
"**/.dotnet/shared/*/*/*.so",
|
|
514
|
+
// Of a Windows runtime's many DLLs, only this one is native and holds the
|
|
515
|
+
// decoder.
|
|
516
|
+
"**/dotnet/shared/*/*/System.IO.Compression.Native.dll",
|
|
517
|
+
];
|
|
518
|
+
const NATIVE_EXTENSION_IGNORES = ["proc/**", "sys/**", "dev/**"];
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* The most native extension candidates one scan returns.
|
|
522
|
+
*
|
|
523
|
+
* @type {number}
|
|
524
|
+
*/
|
|
525
|
+
export const MAX_NATIVE_EXTENSIONS = 4096;
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Collect native extension modules under a root file system that the library
|
|
529
|
+
* collector does not see, leaving out paths a package database owns and paths
|
|
530
|
+
* already collected. Symlinks are not followed, so a link pointing out of the
|
|
531
|
+
* root is never listed.
|
|
532
|
+
*
|
|
533
|
+
* @param {string} basePath extracted root file system
|
|
534
|
+
* @param {string[]} [excludePaths] root-relative paths to leave out
|
|
535
|
+
* @returns {string[]} root-relative paths, sorted, capped at MAX_NATIVE_EXTENSIONS
|
|
536
|
+
*/
|
|
537
|
+
export function collectNativeExtensions(basePath, excludePaths = []) {
|
|
538
|
+
if (!basePath) {
|
|
539
|
+
return [];
|
|
540
|
+
}
|
|
541
|
+
const excluded = new Set(
|
|
542
|
+
(excludePaths || []).map((f) => f.replace(/^\/+/, "").replace(/\\/g, "/")),
|
|
543
|
+
);
|
|
544
|
+
let files = [];
|
|
545
|
+
try {
|
|
546
|
+
files = globSync(NATIVE_EXTENSION_GLOBS, {
|
|
547
|
+
cwd: basePath,
|
|
548
|
+
absolute: false,
|
|
549
|
+
nodir: true,
|
|
550
|
+
dot: true,
|
|
551
|
+
follow: false,
|
|
552
|
+
ignore: NATIVE_EXTENSION_IGNORES,
|
|
553
|
+
});
|
|
554
|
+
} catch (_err) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const result = new Set();
|
|
558
|
+
for (const file of files) {
|
|
559
|
+
const normalized = file.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
560
|
+
if (!excluded.has(normalized)) {
|
|
561
|
+
result.add(normalized);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return Array.from(result).sort().slice(0, MAX_NATIVE_EXTENSIONS);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const OS_PACKAGE_PURL_PREFIXES = [
|
|
568
|
+
"pkg:deb/",
|
|
569
|
+
"pkg:apk/",
|
|
570
|
+
"pkg:rpm/",
|
|
571
|
+
"pkg:alpm/",
|
|
572
|
+
"pkg:qpkg/",
|
|
573
|
+
"pkg:generic/",
|
|
574
|
+
];
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Whether a source file is the installation record of a language package,
|
|
578
|
+
* the file a package manager writes when it installs the package: dist-info
|
|
579
|
+
* or egg-info metadata, an installed gemspec, a package.json inside
|
|
580
|
+
* node_modules, or a jar. A lock file or manifest only names packages, so an
|
|
581
|
+
* OS package owning one says nothing about who installed them.
|
|
582
|
+
*
|
|
583
|
+
* @param {string} filePath root-relative path with forward slashes
|
|
584
|
+
* @returns {boolean}
|
|
585
|
+
*/
|
|
586
|
+
function isInstallRecord(filePath) {
|
|
587
|
+
const parts = filePath.split("/");
|
|
588
|
+
const name = parts.at(-1) || "";
|
|
589
|
+
const parent = parts.at(-2) || "";
|
|
590
|
+
if (parent.endsWith(".dist-info") || parent.endsWith(".egg-info")) {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
if (name.endsWith(".egg-info") || name.endsWith(".jar")) {
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
if (name.endsWith(".gemspec") && parts.includes("specifications")) {
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
return name === "package.json" && parts.includes("node_modules");
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Mark language packages that an OS package installed. Alpine's py3-brotli
|
|
604
|
+
* ships a Brotli-1.1.0.dist-info that a Python scan reports as
|
|
605
|
+
* pkg:pypi/brotli@1.1.0, but a distro fixes it by backporting under the same
|
|
606
|
+
* upstream version, so a PyPI advisory range keeps matching after the fix. The
|
|
607
|
+
* `cdx:os:installedBy` property names the OS package that owns the
|
|
608
|
+
* installation record, so a consumer can judge the component by that package
|
|
609
|
+
* instead.
|
|
610
|
+
*
|
|
611
|
+
* A component is marked only when every installation record it was read from
|
|
612
|
+
* belongs to an OS package: a copy that pip put in a virtual environment
|
|
613
|
+
* beside the distro's is not the distro's to fix. Run it after duplicates
|
|
614
|
+
* merge, so that a merged component carries all of its records.
|
|
615
|
+
*
|
|
616
|
+
* @param {Object[]} components BOM components, mutated in place
|
|
617
|
+
* @param {Map<string, string>|undefined} ownerByPath root-relative file path to
|
|
618
|
+
* owning OS package bom-ref
|
|
619
|
+
* @param {string} [rootDir] extracted root, stripped from absolute source paths
|
|
620
|
+
* @returns {number} how many components were marked
|
|
621
|
+
*/
|
|
622
|
+
export function markOsInstalledLanguagePackages(
|
|
623
|
+
components,
|
|
624
|
+
ownerByPath,
|
|
625
|
+
rootDir,
|
|
626
|
+
) {
|
|
627
|
+
if (!ownerByPath?.size || !Array.isArray(components)) {
|
|
628
|
+
return 0;
|
|
629
|
+
}
|
|
630
|
+
const rootPrefix = rootDir
|
|
631
|
+
? rootDir.replaceAll("\\", "/").replace(/\/+$/, "")
|
|
632
|
+
: undefined;
|
|
633
|
+
let marked = 0;
|
|
634
|
+
for (const component of components) {
|
|
635
|
+
const purl = component?.purl;
|
|
636
|
+
if (
|
|
637
|
+
typeof purl !== "string" ||
|
|
638
|
+
!purl.startsWith("pkg:") ||
|
|
639
|
+
OS_PACKAGE_PURL_PREFIXES.some((prefix) => purl.startsWith(prefix)) ||
|
|
640
|
+
!Array.isArray(component.properties) ||
|
|
641
|
+
component.properties.some((prop) => prop.name === "cdx:os:installedBy")
|
|
642
|
+
) {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
const owners = new Set();
|
|
646
|
+
let records = 0;
|
|
647
|
+
let unowned = 0;
|
|
648
|
+
for (const prop of component.properties) {
|
|
649
|
+
if (prop.name !== "internal:SrcFile" || typeof prop.value !== "string") {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
let filePath = prop.value.replaceAll("\\", "/");
|
|
653
|
+
if (rootPrefix && filePath.startsWith(`${rootPrefix}/`)) {
|
|
654
|
+
filePath = filePath.slice(rootPrefix.length);
|
|
655
|
+
}
|
|
656
|
+
filePath = filePath.replace(/^\/+/, "");
|
|
657
|
+
if (!isInstallRecord(filePath)) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
records += 1;
|
|
661
|
+
const owner = ownerByPath.get(filePath);
|
|
662
|
+
if (owner) {
|
|
663
|
+
owners.add(owner);
|
|
664
|
+
} else {
|
|
665
|
+
unowned += 1;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (!records || unowned) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
for (const owner of Array.from(owners).sort()) {
|
|
672
|
+
component.properties.push({ name: "cdx:os:installedBy", value: owner });
|
|
673
|
+
}
|
|
674
|
+
marked += 1;
|
|
675
|
+
}
|
|
676
|
+
return marked;
|
|
677
|
+
}
|
package/lib/inventory/osinfo.js
CHANGED
|
@@ -49,14 +49,34 @@ export const OS_DISTRO_ALIAS = {
|
|
|
49
49
|
"ubuntu-24.10": "oracular",
|
|
50
50
|
"ubuntu-25.04": "plucky",
|
|
51
51
|
"ubuntu-25.10": "questing",
|
|
52
|
+
"ubuntu-26.04": "resolute",
|
|
53
|
+
"ubuntu-26.10": "stonking",
|
|
52
54
|
"debian-15": "duke",
|
|
53
55
|
"debian-14": "forky",
|
|
54
56
|
"debian-14.5": "forky",
|
|
55
57
|
"debian-13": "trixie",
|
|
58
|
+
"debian-13.7": "trixie",
|
|
59
|
+
"debian-13.6": "trixie",
|
|
56
60
|
"debian-13.5": "trixie",
|
|
61
|
+
"debian-13.4": "trixie",
|
|
62
|
+
"debian-13.3": "trixie",
|
|
63
|
+
"debian-13.2": "trixie",
|
|
64
|
+
"debian-13.1": "trixie",
|
|
57
65
|
"debian-12": "bookworm",
|
|
58
|
-
"debian-12.
|
|
66
|
+
"debian-12.15": "bookworm",
|
|
67
|
+
"debian-12.14": "bookworm",
|
|
68
|
+
"debian-12.13": "bookworm",
|
|
69
|
+
"debian-12.12": "bookworm",
|
|
70
|
+
"debian-12.11": "bookworm",
|
|
71
|
+
"debian-12.10": "bookworm",
|
|
72
|
+
"debian-12.9": "bookworm",
|
|
73
|
+
"debian-12.8": "bookworm",
|
|
74
|
+
"debian-12.7": "bookworm",
|
|
59
75
|
"debian-12.6": "bookworm",
|
|
76
|
+
"debian-12.5": "bookworm",
|
|
77
|
+
"debian-12.4": "bookworm",
|
|
78
|
+
"debian-12.2": "bookworm",
|
|
79
|
+
"debian-12.1": "bookworm",
|
|
60
80
|
"debian-11": "bullseye",
|
|
61
81
|
"debian-11.5": "bullseye",
|
|
62
82
|
"debian-10": "buster",
|
|
@@ -256,15 +276,20 @@ export function _resetOsReleaseCache() {
|
|
|
256
276
|
* Derive structured distro information from an os-release file.
|
|
257
277
|
*
|
|
258
278
|
* Returns an object with:
|
|
259
|
-
* - purlType {string} "deb" | "apk" | "rpm"
|
|
279
|
+
* - purlType {string} "deb" | "apk" | "rpm" | "alpm" (Arch and the
|
|
280
|
+
* distros built on it)
|
|
260
281
|
* - namespace {string} canonical purl namespace (e.g. "ubuntu", "alpine",
|
|
261
282
|
* "azure-linux" for both Azure Linux and CBL-Mariner)
|
|
262
283
|
* - distroId {string} ID + "-" + VERSION_ID with a canonical vendor
|
|
263
|
-
* prefix (e.g. "ubuntu-22.04", "azure-linux-3.0"
|
|
264
|
-
*
|
|
284
|
+
* prefix (e.g. "ubuntu-22.04", "azure-linux-3.0",
|
|
285
|
+
* "alpine-3.24.3")
|
|
286
|
+
* - distroName {string} release codename, or a token OS_DISTRO_ALIAS knows
|
|
287
|
+
* (e.g. "jammy", "rhel", "alpine-3.24"); empty when
|
|
288
|
+
* the distro has neither
|
|
265
289
|
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
290
|
+
* lib/managers/binary.js getOSPackages() reads its distro from here, so a
|
|
291
|
+
* container scan and an OBOM run on the same system give a package the same
|
|
292
|
+
* `distro` and `distro_name` qualifiers.
|
|
268
293
|
*
|
|
269
294
|
* @param {string} [root="/"] - Filesystem root to look for os-release.
|
|
270
295
|
* @returns {{ purlType: string, namespace: string, distroId: string, distroName: string }}
|
|
@@ -290,6 +315,8 @@ export function getDistroInfo(root = "/") {
|
|
|
290
315
|
case "alpine":
|
|
291
316
|
case "alpaquita":
|
|
292
317
|
case "openwrt":
|
|
318
|
+
case "wolfi":
|
|
319
|
+
case "chainguard":
|
|
293
320
|
purlType = "apk";
|
|
294
321
|
break;
|
|
295
322
|
case "sles":
|
|
@@ -312,11 +339,23 @@ export function getDistroInfo(root = "/") {
|
|
|
312
339
|
case "photon":
|
|
313
340
|
purlType = "rpm";
|
|
314
341
|
break;
|
|
342
|
+
case "arch":
|
|
343
|
+
case "archarm":
|
|
344
|
+
case "manjaro":
|
|
345
|
+
case "endeavouros":
|
|
346
|
+
case "cachyos":
|
|
347
|
+
case "steamos":
|
|
348
|
+
case "artix":
|
|
349
|
+
purlType = "alpm";
|
|
350
|
+
break;
|
|
315
351
|
default:
|
|
316
352
|
if (idLike.includes("debian") || idLike.includes("ubuntu")) {
|
|
317
353
|
purlType = "deb";
|
|
318
354
|
} else if (idLike.includes("alpine")) {
|
|
319
355
|
purlType = "apk";
|
|
356
|
+
} else if (idLike.split(/\s+/u).includes("arch")) {
|
|
357
|
+
// A token, not a substring: ID_LIKE is a space-separated list.
|
|
358
|
+
purlType = "alpm";
|
|
320
359
|
} else if (
|
|
321
360
|
idLike.includes("rhel") ||
|
|
322
361
|
idLike.includes("centos") ||
|
|
@@ -343,41 +382,50 @@ export function getDistroInfo(root = "/") {
|
|
|
343
382
|
namespace = "debian";
|
|
344
383
|
} else if (idLike.includes("ubuntu")) {
|
|
345
384
|
namespace = "ubuntu";
|
|
385
|
+
} else if (purlType === "alpm") {
|
|
386
|
+
namespace = "arch";
|
|
346
387
|
} else {
|
|
347
388
|
namespace = "linux";
|
|
348
389
|
}
|
|
349
390
|
}
|
|
350
391
|
|
|
351
|
-
// Build distroId = ID-VERSION_ID (e.g. "fedora-25",
|
|
352
|
-
// canonicalising the vendor prefix so one release has a
|
|
353
|
-
// value (e.g. "mariner-2.0" and "azurelinux-3.0" ->
|
|
354
|
-
// "azure-linux-3.0")
|
|
355
|
-
|
|
356
|
-
distroId = canonicalDistroQualifier(
|
|
392
|
+
// Build distroId = ID-VERSION_ID as os-release writes it (e.g. "fedora-25",
|
|
393
|
+
// "alpine-3.24.3"), canonicalising the vendor prefix so one release has a
|
|
394
|
+
// single qualifier value (e.g. "mariner-2.0" and "azurelinux-3.0" ->
|
|
395
|
+
// "azure-linux-2.0"/"azure-linux-3.0")
|
|
396
|
+
const releaseId = versionId ? `${rawId}-${versionId}` : rawId;
|
|
397
|
+
const distroId = canonicalDistroQualifier(releaseId);
|
|
357
398
|
|
|
358
|
-
//
|
|
359
|
-
|
|
399
|
+
// distro_name is a release codename or a token OS_DISTRO_ALIAS knows
|
|
400
|
+
// ("bookworm", "jammy", "rhel", "alpine-3.24"), never a product display
|
|
401
|
+
// name: "AlmaLinux" or "Rocky Linux 9" has no place in a purl, since RPM
|
|
402
|
+
// distros track packages flat and a display name there breaks vulnerability
|
|
403
|
+
// matching. A product name counts only when it resolves through the alias
|
|
404
|
+
// table, which holds the RHEL names.
|
|
405
|
+
let distroName = (info.VERSION_CODENAME || "").toLowerCase();
|
|
406
|
+
if (!distroName) {
|
|
407
|
+
const productName = (
|
|
408
|
+
info.CENTOS_MANTISBT_PROJECT ||
|
|
409
|
+
info.REDHAT_BUGZILLA_PRODUCT ||
|
|
410
|
+
info.REDHAT_SUPPORT_PRODUCT ||
|
|
411
|
+
""
|
|
412
|
+
).toLowerCase();
|
|
413
|
+
if (productName && Object.hasOwn(OS_DISTRO_ALIAS, productName)) {
|
|
414
|
+
distroName = OS_DISTRO_ALIAS[productName];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Alpine names its release series: 3.24.3 belongs to alpine-3.24, the unit
|
|
418
|
+
// its security data is kept in.
|
|
419
|
+
if (rawId === "alpine" && versionId) {
|
|
360
420
|
const parts = versionId.split(".");
|
|
361
421
|
if (parts.length >= 2) {
|
|
362
|
-
|
|
422
|
+
distroName = `alpine-${parts[0]}.${parts[1]}`;
|
|
363
423
|
}
|
|
364
424
|
}
|
|
365
|
-
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
info.CENTOS_MANTISBT_PROJECT ||
|
|
370
|
-
info.REDHAT_BUGZILLA_PRODUCT ||
|
|
371
|
-
info.REDHAT_SUPPORT_PRODUCT ||
|
|
372
|
-
"";
|
|
373
|
-
distroName = distroName.toLowerCase();
|
|
374
|
-
|
|
375
|
-
// Resolve well-known display name aliases
|
|
376
|
-
if (distroName.includes(" ") && OS_DISTRO_ALIAS[distroName]) {
|
|
377
|
-
distroName = OS_DISTRO_ALIAS[distroName];
|
|
378
|
-
}
|
|
379
|
-
if (!distroName && OS_DISTRO_ALIAS[distroId]) {
|
|
380
|
-
distroName = OS_DISTRO_ALIAS[distroId];
|
|
425
|
+
// The alias of the release itself (ubuntu-24.04 -> noble) wins over the
|
|
426
|
+
// codename the file carries.
|
|
427
|
+
if (versionId && Object.hasOwn(OS_DISTRO_ALIAS, releaseId)) {
|
|
428
|
+
distroName = OS_DISTRO_ALIAS[releaseId];
|
|
381
429
|
}
|
|
382
430
|
|
|
383
431
|
return { purlType, namespace, distroId, distroName };
|