@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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// Bounded, read-only probe for the Ruby no package manager owns on a live host,
|
|
2
|
+
// for `-t os` / obom. osquery's package tables list what dpkg, rpm and the like
|
|
3
|
+
// installed, so a Ruby built by ruby-build, rbenv, rvm, asdf, mise, chruby or
|
|
4
|
+
// frum, unpacked from a toolcache tarball, or installed under /usr/local or
|
|
5
|
+
// /opt is invisible to them, and with it the version an advisory in the
|
|
6
|
+
// interpreter core is read against.
|
|
7
|
+
//
|
|
8
|
+
// Candidates come from fixed directories and from each version manager's tree
|
|
9
|
+
// under the home directories, one wildcard level at a time, never from a walk
|
|
10
|
+
// of the file system. The ownership rules, caps and degradations are the
|
|
11
|
+
// shared host-runtime probe's. Only rbconfig.rb and the interpreter's own
|
|
12
|
+
// files are read; nothing is executed.
|
|
13
|
+
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
isDryRun,
|
|
18
|
+
readEnvironmentVariable,
|
|
19
|
+
recordActivity,
|
|
20
|
+
} from "../core/activity.js";
|
|
21
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
22
|
+
import {
|
|
23
|
+
isDirectory,
|
|
24
|
+
listDirectory,
|
|
25
|
+
probeHostRuntime,
|
|
26
|
+
} from "./hostRuntimeProbe.js";
|
|
27
|
+
import {
|
|
28
|
+
attachRubyBuild,
|
|
29
|
+
createRubyRuntimeInventory,
|
|
30
|
+
isRubyBinaryName,
|
|
31
|
+
MAX_RUBY_PROBES,
|
|
32
|
+
readRubyBuildVersion,
|
|
33
|
+
} from "./rubyBuildInfo.js";
|
|
34
|
+
|
|
35
|
+
// The most directories one wildcard pattern may expand to.
|
|
36
|
+
const MAX_EXPANDED_DIRECTORIES = 256;
|
|
37
|
+
|
|
38
|
+
// Relative to the root.
|
|
39
|
+
const POSIX_DIRECTORIES = {
|
|
40
|
+
linux: [
|
|
41
|
+
"usr/local/bin",
|
|
42
|
+
"opt/*/bin",
|
|
43
|
+
"opt/rubies/*/bin",
|
|
44
|
+
"opt/hostedtoolcache/Ruby/*/*/bin",
|
|
45
|
+
"usr/local/rbenv/versions/*/bin",
|
|
46
|
+
"usr/local/rvm/rubies/*/bin",
|
|
47
|
+
],
|
|
48
|
+
darwin: [
|
|
49
|
+
"usr/local/bin",
|
|
50
|
+
"opt/*/bin",
|
|
51
|
+
"opt/rubies/*/bin",
|
|
52
|
+
"Users/runner/hostedtoolcache/Ruby/*/*/bin",
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Relative to a home directory.
|
|
57
|
+
export const RUBY_VERSION_MANAGER_DIRECTORIES = [
|
|
58
|
+
".rbenv/versions/*/bin",
|
|
59
|
+
".rvm/rubies/*/bin",
|
|
60
|
+
".asdf/installs/ruby/*/bin",
|
|
61
|
+
".local/share/mise/installs/ruby/*/bin",
|
|
62
|
+
".rubies/*/bin",
|
|
63
|
+
".frum/versions/*/bin",
|
|
64
|
+
".local/share/rv/rubies/*/bin",
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export const HOME_PARENTS = {
|
|
68
|
+
linux: ["home"],
|
|
69
|
+
darwin: ["Users"],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Expand a relative directory pattern whose `*` segments match one directory
|
|
74
|
+
* name each.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} base absolute directory the pattern hangs off
|
|
77
|
+
* @param {string} pattern `/`-separated relative pattern
|
|
78
|
+
* @returns {string[]} existing directories, at most MAX_EXPANDED_DIRECTORIES
|
|
79
|
+
*/
|
|
80
|
+
export function expandDirectories(base, pattern) {
|
|
81
|
+
let current = [base];
|
|
82
|
+
for (const segment of pattern.split("/")) {
|
|
83
|
+
const next = [];
|
|
84
|
+
for (const directory of current) {
|
|
85
|
+
if (segment !== "*") {
|
|
86
|
+
next.push(join(directory, segment));
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
for (const name of listDirectory(directory)) {
|
|
90
|
+
const child = join(directory, name);
|
|
91
|
+
if (isDirectory(child)) {
|
|
92
|
+
next.push(child);
|
|
93
|
+
}
|
|
94
|
+
if (next.length >= MAX_EXPANDED_DIRECTORIES) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
current = next.slice(0, MAX_EXPANDED_DIRECTORIES);
|
|
100
|
+
}
|
|
101
|
+
return current.filter(isDirectory);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The home directories of a host: /root and /home/* on Linux, /Users/* on
|
|
106
|
+
* macOS.
|
|
107
|
+
*
|
|
108
|
+
* @param {{rootPath: string, platform: string}} context probe context
|
|
109
|
+
* @returns {string[]}
|
|
110
|
+
*/
|
|
111
|
+
export function homeDirectories(context) {
|
|
112
|
+
const { rootPath, platform } = context;
|
|
113
|
+
const homes = [];
|
|
114
|
+
if (platform === "linux") {
|
|
115
|
+
homes.push(join(rootPath, "root"));
|
|
116
|
+
}
|
|
117
|
+
for (const parent of HOME_PARENTS[platform] || []) {
|
|
118
|
+
for (const name of listDirectory(join(rootPath, parent))) {
|
|
119
|
+
const home = join(rootPath, parent, name);
|
|
120
|
+
if (isDirectory(home)) {
|
|
121
|
+
homes.push(home);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return homes.slice(0, MAX_EXPANDED_DIRECTORIES);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The directories a Windows Ruby lives in: the RubyInstaller's C:\Ruby34-x64,
|
|
130
|
+
* Chocolatey's C:\tools\ruby34, and the hosted toolcache.
|
|
131
|
+
*
|
|
132
|
+
* @param {Object} context probe context
|
|
133
|
+
* @returns {string[]} install roots, each with a `bin` directory
|
|
134
|
+
*/
|
|
135
|
+
export function windowsRubyRoots(context) {
|
|
136
|
+
const { rootPath } = context;
|
|
137
|
+
const roots = [];
|
|
138
|
+
for (const parent of [rootPath, join(rootPath, "tools")]) {
|
|
139
|
+
for (const name of listDirectory(parent)) {
|
|
140
|
+
const directory = join(parent, name);
|
|
141
|
+
if (name.toLowerCase().startsWith("ruby") && isDirectory(directory)) {
|
|
142
|
+
roots.push(directory);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
roots.push(
|
|
147
|
+
...expandDirectories(rootPath, "hostedtoolcache/windows/Ruby/*/*"),
|
|
148
|
+
);
|
|
149
|
+
return roots.slice(0, MAX_EXPANDED_DIRECTORIES);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function candidateDirectories(context) {
|
|
153
|
+
const { rootPath, platform } = context;
|
|
154
|
+
const directories = [];
|
|
155
|
+
if (platform === "win32") {
|
|
156
|
+
for (const root of windowsRubyRoots(context)) {
|
|
157
|
+
directories.push({ directory: join(root, "bin") });
|
|
158
|
+
}
|
|
159
|
+
return directories;
|
|
160
|
+
}
|
|
161
|
+
for (const pattern of POSIX_DIRECTORIES[platform] || []) {
|
|
162
|
+
for (const directory of expandDirectories(rootPath, pattern)) {
|
|
163
|
+
directories.push({ directory });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
for (const home of homeDirectories(context)) {
|
|
167
|
+
for (const pattern of RUBY_VERSION_MANAGER_DIRECTORIES) {
|
|
168
|
+
for (const directory of expandDirectories(home, pattern)) {
|
|
169
|
+
directories.push({ directory });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return directories;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Look for Ruby no package manager owns on the host. Components come back
|
|
178
|
+
* only for the files nothing owns: a `file` component for each `ruby` binary
|
|
179
|
+
* with the cdx:ruby properties, and one `platform` component per Ruby version
|
|
180
|
+
* with its CPE.
|
|
181
|
+
*
|
|
182
|
+
* @param {Object} [options] probe options, as probeHostPhp takes them
|
|
183
|
+
* @returns {{components: Object[], dependencies: Object[]}}
|
|
184
|
+
*/
|
|
185
|
+
export function probeHostRuby(options = {}) {
|
|
186
|
+
return probeHostRuntime(options, {
|
|
187
|
+
label: "Ruby",
|
|
188
|
+
ecosystem: "ruby",
|
|
189
|
+
capability: "ruby-host-probe",
|
|
190
|
+
remediationId: "ruby.host-probe.package-db-unreadable",
|
|
191
|
+
maxProbes: MAX_RUBY_PROBES,
|
|
192
|
+
chocolateyPackages: ["ruby"],
|
|
193
|
+
// Chocolatey's ruby package runs the RubyInstaller into C:\tools.
|
|
194
|
+
chocolateyDirectories: (context) => [join(context.rootPath, "tools")],
|
|
195
|
+
directories: candidateDirectories,
|
|
196
|
+
classify: (name, platform) => {
|
|
197
|
+
if (platform === "win32") {
|
|
198
|
+
return name.toLowerCase() === "ruby.exe" ? "ruby" : undefined;
|
|
199
|
+
}
|
|
200
|
+
return isRubyBinaryName(name) ? "ruby" : undefined;
|
|
201
|
+
},
|
|
202
|
+
readBuild: (context, candidate) =>
|
|
203
|
+
readRubyBuildVersion(candidate.real, context.realRoot),
|
|
204
|
+
createInventory: createRubyRuntimeInventory,
|
|
205
|
+
attach: (inventory, component, build) =>
|
|
206
|
+
attachRubyBuild(inventory, component, build),
|
|
207
|
+
finish: (inventory) => ({
|
|
208
|
+
components: inventory.runtimeComponents,
|
|
209
|
+
dependencies: inventory.dependencies,
|
|
210
|
+
}),
|
|
211
|
+
hooks: {
|
|
212
|
+
isDryRun,
|
|
213
|
+
readEnvironmentVariable,
|
|
214
|
+
recordActivity,
|
|
215
|
+
recordDegradation,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
@@ -7,7 +7,7 @@ import { Purl } from "@cdxgen/cdx-purl";
|
|
|
7
7
|
// platform suffix (`jwt-core`, `upickle_sjs1`), so the same library is named
|
|
8
8
|
// differently depending on the build tool that reported it.
|
|
9
9
|
const SCALA_BINARY_SUFFIX_REGEX = /_(2\.\d+|3)$/;
|
|
10
|
-
const SCALA_PLATFORM_SUFFIX_REGEX = /_(
|
|
10
|
+
const SCALA_PLATFORM_SUFFIX_REGEX = /_(sjs\d+(?:\.\d+)?|native\d+(?:\.\d+)?)$/;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Parse a Scala-published Maven artifact name into its coordinate parts.
|
|
@@ -123,10 +123,11 @@ export function scalaCoordinateOfPurl(purlString) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// An artifact name that already ends in a full Scala version, as scala-cli
|
|
126
|
-
// `:::` and sbt `CrossVersion.full` artifacts do (`_3.3.7`, `_2.13.16
|
|
126
|
+
// `:::` and sbt `CrossVersion.full` artifacts do (`_3.3.7`, `_2.13.16`,
|
|
127
|
+
// `_3.4.0-RC1-bin-20231010-abc1234-NIGHTLY`). A
|
|
127
128
|
// binary suffix such as `_3` left on a name is part of the name: the
|
|
128
129
|
// component producers strip exactly one, into the compiler version.
|
|
129
|
-
const SCALA_FULL_VERSION_SUFFIX_REGEX = /_\d+\.\d+\.\d+(?:-[A-Za-z0-9
|
|
130
|
+
const SCALA_FULL_VERSION_SUFFIX_REGEX = /_\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/;
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
133
|
* The artifactId the library is published under, for cache and repository
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { Purl } from "@cdxgen/cdx-purl";
|
|
4
|
+
|
|
5
|
+
import { mergeDependencies, trimComponents } from "./depsUtils.js";
|
|
6
|
+
import { mavenPurl } from "./purl.js";
|
|
7
|
+
import { publishedArtifactId, scalaCoordinateOfPurl } from "./scalaCoords.js";
|
|
8
|
+
|
|
9
|
+
const COMPILER_VERSION_PROPERTY = "cdx:scala:compilerVersion";
|
|
10
|
+
|
|
11
|
+
// A jar scan that read the coordinates from the jar's own pom.properties
|
|
12
|
+
// records them with this confidence; a guess from the file name or the
|
|
13
|
+
// manifest records less.
|
|
14
|
+
const DESCRIPTOR_CONFIDENCE = 0.5;
|
|
15
|
+
|
|
16
|
+
function compilerVersionsOf(component) {
|
|
17
|
+
return (component.properties || [])
|
|
18
|
+
.filter((property) => property.name === COMPILER_VERSION_PROPERTY)
|
|
19
|
+
.map((property) => property.value)
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parsedPurl(component) {
|
|
24
|
+
try {
|
|
25
|
+
return Purl.parse(component?.purl || "");
|
|
26
|
+
} catch (_err) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// The jar names a component was read from, without the extension.
|
|
32
|
+
function jarStems(component) {
|
|
33
|
+
return (component.properties || [])
|
|
34
|
+
.filter((property) => property.name === "internal:SrcFile")
|
|
35
|
+
.map((property) => basename(String(property.value || "")))
|
|
36
|
+
.filter((name) => name.toLowerCase().endsWith(".jar"))
|
|
37
|
+
.map((name) => name.slice(0, -".jar".length));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isJarSourced(component) {
|
|
41
|
+
return jarStems(component).length > 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Whether a jar scan guessed the coordinates rather than reading them from the
|
|
45
|
+
// jar's Maven descriptor. A jar with a descriptor names its own artifact, even
|
|
46
|
+
// when that artifact is spelled `<group>.<name>` like a staged copy.
|
|
47
|
+
function hasGuessedIdentity(component) {
|
|
48
|
+
const identities = [component.evidence?.identity || []].flat();
|
|
49
|
+
return !identities.some(
|
|
50
|
+
(identity) =>
|
|
51
|
+
(identity?.confidence ?? 0) >= DESCRIPTOR_CONFIDENCE ||
|
|
52
|
+
(identity?.methods || []).some(
|
|
53
|
+
(method) => (method?.confidence ?? 0) >= DESCRIPTOR_CONFIDENCE,
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A classifier or a type other than jar names a different artifact of the same
|
|
59
|
+
// coordinates, which is not a duplicate.
|
|
60
|
+
function isPlainJar(component) {
|
|
61
|
+
const purl = parsedPurl(component);
|
|
62
|
+
if (!purl) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const qualifiers = purl.qualifiers || {};
|
|
66
|
+
return (
|
|
67
|
+
!qualifiers.classifier && (!qualifiers.type || qualifiers.type === "jar")
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The Scala binary versions a component was built for: the suffix its name
|
|
72
|
+
// keeps, or the compiler versions a build tool recorded. Empty for a library
|
|
73
|
+
// that is not a Scala artifact.
|
|
74
|
+
function binaryVersionsOf(component) {
|
|
75
|
+
const suffixVersion = scalaCoordinateOfPurl(component.purl)?.binaryVersion;
|
|
76
|
+
return new Set(
|
|
77
|
+
suffixVersion ? [suffixVersion] : compilerVersionsOf(component),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Two spellings name one artifact only when they were built for the same Scala
|
|
82
|
+
// binary version, or both for none. `kafka_2.12` and `kafka_2.13` share the
|
|
83
|
+
// normalized coordinates but are different artifacts.
|
|
84
|
+
function sameBinaryVersion(a, b) {
|
|
85
|
+
const left = binaryVersionsOf(a);
|
|
86
|
+
const right = binaryVersionsOf(b);
|
|
87
|
+
if (!left.size || !right.size) {
|
|
88
|
+
return !left.size && !right.size;
|
|
89
|
+
}
|
|
90
|
+
return [...left].some((version) => right.has(version));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Lower is better. A component a build tool reported outranks one a jar scan
|
|
94
|
+
// named; then the name without a `<group>.` prefix, then the purl that names
|
|
95
|
+
// its repository, then the shorter purl, then the lexicographically first, so
|
|
96
|
+
// the choice never depends on the order of the BOM.
|
|
97
|
+
function rank(component) {
|
|
98
|
+
return [
|
|
99
|
+
isJarSourced(component) ? 1 : 0,
|
|
100
|
+
component.name?.startsWith(`${component.group}.`) ? 1 : 0,
|
|
101
|
+
component.purl.includes("repository_url=") ? 0 : 1,
|
|
102
|
+
component.purl.length,
|
|
103
|
+
component.purl,
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isBetter(candidate, component) {
|
|
108
|
+
const a = rank(candidate);
|
|
109
|
+
const b = rank(component);
|
|
110
|
+
for (let i = 0; i < a.length; i++) {
|
|
111
|
+
if (a[i] !== b[i]) {
|
|
112
|
+
return a[i] < b[i];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function best(components) {
|
|
119
|
+
let chosen;
|
|
120
|
+
for (const component of components) {
|
|
121
|
+
if (!chosen || isBetter(component, chosen)) {
|
|
122
|
+
chosen = component;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return chosen;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function refOf(component) {
|
|
129
|
+
return component["bom-ref"] || component.purl;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The file names sbt-native-packager gives a staged copy of a library,
|
|
133
|
+
// without the version: `<group>.<artifact>` with the artifact as published,
|
|
134
|
+
// once for each Scala binary version it was built for, and once as named,
|
|
135
|
+
// since a module of the build itself is staged without the suffix when it
|
|
136
|
+
// turns cross paths off.
|
|
137
|
+
function stagedStems(component) {
|
|
138
|
+
return [
|
|
139
|
+
...new Set([
|
|
140
|
+
`${component.group}.${component.name}`,
|
|
141
|
+
...compilerVersionsOf(component).map(
|
|
142
|
+
(version) =>
|
|
143
|
+
`${component.group}.${publishedArtifactId({
|
|
144
|
+
name: component.name,
|
|
145
|
+
properties: [{ name: COMPILER_VERSION_PROPERTY, value: version }],
|
|
146
|
+
})}`,
|
|
147
|
+
),
|
|
148
|
+
]),
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The listed libraries a staged file name can be a copy of, with the version
|
|
153
|
+
// the name carries: every split of the name at a hyphen whose left side is the
|
|
154
|
+
// staged stem of a library.
|
|
155
|
+
function stagedOwners(stem, ownersByStem) {
|
|
156
|
+
const found = [];
|
|
157
|
+
for (
|
|
158
|
+
let index = stem.indexOf("-");
|
|
159
|
+
index > 0;
|
|
160
|
+
index = stem.indexOf("-", index + 1)
|
|
161
|
+
) {
|
|
162
|
+
const owners = ownersByStem.get(stem.slice(0, index));
|
|
163
|
+
if (owners) {
|
|
164
|
+
found.push({ owners, version: stem.slice(index + 1) });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return found;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function rewriteReferences(dependencies, refMap) {
|
|
171
|
+
if (!refMap.size) {
|
|
172
|
+
return dependencies;
|
|
173
|
+
}
|
|
174
|
+
const rewritten = (dependencies || []).map((dependency) => {
|
|
175
|
+
const ref = refMap.get(dependency.ref) || dependency.ref;
|
|
176
|
+
return {
|
|
177
|
+
...dependency,
|
|
178
|
+
ref,
|
|
179
|
+
...(dependency.dependsOn
|
|
180
|
+
? {
|
|
181
|
+
dependsOn: [
|
|
182
|
+
...new Set(
|
|
183
|
+
dependency.dependsOn.map(
|
|
184
|
+
(dependsOn) => refMap.get(dependsOn) || dependsOn,
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
].filter((dependsOn) => dependsOn !== ref),
|
|
188
|
+
}
|
|
189
|
+
: {}),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
return mergeDependencies([], rewritten);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Collapse the components that stand for one Scala library.
|
|
197
|
+
*
|
|
198
|
+
* A build tool such as sbt reports a library as `group:name@version` with the
|
|
199
|
+
* Scala binary suffix moved into `cdx:scala:compilerVersion`. The jars a scan
|
|
200
|
+
* finds on disk name the same library again, and not in the same way: with the
|
|
201
|
+
* suffix (`cats-core_3`), without a repository, or, for a jar without a Maven
|
|
202
|
+
* descriptor, only as the staged file name `<group>.<artifact>-<version>.jar`,
|
|
203
|
+
* which a filename-based guess splits at the wrong dot
|
|
204
|
+
* (`pkg:maven/cpg2-protos/io.appthreat.cpg2-protos_3`). Each of those is merged
|
|
205
|
+
* into the component the build tool reported. A staged jar of another version
|
|
206
|
+
* of a reported library, such as a stale build, keeps its own component but is
|
|
207
|
+
* renamed to the coordinates the reported library has.
|
|
208
|
+
*
|
|
209
|
+
* Spellings of the same coordinates merge only when they were built for the
|
|
210
|
+
* same Scala binary version, so the `_2.12` and `_2.13` artifacts of one
|
|
211
|
+
* library stay two components. Components that no reported library accounts
|
|
212
|
+
* for are left as they are.
|
|
213
|
+
*
|
|
214
|
+
* @param {Object[]} components BOM components
|
|
215
|
+
* @param {Object[]} dependencies BOM dependencies
|
|
216
|
+
* @returns {{components: Object[], dependencies: Object[], purlMap: Map<string, string>}}
|
|
217
|
+
* The collapsed components, the dependencies with references to merged
|
|
218
|
+
* components pointed at the component that remains, and the purl each
|
|
219
|
+
* merged or renamed component now has, keyed by the purl it had
|
|
220
|
+
*/
|
|
221
|
+
export function mergeScalaDuplicateComponents(components, dependencies) {
|
|
222
|
+
const purlMap = new Map();
|
|
223
|
+
const candidates = (components || []).filter(
|
|
224
|
+
(component) =>
|
|
225
|
+
component?.purl?.startsWith("pkg:maven/") &&
|
|
226
|
+
isPlainJar(component) &&
|
|
227
|
+
component.group &&
|
|
228
|
+
component.version,
|
|
229
|
+
);
|
|
230
|
+
if (candidates.length < 2) {
|
|
231
|
+
return { components, dependencies, purlMap };
|
|
232
|
+
}
|
|
233
|
+
const byKey = new Map();
|
|
234
|
+
const ownersByStem = new Map();
|
|
235
|
+
for (const component of candidates) {
|
|
236
|
+
const key = scalaCoordinateOfPurl(component.purl)?.key;
|
|
237
|
+
if (key) {
|
|
238
|
+
byKey.set(key, [...(byKey.get(key) || []), component]);
|
|
239
|
+
}
|
|
240
|
+
for (const stem of stagedStems(component)) {
|
|
241
|
+
ownersByStem.set(stem, [...(ownersByStem.get(stem) || []), component]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Which component each one is a duplicate of.
|
|
245
|
+
const duplicateOf = new Map();
|
|
246
|
+
const repoints = new Map();
|
|
247
|
+
for (const component of candidates) {
|
|
248
|
+
const matches = new Set();
|
|
249
|
+
// The same coordinates, spelled with or without the Scala suffix, with or
|
|
250
|
+
// without a repository.
|
|
251
|
+
for (const other of byKey.get(scalaCoordinateOfPurl(component.purl)?.key) ||
|
|
252
|
+
[]) {
|
|
253
|
+
if (other !== component && sameBinaryVersion(component, other)) {
|
|
254
|
+
matches.add(other);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// The staged file name of another listed library, whatever group and name
|
|
258
|
+
// the guess made of it. A jar that names itself in its Maven descriptor is
|
|
259
|
+
// the artifact it says it is.
|
|
260
|
+
if (isJarSourced(component) && hasGuessedIdentity(component)) {
|
|
261
|
+
for (const stem of jarStems(component)) {
|
|
262
|
+
for (const { owners, version } of stagedOwners(stem, ownersByStem)) {
|
|
263
|
+
const sameVersion = owners.filter(
|
|
264
|
+
(owner) => owner !== component && owner.version === version,
|
|
265
|
+
);
|
|
266
|
+
if (sameVersion.length) {
|
|
267
|
+
for (const owner of sameVersion) {
|
|
268
|
+
matches.add(owner);
|
|
269
|
+
}
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
// Another version of a library a build tool reported, when the guess
|
|
273
|
+
// agrees on it. A version that extends a known one
|
|
274
|
+
// (`4.1.100.Final-linux-x86_64`) can as well be that version with a
|
|
275
|
+
// classifier, so it is left alone.
|
|
276
|
+
const reportedOwners = owners.filter(
|
|
277
|
+
(owner) => owner !== component && !isJarSourced(owner),
|
|
278
|
+
);
|
|
279
|
+
const extendsKnown = owners.some((owner) =>
|
|
280
|
+
version.startsWith(`${owner.version}-`),
|
|
281
|
+
);
|
|
282
|
+
if (
|
|
283
|
+
reportedOwners.length &&
|
|
284
|
+
version === component.version &&
|
|
285
|
+
/^\d/.test(version) &&
|
|
286
|
+
!extendsKnown
|
|
287
|
+
) {
|
|
288
|
+
const owner = best(reportedOwners);
|
|
289
|
+
const known = repoints.get(component);
|
|
290
|
+
if (!known || isBetter(owner, known)) {
|
|
291
|
+
repoints.set(component, owner);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const target = best(
|
|
298
|
+
[...matches].filter((other) => isBetter(other, component)),
|
|
299
|
+
);
|
|
300
|
+
if (target) {
|
|
301
|
+
duplicateOf.set(component, target);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// A chain of merges never carries a component past one built for another
|
|
305
|
+
// Scala binary version.
|
|
306
|
+
const root = (component) => {
|
|
307
|
+
let current = component;
|
|
308
|
+
while (duplicateOf.has(current)) {
|
|
309
|
+
const next = duplicateOf.get(current);
|
|
310
|
+
if (current !== component && !sameBinaryVersion(component, next)) {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
current = next;
|
|
314
|
+
}
|
|
315
|
+
return current;
|
|
316
|
+
};
|
|
317
|
+
const refMap = new Map();
|
|
318
|
+
const survivors = new Set(components);
|
|
319
|
+
for (const [component] of duplicateOf) {
|
|
320
|
+
const target = root(component);
|
|
321
|
+
const duplicate = { ...component };
|
|
322
|
+
const targetRef = refOf(target);
|
|
323
|
+
if (refOf(component) && targetRef && refOf(component) !== targetRef) {
|
|
324
|
+
refMap.set(refOf(component), targetRef);
|
|
325
|
+
}
|
|
326
|
+
if (component.purl !== target.purl) {
|
|
327
|
+
purlMap.set(component.purl, target.purl);
|
|
328
|
+
}
|
|
329
|
+
duplicate.purl = target.purl;
|
|
330
|
+
duplicate["bom-ref"] = target["bom-ref"];
|
|
331
|
+
trimComponents([target, duplicate]);
|
|
332
|
+
survivors.delete(component);
|
|
333
|
+
}
|
|
334
|
+
for (const [component, owner] of repoints) {
|
|
335
|
+
if (survivors.has(component)) {
|
|
336
|
+
repointToAnchor(component, root(owner), refMap, purlMap);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// A component merged into one that was renamed afterwards follows it.
|
|
340
|
+
resolveChains(refMap);
|
|
341
|
+
resolveChains(purlMap);
|
|
342
|
+
// Repointed components can now spell the same coordinates twice.
|
|
343
|
+
return {
|
|
344
|
+
components: trimComponents([...survivors]),
|
|
345
|
+
dependencies: rewriteReferences(dependencies, refMap),
|
|
346
|
+
purlMap,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function resolveChains(map) {
|
|
351
|
+
for (const [key, value] of map) {
|
|
352
|
+
let current = value;
|
|
353
|
+
const seen = new Set([key]);
|
|
354
|
+
while (map.has(current) && !seen.has(current)) {
|
|
355
|
+
seen.add(current);
|
|
356
|
+
current = map.get(current);
|
|
357
|
+
}
|
|
358
|
+
map.set(key, current);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Give a staged jar of another version the group, name and purl shape the
|
|
363
|
+
// reported library has, so it is the same library at an older or newer version
|
|
364
|
+
// and not a differently named one.
|
|
365
|
+
function repointToAnchor(component, anchor, refMap, purlMap) {
|
|
366
|
+
const anchorPurl = parsedPurl(anchor);
|
|
367
|
+
if (!anchorPurl) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// The repository the reported version came from says nothing about where
|
|
371
|
+
// this one came from.
|
|
372
|
+
const { repository_url: _repository, ...qualifiers } =
|
|
373
|
+
anchorPurl.qualifiers || {};
|
|
374
|
+
const purl = mavenPurl(
|
|
375
|
+
anchor.group,
|
|
376
|
+
anchorPurl.name,
|
|
377
|
+
component.version,
|
|
378
|
+
qualifiers,
|
|
379
|
+
);
|
|
380
|
+
if (!purl) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const previousRef = refOf(component);
|
|
384
|
+
purlMap.set(component.purl, purl);
|
|
385
|
+
component.group = anchor.group;
|
|
386
|
+
component.name = anchor.name;
|
|
387
|
+
component.purl = purl;
|
|
388
|
+
component["bom-ref"] = decodeURIComponent(purl);
|
|
389
|
+
const known = new Set(compilerVersionsOf(component));
|
|
390
|
+
component.properties = [
|
|
391
|
+
...(component.properties || []),
|
|
392
|
+
...(anchor.properties || []).filter(
|
|
393
|
+
(property) =>
|
|
394
|
+
property.name === COMPILER_VERSION_PROPERTY &&
|
|
395
|
+
!known.has(property.value),
|
|
396
|
+
),
|
|
397
|
+
];
|
|
398
|
+
if (previousRef && previousRef !== component["bom-ref"]) {
|
|
399
|
+
refMap.set(previousRef, component["bom-ref"]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Key the jar namespace map by the purls the components have after
|
|
405
|
+
* {@link mergeScalaDuplicateComponents}, so a merged or renamed jar still
|
|
406
|
+
* finds its namespaces. A purl that already has an entry keeps it, with the
|
|
407
|
+
* namespaces of the merged entry added.
|
|
408
|
+
*
|
|
409
|
+
* @param {Object} mapping Jar namespace map keyed by purl
|
|
410
|
+
* @param {Map<string, string>} purlMap New purl keyed by old purl
|
|
411
|
+
* @returns {Object} The re-keyed map
|
|
412
|
+
*/
|
|
413
|
+
export function rekeyNamespaceMapping(mapping, purlMap) {
|
|
414
|
+
if (!mapping || !purlMap?.size) {
|
|
415
|
+
return mapping;
|
|
416
|
+
}
|
|
417
|
+
const rekeyed = { ...mapping };
|
|
418
|
+
for (const [oldPurl, newPurl] of purlMap) {
|
|
419
|
+
const entry = rekeyed[oldPurl];
|
|
420
|
+
if (!entry || oldPurl === newPurl) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
delete rekeyed[oldPurl];
|
|
424
|
+
const existing = rekeyed[newPurl];
|
|
425
|
+
if (!existing) {
|
|
426
|
+
rekeyed[newPurl] = entry;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
rekeyed[newPurl] = {
|
|
430
|
+
...existing,
|
|
431
|
+
namespaces: [
|
|
432
|
+
...new Set([
|
|
433
|
+
...(existing.namespaces || []),
|
|
434
|
+
...(entry.namespaces || []),
|
|
435
|
+
]),
|
|
436
|
+
],
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
return rekeyed;
|
|
440
|
+
}
|