@cdxgen/cdxgen 13.5.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 +6 -6
- package/bin/cdxgen.js +4 -0
- package/bin/evinse.js +3 -0
- package/bin/validate.js +6 -0
- package/data/license-aliases.json +24 -2
- package/data/remediations.json +124 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -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/cli/bomAssembly.js +92 -3
- package/lib/cli/index.js +108 -1
- package/lib/cli/jsBom.js +153 -64
- package/lib/cli/jvmBom.js +237 -46
- package/lib/cli/managedBom.js +142 -16
- package/lib/cli/nativeBom.js +42 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +8 -0
- package/lib/core/fs.js +12 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +5 -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/gradleutils.js +828 -224
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +89 -2
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +796 -151
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +346 -43
- package/lib/helpers/agenticToolutils.js +413 -93
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/atomUtils.js +77 -0
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/deps.js +35 -0
- package/lib/inventory/dosai.js +139 -5
- package/lib/inventory/dotnetRuntime.js +217 -0
- package/lib/inventory/envcontext.js +307 -13
- package/lib/inventory/evidenceUtils.js +11 -0
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +31 -3
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -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/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- 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 +49 -2
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +12 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +6 -1
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +1 -1
- package/lib/validator/index.js +1 -1
- package/package.json +3 -2
- package/types/lib/cli/bomAssembly.d.ts +13 -1
- package/types/lib/cli/bomAssembly.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.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 +4 -0
- 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/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/gradleutils.d.ts +47 -13
- 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-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +20 -2
- 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/scalacliutils.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 +63 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +19 -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/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +17 -1
- 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/envcontext.d.ts +52 -6
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/gtfobins.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/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/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- 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 +10 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// Bounded reader for the .NET shared frameworks installed in a root file
|
|
2
|
+
// system.
|
|
3
|
+
//
|
|
4
|
+
// The .NET Docker images, dotnet-install.sh and the tarballs put the runtime
|
|
5
|
+
// under <dotnet root>/shared/<framework>/<version>/ without a package manager,
|
|
6
|
+
// so a BOM sees only its files. Each framework directory carries a
|
|
7
|
+
// <framework>.deps.json whose runtime target names the runtime pack it was
|
|
8
|
+
// built from, e.g. `Microsoft.NETCore.App.Runtime.linux-arm64/8.0.32`. That pack
|
|
9
|
+
// is a NuGet package, and it is the identity .NET security advisories list, so
|
|
10
|
+
// it becomes the component's purl.
|
|
11
|
+
// https://learn.microsoft.com/dotnet/core/dependency-loading/default-probing#how-are-the-properties-populated
|
|
12
|
+
|
|
13
|
+
import { closeSync, readSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
|
|
16
|
+
import { globSync } from "glob";
|
|
17
|
+
|
|
18
|
+
import { openRegularFile } from "../core/boundedFile.js";
|
|
19
|
+
import { applyPurl, tryBuildPurl } from "./purl.js";
|
|
20
|
+
|
|
21
|
+
const MAX_DEPS_JSON_BYTES = 8 * 1024 * 1024;
|
|
22
|
+
const MAX_FRAMEWORKS = 64;
|
|
23
|
+
const FRAMEWORKS = new Set([
|
|
24
|
+
"Microsoft.NETCore.App",
|
|
25
|
+
"Microsoft.AspNetCore.App",
|
|
26
|
+
"Microsoft.WindowsDesktop.App",
|
|
27
|
+
]);
|
|
28
|
+
// Where the .NET images, dotnet-install.sh, the tarballs and the distro
|
|
29
|
+
// packages put the dotnet root.
|
|
30
|
+
const DOTNET_ROOTS = [
|
|
31
|
+
"usr/share/dotnet",
|
|
32
|
+
"usr/lib/dotnet",
|
|
33
|
+
"usr/lib64/dotnet",
|
|
34
|
+
"usr/local/share/dotnet",
|
|
35
|
+
"opt/dotnet",
|
|
36
|
+
"root/.dotnet",
|
|
37
|
+
"home/*/.dotnet",
|
|
38
|
+
];
|
|
39
|
+
const RUNTIME_PACK_RE =
|
|
40
|
+
/^(Microsoft\.[A-Za-z]+(?:\.[A-Za-z]+)*\.App\.Runtime\.[A-Za-z0-9.-]+)\/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/;
|
|
41
|
+
|
|
42
|
+
function readJsonBounded(absPath, rootDir) {
|
|
43
|
+
const opened = openRegularFile(absPath, rootDir);
|
|
44
|
+
if (!opened) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
if (opened.size <= 0 || opened.size > MAX_DEPS_JSON_BYTES) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const buffer = Buffer.allocUnsafe(opened.size);
|
|
52
|
+
const read = readSync(opened.fd, buffer, 0, opened.size, 0);
|
|
53
|
+
return JSON.parse(buffer.toString("utf8", 0, read));
|
|
54
|
+
} catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
} finally {
|
|
57
|
+
try {
|
|
58
|
+
closeSync(opened.fd);
|
|
59
|
+
} catch {
|
|
60
|
+
// already closed
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Read the runtime pack a shared framework's deps.json was built from.
|
|
67
|
+
*
|
|
68
|
+
* @param {Object} depsJson parsed <framework>.deps.json
|
|
69
|
+
* @param {string} framework framework directory name
|
|
70
|
+
* @returns {{pack: string, version: string, rid: string}|undefined}
|
|
71
|
+
*/
|
|
72
|
+
export function runtimePackFromDepsJson(depsJson, framework) {
|
|
73
|
+
const targetName = depsJson?.runtimeTarget?.name;
|
|
74
|
+
if (typeof targetName !== "string") {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const target = depsJson?.targets?.[targetName];
|
|
78
|
+
if (!target || typeof target !== "object") {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
for (const key of Object.keys(target)) {
|
|
82
|
+
const match = RUNTIME_PACK_RE.exec(key);
|
|
83
|
+
if (!match?.[1].startsWith(`${framework}.Runtime.`)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
pack: match[1],
|
|
88
|
+
version: match[2],
|
|
89
|
+
rid: match[1].slice(`${framework}.Runtime.`.length),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The CPE NVD uses for a shared framework version: `.net` from 5.0 on,
|
|
97
|
+
* `.net_core` before it, and `asp.net_core` for ASP.NET Core.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} framework framework directory name
|
|
100
|
+
* @param {string} version runtime version
|
|
101
|
+
* @returns {string|undefined} CPE 2.3 string
|
|
102
|
+
*/
|
|
103
|
+
export function dotnetCpe(framework, version) {
|
|
104
|
+
const base = /^(\d+)\.\d+\.\d+/.exec(version || "");
|
|
105
|
+
if (!base) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const plain = base[0];
|
|
109
|
+
if (framework === "Microsoft.AspNetCore.App") {
|
|
110
|
+
return `cpe:2.3:a:microsoft:asp.net_core:${plain}:*:*:*:*:*:*:*`;
|
|
111
|
+
}
|
|
112
|
+
if (framework === "Microsoft.NETCore.App") {
|
|
113
|
+
const product = Number(base[1]) >= 5 ? ".net" : ".net_core";
|
|
114
|
+
return `cpe:2.3:a:microsoft:${product}:${plain}:*:*:*:*:*:*:*`;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Create the platform component for one installed shared framework.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} framework framework directory name
|
|
123
|
+
* @param {{pack: string, version: string, rid: string}} runtime runtime pack
|
|
124
|
+
* @param {string} location root-relative path of the deps.json
|
|
125
|
+
* @returns {Object|undefined} component, or undefined when no purl can be built
|
|
126
|
+
*/
|
|
127
|
+
export function createDotnetRuntimeComponent(framework, runtime, location) {
|
|
128
|
+
const component = {
|
|
129
|
+
type: "platform",
|
|
130
|
+
name: runtime.pack,
|
|
131
|
+
version: runtime.version,
|
|
132
|
+
properties: [
|
|
133
|
+
{ name: "cdx:dotnet:sharedFramework", value: framework },
|
|
134
|
+
{ name: "cdx:dotnet:runtimeIdentifier", value: runtime.rid },
|
|
135
|
+
],
|
|
136
|
+
evidence: {
|
|
137
|
+
occurrences: [{ location: `/${location.replace(/^\/+/, "")}` }],
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const cpe = dotnetCpe(framework, runtime.version);
|
|
141
|
+
if (cpe) {
|
|
142
|
+
component.cpe = cpe;
|
|
143
|
+
}
|
|
144
|
+
const purl = tryBuildPurl({
|
|
145
|
+
type: "nuget",
|
|
146
|
+
name: runtime.pack,
|
|
147
|
+
version: runtime.version,
|
|
148
|
+
});
|
|
149
|
+
if (!purl) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
return applyPurl(component, purl);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Find the .NET shared frameworks installed under a root that no package
|
|
157
|
+
* manager owns, and return one platform component per runtime pack.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} rootDir extracted root file system
|
|
160
|
+
* @param {string[]} [ownedPaths] root-relative paths a package database owns
|
|
161
|
+
* @returns {Object[]} platform components
|
|
162
|
+
*/
|
|
163
|
+
export function collectDotnetRuntimes(rootDir, ownedPaths = []) {
|
|
164
|
+
if (!rootDir) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
const owned = new Set(
|
|
168
|
+
(ownedPaths || []).map((p) => p.replace(/^\/+/, "").replaceAll("\\", "/")),
|
|
169
|
+
);
|
|
170
|
+
let depsFiles = [];
|
|
171
|
+
try {
|
|
172
|
+
depsFiles = globSync(
|
|
173
|
+
DOTNET_ROOTS.map((root) => `${root}/shared/*/*/*.deps.json`),
|
|
174
|
+
{
|
|
175
|
+
cwd: rootDir,
|
|
176
|
+
absolute: false,
|
|
177
|
+
nodir: true,
|
|
178
|
+
dot: true,
|
|
179
|
+
follow: false,
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
.map((file) => file.replaceAll("\\", "/"))
|
|
183
|
+
.sort();
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const components = [];
|
|
188
|
+
const seen = new Set();
|
|
189
|
+
for (const file of depsFiles.slice(0, MAX_FRAMEWORKS)) {
|
|
190
|
+
if (owned.has(file)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const parts = file.split("/");
|
|
194
|
+
const framework = parts[parts.length - 3];
|
|
195
|
+
if (
|
|
196
|
+
!FRAMEWORKS.has(framework) ||
|
|
197
|
+
parts.at(-1) !== `${framework}.deps.json`
|
|
198
|
+
) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const runtime = runtimePackFromDepsJson(
|
|
202
|
+
readJsonBounded(join(rootDir, file), rootDir),
|
|
203
|
+
framework,
|
|
204
|
+
);
|
|
205
|
+
// The directory a runtime lives in is named after its version.
|
|
206
|
+
if (!runtime || runtime.version !== parts[parts.length - 2]) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const component = createDotnetRuntimeComponent(framework, runtime, file);
|
|
210
|
+
if (!component || seen.has(component["bom-ref"])) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
seen.add(component["bom-ref"]);
|
|
214
|
+
components.push(component);
|
|
215
|
+
}
|
|
216
|
+
return components;
|
|
217
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { arch, homedir } from "node:os";
|
|
3
|
-
import { basename, delimiter, dirname, join } from "node:path";
|
|
3
|
+
import { basename, delimiter, dirname, isAbsolute, join } from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
|
|
6
6
|
import { compareLoose } from "semver";
|
|
@@ -19,10 +19,12 @@ import {
|
|
|
19
19
|
recordLedgerEvent,
|
|
20
20
|
} from "../core/buildLedger.js";
|
|
21
21
|
import {
|
|
22
|
+
BUNDLE_CMD,
|
|
22
23
|
CARGO_CMD,
|
|
23
24
|
COMPOSER_CMD,
|
|
24
25
|
DOTNET_CMD,
|
|
25
26
|
GCC_CMD,
|
|
27
|
+
GEM_CMD,
|
|
26
28
|
GO_CMD,
|
|
27
29
|
getJavaCommand,
|
|
28
30
|
getPythonCommand,
|
|
@@ -44,12 +46,19 @@ import {
|
|
|
44
46
|
import { diagnosticStream } from "../core/logger.js";
|
|
45
47
|
import { dirNameStr, isMac, isWin } from "../core/paths.js";
|
|
46
48
|
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
49
|
+
import { brotliDecoderProperties, readBrotliDecoder } from "./brotliDecoder.js";
|
|
47
50
|
import { isValidSdkmanVersion } from "./jvmToolEnv.js";
|
|
51
|
+
import { createNodeRuntimeComponent } from "./nodeBuildInfo.js";
|
|
48
52
|
import {
|
|
49
53
|
createPhpRuntimeComponent,
|
|
54
|
+
packageStoreOf,
|
|
50
55
|
parsePhpReportedVersion,
|
|
51
56
|
} from "./phpBuildInfo.js";
|
|
52
57
|
import { applyPurl, tryBuildPurl } from "./purl.js";
|
|
58
|
+
import {
|
|
59
|
+
createRubyRuntimeComponent,
|
|
60
|
+
parseRubyDescription,
|
|
61
|
+
} from "./rubyBuildInfo.js";
|
|
53
62
|
import {
|
|
54
63
|
classifySpawnRestriction,
|
|
55
64
|
describeSpawnRestriction,
|
|
@@ -137,6 +146,11 @@ export const SDKMAN_JAVA_TOOL_ALIASES = {
|
|
|
137
146
|
java24: readEnvironmentVariable("JAVA24_TOOL") || "24.0.2-tem",
|
|
138
147
|
java25: readEnvironmentVariable("JAVA25_TOOL") || "25.0.4-tem",
|
|
139
148
|
java26: readEnvironmentVariable("JAVA26_TOOL") || "26.0.2-tem",
|
|
149
|
+
// The broker serves no Java 27 build yet (every vendor id 404s), so java27
|
|
150
|
+
// has an alias only when JAVA27_TOOL names one that is downloadable.
|
|
151
|
+
...(readEnvironmentVariable("JAVA27_TOOL")
|
|
152
|
+
? { java27: readEnvironmentVariable("JAVA27_TOOL") }
|
|
153
|
+
: {}),
|
|
140
154
|
};
|
|
141
155
|
|
|
142
156
|
/**
|
|
@@ -486,6 +500,16 @@ export function collectNodeInfo(dir) {
|
|
|
486
500
|
version: runtimeInfo.version,
|
|
487
501
|
components: runtimeInfo.components,
|
|
488
502
|
};
|
|
503
|
+
if (runtimeInfo.runtime === "Node.js" && runtimeInfo.version) {
|
|
504
|
+
// The same identity a scanned image's Node.js gets, without a bom-ref, as
|
|
505
|
+
// for the other toolchains.
|
|
506
|
+
const runtime = formulationToolchain(
|
|
507
|
+
createNodeRuntimeComponent(runtimeInfo.version),
|
|
508
|
+
);
|
|
509
|
+
nodeInfo.purl = runtime.purl;
|
|
510
|
+
nodeInfo.cpe = runtime.cpe;
|
|
511
|
+
annotateNodeBrotli(nodeInfo.components);
|
|
512
|
+
}
|
|
489
513
|
const moduleDesc = getCommandOutput(NPM_CMD, dir, ["--version"]);
|
|
490
514
|
if (moduleDesc) {
|
|
491
515
|
nodeInfo.description = `npm: ${moduleDesc}`;
|
|
@@ -496,6 +520,36 @@ export function collectNodeInfo(dir) {
|
|
|
496
520
|
return nodeInfo;
|
|
497
521
|
}
|
|
498
522
|
|
|
523
|
+
/**
|
|
524
|
+
* Record whether the Brotli decoder the running Node.js uses carries the
|
|
525
|
+
* CVE-2026-111497 fix. process.versions.brotli cannot say: Node.js backports
|
|
526
|
+
* the fix without changing it. The decoder is read where it lives, in the node
|
|
527
|
+
* binary or in the shared libbrotlidec a `--shared-brotli` build loads.
|
|
528
|
+
*
|
|
529
|
+
* @param {Object[]|undefined} components bundled components of the runtime
|
|
530
|
+
*/
|
|
531
|
+
function annotateNodeBrotli(components) {
|
|
532
|
+
const brotli = (components || []).find(
|
|
533
|
+
(component) => component.name === "brotli",
|
|
534
|
+
);
|
|
535
|
+
if (!brotli) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
let decoderFile = globalThis.process?.execPath;
|
|
539
|
+
if (nodeComponentLinkage("brotli") === "shared") {
|
|
540
|
+
decoderFile = (
|
|
541
|
+
globalThis.process?.report?.getReport?.()?.sharedObjects || []
|
|
542
|
+
).find((path) => basename(path).startsWith("libbrotlidec"));
|
|
543
|
+
}
|
|
544
|
+
const decoder = decoderFile ? readBrotliDecoder(decoderFile) : undefined;
|
|
545
|
+
if (decoder) {
|
|
546
|
+
brotli.properties = [
|
|
547
|
+
...(brotli.properties || []),
|
|
548
|
+
...brotliDecoderProperties(decoder),
|
|
549
|
+
];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
499
553
|
/**
|
|
500
554
|
* Collect gcc version
|
|
501
555
|
*
|
|
@@ -585,23 +639,215 @@ export function collectSwiftInfo(dir) {
|
|
|
585
639
|
}
|
|
586
640
|
|
|
587
641
|
/**
|
|
588
|
-
*
|
|
642
|
+
* The real path of the executable a command name runs, looked up on PATH as
|
|
643
|
+
* the shell would, or undefined when it cannot be found.
|
|
644
|
+
*
|
|
645
|
+
* @param {string} command command name or path
|
|
646
|
+
* @returns {string|undefined}
|
|
647
|
+
*/
|
|
648
|
+
function resolveExecutable(command) {
|
|
649
|
+
const name = `${command || ""}`.trim().split(" ")[0];
|
|
650
|
+
if (!name) {
|
|
651
|
+
return undefined;
|
|
652
|
+
}
|
|
653
|
+
const extensions = isWin
|
|
654
|
+
? ["", ...`${process.env.PATHEXT || ".EXE;.CMD;.BAT"}`.split(";")]
|
|
655
|
+
: [""];
|
|
656
|
+
const directories = isAbsolute(name)
|
|
657
|
+
? [dirname(name)]
|
|
658
|
+
: `${process.env.PATH || ""}`.split(delimiter).filter(Boolean);
|
|
659
|
+
const base = isAbsolute(name) ? basename(name) : name;
|
|
660
|
+
for (const directory of directories) {
|
|
661
|
+
for (const extension of extensions) {
|
|
662
|
+
const candidate = join(directory, `${base}${extension}`);
|
|
663
|
+
try {
|
|
664
|
+
if (statSync(candidate).isFile()) {
|
|
665
|
+
return realpathSync(candidate);
|
|
666
|
+
}
|
|
667
|
+
} catch {
|
|
668
|
+
// not here
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return undefined;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Whether a Ruby executable is one the operating system or a package manager
|
|
677
|
+
* that patches it owns. Distros backport fixes under the old upstream version,
|
|
678
|
+
* so its version must not be matched against upstream advisories by CPE.
|
|
679
|
+
*
|
|
680
|
+
* @param {string|undefined} realPath real path of the executable
|
|
681
|
+
* @returns {boolean}
|
|
682
|
+
*/
|
|
683
|
+
export function isPackagedRubyPath(realPath) {
|
|
684
|
+
if (!realPath || isWin) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
// Homebrew builds the upstream release, so it keeps the CPE, as it does in
|
|
688
|
+
// an OBOM; Nix and Guix patch theirs.
|
|
689
|
+
const store = packageStoreOf(realPath);
|
|
690
|
+
if (store) {
|
|
691
|
+
return store !== "homebrew";
|
|
692
|
+
}
|
|
693
|
+
return ["/usr/bin/", "/bin/", "/usr/lib/", "/usr/libexec/", "/System/"].some(
|
|
694
|
+
(prefix) => realPath.startsWith(prefix),
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// The shims of rbenv, asdf and mise run whichever Ruby is selected, which can
|
|
699
|
+
// be the distro's (`rbenv global system`).
|
|
700
|
+
function isVersionManagerShim(realPath) {
|
|
701
|
+
return `${realPath}`.split(/[\\/]/).includes("shims");
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Whether the Ruby `ruby --version` described may carry its release's CPE.
|
|
705
|
+
// A shim is followed to the interpreter it runs; one that cannot be followed
|
|
706
|
+
// keeps no CPE, since it may be the distro's.
|
|
707
|
+
function rubyKeepsCpe(dir) {
|
|
708
|
+
let executable = resolveExecutable(RUBY_CMD);
|
|
709
|
+
if (executable && isVersionManagerShim(executable)) {
|
|
710
|
+
const target = isWin
|
|
711
|
+
? ""
|
|
712
|
+
: firstOutputLine(
|
|
713
|
+
getCommandOutput(RUBY_CMD, dir, ["-e", "print RbConfig.ruby"]),
|
|
714
|
+
);
|
|
715
|
+
if (!isAbsolute(target)) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
try {
|
|
719
|
+
executable = realpathSync(target);
|
|
720
|
+
} catch {
|
|
721
|
+
executable = target;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return !isPackagedRubyPath(executable);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Collect the Ruby on the PATH.
|
|
729
|
+
*
|
|
730
|
+
* `ruby --version` prints the interpreter's description, such as
|
|
731
|
+
* `ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x86_64-linux]`, which is kept
|
|
732
|
+
* as the component description. The identity is `pkg:generic/ruby@3.3.6`
|
|
733
|
+
* (or jruby, truffleruby), with the CPE of the release unless a package
|
|
734
|
+
* manager owns the executable: a distro backports fixes under the old upstream
|
|
735
|
+
* version.
|
|
589
736
|
*
|
|
590
737
|
* @param {string} dir Working directory
|
|
591
738
|
* @returns Object containing Ruby details
|
|
592
739
|
*/
|
|
593
740
|
export function collectRubyInfo(dir) {
|
|
594
741
|
const versionDesc = getCommandOutput(RUBY_CMD, dir, ["--version"]);
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
version
|
|
600
|
-
|
|
601
|
-
|
|
742
|
+
const banner = firstOutputLine(versionDesc);
|
|
743
|
+
const parsed = parseRubyDescription(banner);
|
|
744
|
+
if (parsed) {
|
|
745
|
+
const info = formulationToolchain(
|
|
746
|
+
createRubyRuntimeComponent(parsed.version, {
|
|
747
|
+
engine: parsed.engine,
|
|
748
|
+
withCpe: rubyKeepsCpe(dir),
|
|
749
|
+
}),
|
|
750
|
+
);
|
|
751
|
+
info.description = banner;
|
|
752
|
+
const properties = [];
|
|
753
|
+
if (parsed.patchlevel !== undefined) {
|
|
754
|
+
properties.push({
|
|
755
|
+
name: "cdx:ruby:patchlevel",
|
|
756
|
+
value: parsed.patchlevel,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (parsed.rubyVersion) {
|
|
760
|
+
properties.push({
|
|
761
|
+
name: "cdx:ruby:languageVersion",
|
|
762
|
+
value: parsed.rubyVersion,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
if (parsed.platform) {
|
|
766
|
+
properties.push({ name: "cdx:ruby:platform", value: parsed.platform });
|
|
767
|
+
}
|
|
768
|
+
if (properties.length) {
|
|
769
|
+
info.properties = properties;
|
|
770
|
+
}
|
|
771
|
+
recordToolProbeResolved("ruby", "ruby", banner);
|
|
602
772
|
return info;
|
|
603
773
|
}
|
|
604
|
-
recordToolProbeFailure(
|
|
774
|
+
recordToolProbeFailure(
|
|
775
|
+
"ruby",
|
|
776
|
+
"ruby",
|
|
777
|
+
RUBY_CMD,
|
|
778
|
+
versionDesc
|
|
779
|
+
? "The ruby version probe did not print a Ruby version banner."
|
|
780
|
+
: undefined,
|
|
781
|
+
);
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Whether the Ruby collectRubyInfo found is the MRI release a project needs.
|
|
787
|
+
*
|
|
788
|
+
* @param {Object|undefined} info result of collectRubyInfo
|
|
789
|
+
* @param {string} version wanted MRI version
|
|
790
|
+
* @returns {boolean}
|
|
791
|
+
*/
|
|
792
|
+
export function isRubyVersionActive(info, version) {
|
|
793
|
+
return Boolean(info && info.name === "ruby" && info.version === version);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Collect the RubyGems version. `gem --version` prints the bare version.
|
|
798
|
+
*
|
|
799
|
+
* @param {string} dir Working directory
|
|
800
|
+
* @returns {Object|undefined} the RubyGems component
|
|
801
|
+
*/
|
|
802
|
+
export function collectRubyGemsInfo(dir) {
|
|
803
|
+
const token = firstOutputLine(getCommandOutput(GEM_CMD, dir, ["--version"]));
|
|
804
|
+
if (/^\d+\.\d+\.\d+$/.test(token)) {
|
|
805
|
+
recordToolProbeResolved("gem", "ruby", token);
|
|
806
|
+
return formulationToolchain(
|
|
807
|
+
applyPurl(
|
|
808
|
+
{
|
|
809
|
+
type: "platform",
|
|
810
|
+
name: "rubygems",
|
|
811
|
+
version: token,
|
|
812
|
+
cpe: `cpe:2.3:a:rubygems:rubygems:${token}:*:*:*:*:*:*:*`,
|
|
813
|
+
},
|
|
814
|
+
tryBuildPurl({ type: "generic", name: "rubygems", version: token }),
|
|
815
|
+
),
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
recordToolProbeFailure("gem", "ruby", GEM_CMD);
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Collect the Bundler version. `bundle --version` prints `Bundler version
|
|
824
|
+
* 2.5.22`, or the bare version from Bundler 2.6 on.
|
|
825
|
+
*
|
|
826
|
+
* @param {string} dir Working directory
|
|
827
|
+
* @returns {Object|undefined} the Bundler component
|
|
828
|
+
*/
|
|
829
|
+
export function collectBundlerInfo(dir) {
|
|
830
|
+
const line = firstOutputLine(
|
|
831
|
+
getCommandOutput(BUNDLE_CMD, dir, ["--version"]),
|
|
832
|
+
);
|
|
833
|
+
const token = line.startsWith("Bundler version ")
|
|
834
|
+
? line.slice("Bundler version ".length).trim()
|
|
835
|
+
: line;
|
|
836
|
+
if (/^\d+\.\d+\.\d+$/.test(token)) {
|
|
837
|
+
recordToolProbeResolved("bundler", "ruby", token);
|
|
838
|
+
return formulationToolchain(
|
|
839
|
+
applyPurl(
|
|
840
|
+
{
|
|
841
|
+
type: "platform",
|
|
842
|
+
name: "bundler",
|
|
843
|
+
version: token,
|
|
844
|
+
cpe: `cpe:2.3:a:bundler:bundler:${token}:*:*:*:*:*:*:*`,
|
|
845
|
+
},
|
|
846
|
+
tryBuildPurl({ type: "gem", name: "bundler", version: token }),
|
|
847
|
+
),
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
recordToolProbeFailure("bundler", "ruby", BUNDLE_CMD);
|
|
605
851
|
return undefined;
|
|
606
852
|
}
|
|
607
853
|
|
|
@@ -787,6 +1033,14 @@ export function collectEnvInfo(dir, options = undefined) {
|
|
|
787
1033
|
cmp = collectRubyInfo(dir);
|
|
788
1034
|
if (cmp) {
|
|
789
1035
|
infoComponents.push(cmp);
|
|
1036
|
+
cmp = collectRubyGemsInfo(dir);
|
|
1037
|
+
if (cmp) {
|
|
1038
|
+
infoComponents.push(cmp);
|
|
1039
|
+
}
|
|
1040
|
+
cmp = collectBundlerInfo(dir);
|
|
1041
|
+
if (cmp) {
|
|
1042
|
+
infoComponents.push(cmp);
|
|
1043
|
+
}
|
|
790
1044
|
}
|
|
791
1045
|
cmp = collectPhpInfo(dir);
|
|
792
1046
|
if (cmp) {
|
|
@@ -1218,7 +1472,7 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
1218
1472
|
return { fullToolBinDir: undefined, status: false };
|
|
1219
1473
|
}
|
|
1220
1474
|
const existingRuby = collectRubyInfo(filePath);
|
|
1221
|
-
if (existingRuby
|
|
1475
|
+
if (isRubyVersionActive(existingRuby, rubyVersion)) {
|
|
1222
1476
|
return { fullToolBinDir: undefined, status: true };
|
|
1223
1477
|
}
|
|
1224
1478
|
const fullToolBinDir = rubyVersionDir(rubyVersion);
|
|
@@ -1984,6 +2238,36 @@ export function gitRevertsAndHotfixes(dir, maxCount = 20) {
|
|
|
1984
2238
|
return entries;
|
|
1985
2239
|
}
|
|
1986
2240
|
|
|
2241
|
+
// process.versions keys whose configure flag is named differently.
|
|
2242
|
+
const NODE_SHARED_FLAG_NAMES = {
|
|
2243
|
+
ares: "cares",
|
|
2244
|
+
libffi: "ffi",
|
|
2245
|
+
llhttp: "http_parser",
|
|
2246
|
+
uv: "libuv",
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
/**
|
|
2250
|
+
* Whether the running Node.js links a bundled dependency as a shared library,
|
|
2251
|
+
* from the `node_shared_<name>` configure variables it reports.
|
|
2252
|
+
*
|
|
2253
|
+
* @param {string} name key of process.versions
|
|
2254
|
+
* @param {Object} [variables] configure variables, process.config.variables
|
|
2255
|
+
* by default
|
|
2256
|
+
* @returns {string|undefined} `shared`, `bundled`, or undefined when the build
|
|
2257
|
+
* does not say
|
|
2258
|
+
*/
|
|
2259
|
+
export function nodeComponentLinkage(
|
|
2260
|
+
name,
|
|
2261
|
+
variables = globalThis.process?.config?.variables,
|
|
2262
|
+
) {
|
|
2263
|
+
const flag = `node_shared_${NODE_SHARED_FLAG_NAMES[name] || name}`;
|
|
2264
|
+
if (!variables || !(flag in variables)) {
|
|
2265
|
+
return undefined;
|
|
2266
|
+
}
|
|
2267
|
+
const value = variables[flag];
|
|
2268
|
+
return value === true || value === "true" ? "shared" : "bundled";
|
|
2269
|
+
}
|
|
2270
|
+
|
|
1987
2271
|
/**
|
|
1988
2272
|
* Get information about the runtime.
|
|
1989
2273
|
*
|
|
@@ -2016,12 +2300,22 @@ export function getRuntimeInformation() {
|
|
|
2016
2300
|
name,
|
|
2017
2301
|
version: version || null,
|
|
2018
2302
|
});
|
|
2303
|
+
// A distro or Homebrew build links some of these as shared libraries
|
|
2304
|
+
// (`configure --shared-brotli`); the version is then the shared
|
|
2305
|
+
// library's, and updating Node.js alone does not update it.
|
|
2306
|
+
const linkage = nodeComponentLinkage(name);
|
|
2019
2307
|
const apkg = {
|
|
2020
2308
|
name,
|
|
2021
2309
|
version,
|
|
2022
|
-
description:
|
|
2310
|
+
description:
|
|
2311
|
+
linkage === "shared"
|
|
2312
|
+
? `Linked by Node.js ${runtimeInfo.version} as a shared library`
|
|
2313
|
+
: `Bundled with Node.js ${runtimeInfo.version}`,
|
|
2023
2314
|
type: "library",
|
|
2024
2315
|
scope: "excluded",
|
|
2316
|
+
...(linkage
|
|
2317
|
+
? { properties: [{ name: "cdx:node:linkage", value: linkage }] }
|
|
2318
|
+
: {}),
|
|
2025
2319
|
};
|
|
2026
2320
|
applyPurl(apkg, bundledPurl);
|
|
2027
2321
|
if (nodeSourceUrl) {
|
|
@@ -6,6 +6,7 @@ import { build } from "@cdxgen/cdx-purl";
|
|
|
6
6
|
import { safeExistsSync } from "../core/fs.js";
|
|
7
7
|
import { thoughtLog } from "../core/logger.js";
|
|
8
8
|
import { sanitizeBomUrl } from "../core/propertySanitizer.js";
|
|
9
|
+
import { vscodeExtensionPlatformFromPath } from "../parsers/vscodeExtensionId.js";
|
|
9
10
|
import { isAgentSkillSpecValid } from "./agentSkillSpec.js";
|
|
10
11
|
import {
|
|
11
12
|
addDosaiReachabilityProperties,
|
|
@@ -619,6 +620,13 @@ export function convertOSQueryResults(
|
|
|
619
620
|
// The profile an add-on was found in is not part of its purl, so the
|
|
620
621
|
// add-on ID is what tells apart two add-ons that share a name.
|
|
621
622
|
qualifiers = { tag_id: firefoxAddonTagId(res.identifier) };
|
|
623
|
+
} else if (queryObj.purlType === "vscode-extension") {
|
|
624
|
+
// A platform-specific build is a different artefact from the one for
|
|
625
|
+
// another platform, and the extension scan names it the same way.
|
|
626
|
+
const targetPlatform = vscodeExtensionPlatformFromPath(res.path);
|
|
627
|
+
if (targetPlatform) {
|
|
628
|
+
qualifiers = { platform: targetPlatform };
|
|
629
|
+
}
|
|
622
630
|
}
|
|
623
631
|
if (name) {
|
|
624
632
|
name = sanitizeOsQueryIdentity(name);
|
|
@@ -788,11 +796,14 @@ export function convertOSQueryResults(
|
|
|
788
796
|
// profile. Where it was found is not part of what it is, so the path is an
|
|
789
797
|
// occurrence rather than a subpath: a home directory stays out of the purl, and
|
|
790
798
|
// one package installed in two places is one component with two occurrences.
|
|
799
|
+
// Chocolatey rows carry the absolute path of the package's .nuspec, which a
|
|
800
|
+
// purl subpath cannot hold, so it is kept as an occurrence like these.
|
|
791
801
|
const LOCATED_PACKAGE_PURL_TYPES = new Set([
|
|
792
802
|
"npm",
|
|
793
803
|
"pypi",
|
|
794
804
|
"chrome-extension",
|
|
795
805
|
"vscode-extension",
|
|
806
|
+
"chocolatey",
|
|
796
807
|
]);
|
|
797
808
|
const LOCATED_PACKAGE_CATEGORIES = new Set(["firefox_addons"]);
|
|
798
809
|
|
|
@@ -221,7 +221,7 @@ export function openHostCircuit(
|
|
|
221
221
|
);
|
|
222
222
|
} else if (breaker === "maven-central") {
|
|
223
223
|
console.warn(
|
|
224
|
-
`${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set
|
|
224
|
+
`${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set MAVEN_CENTRAL_MIRROR=true to use Google's Maven Central mirror, or name a repository manager in MAVEN_CENTRAL_URL, to keep license enrichment working.`,
|
|
225
225
|
);
|
|
226
226
|
} else {
|
|
227
227
|
console.warn(
|