@cdxgen/cdxgen 13.1.0 → 13.2.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 +8 -7
- package/bin/cdxgen.js +16 -3
- package/bin/evinse.js +2 -0
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +23 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +12 -0
- package/lib/cli/index.js +192 -1
- package/lib/cli/jsBom.js +59 -2
- package/lib/cli/jvmBom.js +22 -0
- package/lib/cli/managedBom.js +152 -0
- package/lib/cli/nativeBom.js +798 -1
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +39 -1
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +13 -5
- package/lib/ecosystems/cppEvidence.js +40 -2
- package/lib/ecosystems/ecosystems.js +241 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +143 -2
- package/lib/ecosystems/kosi.js +1256 -0
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +69 -39
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +172 -0
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +1 -1
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +932 -17
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +111 -1
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +7 -2
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/dosai.js +77 -0
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +74 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +54 -88
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/source.js +10 -4
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +169 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/package.json +25 -25
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- 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 +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +20 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +73 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +29 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +23 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/lib/core/activity.js
CHANGED
|
@@ -3,7 +3,7 @@ import process from "node:process";
|
|
|
3
3
|
|
|
4
4
|
import { createHttpClient, getDefaultHttpTimeoutMs } from "./httpClient.js";
|
|
5
5
|
import { traceLog } from "./logger.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getUserAgent } from "./userAgent.js";
|
|
7
7
|
|
|
8
8
|
/** True when CDXGEN_DEBUG_MODE or SCAN_DEBUG_MODE requests debug/verbose output. */
|
|
9
9
|
export const DEBUG_MODE =
|
|
@@ -85,6 +85,30 @@ const LOCKFILE_ACTIVITY_HINTS = new Map([
|
|
|
85
85
|
label: "Bundler lockfile",
|
|
86
86
|
},
|
|
87
87
|
],
|
|
88
|
+
[
|
|
89
|
+
"cpanfile.snapshot",
|
|
90
|
+
{
|
|
91
|
+
classification: "lockfile",
|
|
92
|
+
ecosystem: "cpan",
|
|
93
|
+
label: "Carton snapshot",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
"manifest.toml",
|
|
98
|
+
{
|
|
99
|
+
classification: "lockfile",
|
|
100
|
+
ecosystem: "gleam",
|
|
101
|
+
label: "Gleam or Julia lockfile",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
"nimble.lock",
|
|
106
|
+
{
|
|
107
|
+
classification: "lockfile",
|
|
108
|
+
ecosystem: "nim",
|
|
109
|
+
label: "Nimble lockfile",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
88
112
|
[
|
|
89
113
|
"package-lock.json",
|
|
90
114
|
{ classification: "lockfile", ecosystem: "npm", label: "npm lockfile" },
|
|
@@ -125,6 +149,62 @@ const LOCKFILE_ACTIVITY_HINTS = new Map([
|
|
|
125
149
|
label: "PEP 751 lockfile",
|
|
126
150
|
},
|
|
127
151
|
],
|
|
152
|
+
[
|
|
153
|
+
"renv.lock",
|
|
154
|
+
{
|
|
155
|
+
classification: "lockfile",
|
|
156
|
+
ecosystem: "cran",
|
|
157
|
+
label: "renv lockfile",
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
"rebar.lock",
|
|
162
|
+
{
|
|
163
|
+
classification: "lockfile",
|
|
164
|
+
ecosystem: "hex",
|
|
165
|
+
label: "rebar3 lockfile",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
"shard.lock",
|
|
170
|
+
{
|
|
171
|
+
classification: "lockfile",
|
|
172
|
+
ecosystem: "crystal",
|
|
173
|
+
label: "Crystal shards lockfile",
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
"spack.lock",
|
|
178
|
+
{
|
|
179
|
+
classification: "lockfile",
|
|
180
|
+
ecosystem: "spack",
|
|
181
|
+
label: "Spack lockfile",
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
"stack.lock",
|
|
186
|
+
{
|
|
187
|
+
classification: "lockfile",
|
|
188
|
+
ecosystem: "hackage",
|
|
189
|
+
label: "Stack lockfile",
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
[
|
|
193
|
+
"stack.yaml.lock",
|
|
194
|
+
{
|
|
195
|
+
classification: "lockfile",
|
|
196
|
+
ecosystem: "hackage",
|
|
197
|
+
label: "Stack lockfile",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
".terraform.lock.hcl",
|
|
202
|
+
{
|
|
203
|
+
classification: "lockfile",
|
|
204
|
+
ecosystem: "terraform",
|
|
205
|
+
label: "Terraform provider lockfile",
|
|
206
|
+
},
|
|
207
|
+
],
|
|
128
208
|
[
|
|
129
209
|
"uv.lock",
|
|
130
210
|
{ classification: "lockfile", ecosystem: "python", label: "uv lockfile" },
|
|
@@ -167,6 +247,18 @@ const MANIFEST_ACTIVITY_HINTS = new Map([
|
|
|
167
247
|
"pom.xml",
|
|
168
248
|
{ classification: "manifest", ecosystem: "maven", label: "Maven manifest" },
|
|
169
249
|
],
|
|
250
|
+
[
|
|
251
|
+
"cpanfile",
|
|
252
|
+
{ classification: "manifest", ecosystem: "cpan", label: "CPAN manifest" },
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
"project.toml",
|
|
256
|
+
{
|
|
257
|
+
classification: "manifest",
|
|
258
|
+
ecosystem: "julia",
|
|
259
|
+
label: "Julia project manifest",
|
|
260
|
+
},
|
|
261
|
+
],
|
|
170
262
|
[
|
|
171
263
|
"pyproject.toml",
|
|
172
264
|
{
|
|
@@ -942,10 +1034,35 @@ function inferNetworkIntent(requestUrl) {
|
|
|
942
1034
|
return "metadata-fetch";
|
|
943
1035
|
}
|
|
944
1036
|
|
|
1037
|
+
/**
|
|
1038
|
+
* Set the request's user-agent to the value resolved for its destination host,
|
|
1039
|
+
* so that a `CDXGEN_USER_AGENT_<REGISTRY>` override reaches the registry it
|
|
1040
|
+
* names. A user-agent a caller set explicitly is left alone; only the client's
|
|
1041
|
+
* own default, which carries no host context, is replaced.
|
|
1042
|
+
*
|
|
1043
|
+
* @param {Object} options Request options being prepared by the HTTP client.
|
|
1044
|
+
* @returns {void}
|
|
1045
|
+
*/
|
|
1046
|
+
function applyUserAgent(options) {
|
|
1047
|
+
const headers = options.headers || (options.headers = {});
|
|
1048
|
+
const existingKey = Object.keys(headers).find(
|
|
1049
|
+
(name) => name.toLowerCase() === "user-agent",
|
|
1050
|
+
);
|
|
1051
|
+
if (existingKey && headers[existingKey] !== getUserAgent()) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (existingKey) {
|
|
1055
|
+
delete headers[existingKey];
|
|
1056
|
+
}
|
|
1057
|
+
headers["user-agent"] = getUserAgent(options.url?.hostname);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
945
1060
|
/** The shared got-compatible HTTP client for all outbound requests, enforcing dry-run, host-allow-list, and secure-mode policies via beforeRequest/afterResponse/beforeError hooks. */
|
|
946
1061
|
export const cdxgenAgent = createHttpClient({
|
|
947
1062
|
headers: {
|
|
948
|
-
|
|
1063
|
+
// Per-host overrides are applied in beforeRequest; this is the fallback for
|
|
1064
|
+
// any code path that reads the client's configured headers directly.
|
|
1065
|
+
"user-agent": getUserAgent(),
|
|
949
1066
|
},
|
|
950
1067
|
// Bound every request so an unresponsive host cannot hang a scan. Overridable
|
|
951
1068
|
// via CDXGEN_HTTP_TIMEOUT_MS; individual calls may pass a smaller `timeout`.
|
|
@@ -959,6 +1076,7 @@ export const cdxgenAgent = createHttpClient({
|
|
|
959
1076
|
(options) => {
|
|
960
1077
|
const networkIntent =
|
|
961
1078
|
options.context?.activityIntent || inferNetworkIntent(options.url);
|
|
1079
|
+
applyUserAgent(options);
|
|
962
1080
|
const allowedHostsEnv = readEnvironmentVariable("CDXGEN_ALLOWED_HOSTS");
|
|
963
1081
|
const hostAllowed = isAllowedHttpHost(
|
|
964
1082
|
options.url.hostname,
|
package/lib/core/env.js
CHANGED
|
@@ -256,6 +256,19 @@ if (readEnvironmentVariable("CARGO_CMD")) {
|
|
|
256
256
|
CARGO_CMD = readEnvironmentVariable("CARGO_CMD");
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Target triple the cargo resolve graph is filtered to, so that a
|
|
261
|
+
* cross-compiled project can be described for the triple it ships on rather
|
|
262
|
+
* than for the union of every triple its lock file covers.
|
|
263
|
+
*
|
|
264
|
+
* `CDXGEN_CARGO_TARGET` is cdxgen's own name for it; `CARGO_BUILD_TARGET` is
|
|
265
|
+
* cargo's standard variable, which a cross-compiling build already sets.
|
|
266
|
+
*/
|
|
267
|
+
export const CARGO_TARGET =
|
|
268
|
+
readEnvironmentVariable("CDXGEN_CARGO_TARGET") ||
|
|
269
|
+
readEnvironmentVariable("CARGO_BUILD_TARGET") ||
|
|
270
|
+
undefined;
|
|
271
|
+
|
|
259
272
|
/** Resolved Clojure CLI executable command (CLJ_CMD env var, or "clj"). */
|
|
260
273
|
export let CLJ_CMD = "clj";
|
|
261
274
|
if (readEnvironmentVariable("CLJ_CMD")) {
|
|
@@ -307,6 +320,7 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
307
320
|
"java24",
|
|
308
321
|
"java25",
|
|
309
322
|
"java26",
|
|
323
|
+
"java27",
|
|
310
324
|
"groovy",
|
|
311
325
|
"kotlin",
|
|
312
326
|
"kt",
|
|
@@ -339,6 +353,9 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
339
353
|
"nodejs20",
|
|
340
354
|
"nodejs22",
|
|
341
355
|
"nodejs23",
|
|
356
|
+
"nodejs24",
|
|
357
|
+
"nodejs25",
|
|
358
|
+
"nodejs26",
|
|
342
359
|
"node",
|
|
343
360
|
"node8",
|
|
344
361
|
"node10",
|
|
@@ -349,11 +366,16 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
349
366
|
"node20",
|
|
350
367
|
"node22",
|
|
351
368
|
"node23",
|
|
369
|
+
"node24",
|
|
370
|
+
"node25",
|
|
371
|
+
"node26",
|
|
352
372
|
"js",
|
|
353
373
|
"javascript",
|
|
354
374
|
"typescript",
|
|
355
375
|
"ts",
|
|
356
376
|
"tsx",
|
|
377
|
+
"svelte",
|
|
378
|
+
"sveltekit",
|
|
357
379
|
"yarn",
|
|
358
380
|
"rush",
|
|
359
381
|
"bun",
|
|
@@ -361,6 +383,7 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
361
383
|
],
|
|
362
384
|
mcp: ["mcp"],
|
|
363
385
|
"ai-skill": ["ai-skill", "skill", "skills"],
|
|
386
|
+
agentic: ["agentic", "agentic-cli", "ai-tools", "ai-cli"],
|
|
364
387
|
py: [
|
|
365
388
|
"py",
|
|
366
389
|
"python",
|
|
@@ -372,6 +395,8 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
372
395
|
"python311",
|
|
373
396
|
"python312",
|
|
374
397
|
"python313",
|
|
398
|
+
"python314",
|
|
399
|
+
"python315",
|
|
375
400
|
"pixi",
|
|
376
401
|
"pip",
|
|
377
402
|
"poetry",
|
|
@@ -398,6 +423,8 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
398
423
|
"dotnet7",
|
|
399
424
|
"dotnet8",
|
|
400
425
|
"dotnet9",
|
|
426
|
+
"dotnet10",
|
|
427
|
+
"dotnet11",
|
|
401
428
|
"dotnet-framework",
|
|
402
429
|
"dotnet-framework47",
|
|
403
430
|
"dotnet-framework48",
|
|
@@ -417,8 +444,19 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
417
444
|
"hmiproj",
|
|
418
445
|
],
|
|
419
446
|
dart: ["dart", "flutter", "pub"],
|
|
420
|
-
haskell: ["haskell", "hackage", "cabal"],
|
|
447
|
+
haskell: ["haskell", "hackage", "cabal", "stack", "stackage"],
|
|
421
448
|
elixir: ["elixir", "hex", "mix"],
|
|
449
|
+
erlang: ["erlang", "rebar", "rebar3"],
|
|
450
|
+
julia: ["julia"],
|
|
451
|
+
r: ["r", "cran", "renv"],
|
|
452
|
+
ocaml: ["ocaml", "opam", "dune"],
|
|
453
|
+
elm: ["elm"],
|
|
454
|
+
crystal: ["crystal", "shards"],
|
|
455
|
+
nim: ["nim", "nimble"],
|
|
456
|
+
lua: ["lua", "luarocks"],
|
|
457
|
+
perl: ["perl", "cpan", "carton", "cpanfile"],
|
|
458
|
+
spack: ["spack"],
|
|
459
|
+
terraform: ["terraform", "opentofu", "tf"],
|
|
422
460
|
c: ["c", "cpp", "c++", "conan", "collider"],
|
|
423
461
|
clojure: ["clojure", "edn", "clj", "leiningen"],
|
|
424
462
|
github: ["github", "actions"],
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound `user-agent` construction.
|
|
3
|
+
*
|
|
4
|
+
* Package registries police automated traffic through this header, and their
|
|
5
|
+
* requirements differ. GitHub rejects an absent one with a 403; crates.io
|
|
6
|
+
* rejects an absent one and additionally blocks a configurable list of exact
|
|
7
|
+
* strings and prefixes at the CDN edge; Packagist asks for a `mailto=` contact;
|
|
8
|
+
* PyPI asks for a value that uniquely identifies the consumer, with contact
|
|
9
|
+
* information; pub.dev documents a `name/version (+url)` shape. The default
|
|
10
|
+
* built here satisfies all of them at once.
|
|
11
|
+
*
|
|
12
|
+
* Two escape hatches exist for operators whose traffic a registry has taken
|
|
13
|
+
* issue with: `CDXGEN_USER_AGENT` replaces the header everywhere, and
|
|
14
|
+
* `CDXGEN_USER_AGENT_<REGISTRY>` replaces it for one destination.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { CDXGEN_VERSION } from "./state.js";
|
|
18
|
+
|
|
19
|
+
/** Project home page offered as the contact URL in the default user-agent. */
|
|
20
|
+
const CDXGEN_HOME_URL = "https://github.com/cdxgen/cdxgen";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Contact address offered in the default user-agent. Packagist asks API
|
|
24
|
+
* consumers to include one so an operator can reach the project rather than
|
|
25
|
+
* block its traffic; crates.io and PyPI make the same recommendation.
|
|
26
|
+
* Overridable with CDXGEN_CONTACT_EMAIL.
|
|
27
|
+
*/
|
|
28
|
+
const CDXGEN_CONTACT_EMAIL = "cloud@appthreat.com";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Registry keys and the hosts they cover, used to resolve the
|
|
32
|
+
* `CDXGEN_USER_AGENT_<REGISTRY>` override for a request. A host matches its
|
|
33
|
+
* entry exactly or as a subdomain of it.
|
|
34
|
+
*/
|
|
35
|
+
const REGISTRY_HOSTS = Object.freeze({
|
|
36
|
+
CRATES: ["crates.io", "static.crates.io", "index.crates.io"],
|
|
37
|
+
DOCKER: ["docker.io", "docker.com"],
|
|
38
|
+
ELM: ["package.elm-lang.org"],
|
|
39
|
+
GITHUB: ["github.com", "githubusercontent.com", "ghcr.io"],
|
|
40
|
+
GITLAB: ["gitlab.com"],
|
|
41
|
+
GOPROXY: ["proxy.golang.org", "sum.golang.org", "pkg.go.dev"],
|
|
42
|
+
HUGGINGFACE: ["huggingface.co"],
|
|
43
|
+
JSR: ["jsr.io"],
|
|
44
|
+
MAVEN: [
|
|
45
|
+
"repo1.maven.org",
|
|
46
|
+
"repo.maven.apache.org",
|
|
47
|
+
"search.maven.org",
|
|
48
|
+
"central.sonatype.com",
|
|
49
|
+
"oss.sonatype.org",
|
|
50
|
+
],
|
|
51
|
+
NPM: ["registry.npmjs.org", "npmjs.org", "npmjs.com"],
|
|
52
|
+
NUGET: ["nuget.org"],
|
|
53
|
+
PACKAGIST: ["packagist.org"],
|
|
54
|
+
PUB: ["pub.dev"],
|
|
55
|
+
PYPI: ["pypi.org", "pythonhosted.org"],
|
|
56
|
+
RUBYGEMS: ["rubygems.org"],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Substrings that crates.io blocks at the edge, taken from the client
|
|
61
|
+
* libraries its middleware singles out. A user-agent containing one of these
|
|
62
|
+
* is refused with an empty-bodied 403, so an override carrying one would fail
|
|
63
|
+
* silently against that registry.
|
|
64
|
+
*/
|
|
65
|
+
const BLOCKED_SUBSTRINGS = Object.freeze([
|
|
66
|
+
"curl",
|
|
67
|
+
"python-requests",
|
|
68
|
+
"Go-http-client",
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The registry key covering a host, if any.
|
|
73
|
+
*
|
|
74
|
+
* @param {String} hostname Lower-case hostname of the request
|
|
75
|
+
* @returns {String|undefined} Registry key such as `CRATES`, or undefined
|
|
76
|
+
*/
|
|
77
|
+
export function registryKeyForHost(hostname) {
|
|
78
|
+
if (!hostname) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const host = hostname.toLowerCase().replace(/\.$/, "");
|
|
82
|
+
for (const [key, hosts] of Object.entries(REGISTRY_HOSTS)) {
|
|
83
|
+
for (const candidate of hosts) {
|
|
84
|
+
if (host === candidate || host.endsWith(`.${candidate}`)) {
|
|
85
|
+
return key;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The default user-agent: `cdxgen/<version> (+<home url>; mailto=<address>)`.
|
|
94
|
+
* Setting CDXGEN_CONTACT_EMAIL substitutes a different address, which is what
|
|
95
|
+
* an operator running cdxgen at scale should do so that a registry contacts
|
|
96
|
+
* them rather than the project.
|
|
97
|
+
*
|
|
98
|
+
* The `@scope/name` npm spelling is deliberately avoided. crates.io blocks
|
|
99
|
+
* user-agents beginning `@CycloneDX/cdxgen` outright, and a leading `@` is
|
|
100
|
+
* unusual enough elsewhere to invite the same treatment.
|
|
101
|
+
*
|
|
102
|
+
* @returns {String} Default user-agent for outbound requests
|
|
103
|
+
*/
|
|
104
|
+
export function defaultUserAgent() {
|
|
105
|
+
const email =
|
|
106
|
+
(process.env.CDXGEN_CONTACT_EMAIL || "").trim() || CDXGEN_CONTACT_EMAIL;
|
|
107
|
+
return `cdxgen/${CDXGEN_VERSION} (+${CDXGEN_HOME_URL}; mailto=${email})`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The user-agent to send to a host. A `CDXGEN_USER_AGENT_<REGISTRY>` override
|
|
112
|
+
* wins for the hosts of that registry, then the global `CDXGEN_USER_AGENT`,
|
|
113
|
+
* then the default. Blank overrides are ignored: several registries reject an
|
|
114
|
+
* empty header outright, so falling back is safer than honouring it.
|
|
115
|
+
*
|
|
116
|
+
* @param {String} [hostname] Hostname the request is bound for
|
|
117
|
+
* @returns {String} User-agent header value
|
|
118
|
+
*/
|
|
119
|
+
export function getUserAgent(hostname) {
|
|
120
|
+
const key = registryKeyForHost(hostname);
|
|
121
|
+
const perRegistry = key
|
|
122
|
+
? (process.env[`CDXGEN_USER_AGENT_${key}`] || "").trim()
|
|
123
|
+
: "";
|
|
124
|
+
if (perRegistry) {
|
|
125
|
+
return perRegistry;
|
|
126
|
+
}
|
|
127
|
+
const global = (process.env.CDXGEN_USER_AGENT || "").trim();
|
|
128
|
+
return global || defaultUserAgent();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether a user-agent carries a substring that crates.io blocks. Useful for
|
|
133
|
+
* warning an operator whose override would be refused there.
|
|
134
|
+
*
|
|
135
|
+
* @param {String} userAgent User-agent value to test
|
|
136
|
+
* @returns {String|undefined} The offending substring, or undefined
|
|
137
|
+
*/
|
|
138
|
+
export function blockedUserAgentSubstring(userAgent) {
|
|
139
|
+
if (!userAgent) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
const lowered = userAgent.toLowerCase();
|
|
143
|
+
return BLOCKED_SUBSTRINGS.find((candidate) =>
|
|
144
|
+
lowered.includes(candidate.toLowerCase()),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Registry keys that `CDXGEN_USER_AGENT_<REGISTRY>` accepts. */
|
|
149
|
+
export const USER_AGENT_REGISTRY_KEYS = Object.freeze(
|
|
150
|
+
Object.keys(REGISTRY_HOSTS),
|
|
151
|
+
);
|