@cdxgen/cdxgen 13.0.1 → 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 +30 -16
- package/bin/audit.js +6 -0
- package/bin/cdxgen.js +141 -7
- package/bin/evinse.js +3 -1
- package/data/component-tags.json +8 -1
- package/data/crypto-oid.json +88 -12
- package/data/frameworks-list.json +86 -1
- package/data/remediations.json +1167 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/asar-archives.yaml +1 -1
- package/data/rules/build-fidelity.yaml +782 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/data/rules/package-integrity.yaml +13 -13
- package/lib/audit/index.js +48 -4
- package/lib/audit/scoring.js +2 -8
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +32 -1
- package/lib/cli/cliOptions.js +94 -7
- package/lib/cli/index.js +268 -49
- package/lib/cli/jsBom.js +91 -5
- package/lib/cli/jvmBom.js +1515 -1314
- package/lib/cli/managedBom.js +249 -28
- package/lib/cli/nativeBom.js +986 -10
- package/lib/core/activity.js +120 -2
- package/lib/core/buildLedger.js +799 -0
- package/lib/core/deferredExit.js +141 -0
- package/lib/core/env.js +108 -2
- package/lib/core/fs.js +60 -0
- package/lib/core/logger.js +12 -99
- package/lib/core/severity.js +16 -0
- package/lib/core/syncFileWriter.js +106 -0
- 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 +250 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +498 -17
- 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-js.js +26 -0
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-jvm.js +9 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +188 -2
- 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 +172 -36
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +944 -19
- 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/pypiClassifiers.js +326 -0
- package/lib/ecosystems/pythonutils.js +49 -1
- package/lib/ecosystems/rubyutils.js +13 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +141 -13
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/remote/dependency-track.js +81 -14
- 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 +17 -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/depsUtils.js +72 -0
- package/lib/inventory/dosai.js +508 -22
- package/lib/inventory/envcontext.js +215 -24
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +131 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +594 -0
- 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/osinfo.js +1 -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/inventory/toolRequirements.js +903 -0
- package/lib/managers/binary.js +24 -0
- package/lib/managers/docker.js +26 -5
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/server/server.js +1 -1
- package/lib/stages/postgen/annotationBuilder.js +97 -0
- package/lib/stages/postgen/auditBom.js +76 -79
- package/lib/stages/postgen/introspection/annotate.js +320 -0
- package/lib/stages/postgen/introspection/commandFacts.js +293 -0
- package/lib/stages/postgen/introspection/emit.js +192 -0
- package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
- package/lib/stages/postgen/introspection/markers.js +250 -0
- package/lib/stages/postgen/introspection/reflect.js +1214 -0
- package/lib/stages/postgen/introspection/report.js +1208 -0
- package/lib/stages/postgen/introspection/score.js +1067 -0
- package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
- package/lib/stages/postgen/parentEdge.js +156 -0
- package/lib/stages/postgen/postgen.js +284 -3
- package/lib/stages/postgen/ruleEngine.js +17 -0
- package/lib/stages/pregen/envAudit.js +42 -2
- package/lib/stages/pregen/pregen.js +1171 -6
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +26 -26
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts +2 -8
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +2 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +10 -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/buildLedger.d.ts +272 -0
- package/types/lib/core/buildLedger.d.ts.map +1 -0
- package/types/lib/core/deferredExit.d.ts +84 -0
- package/types/lib/core/deferredExit.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +43 -1
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +21 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/logger.d.ts +2 -11
- package/types/lib/core/logger.d.ts.map +1 -1
- package/types/lib/core/severity.d.ts +11 -0
- package/types/lib/core/severity.d.ts.map +1 -0
- package/types/lib/core/syncFileWriter.d.ts +33 -0
- package/types/lib/core/syncFileWriter.d.ts.map +1 -0
- 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 +53 -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-js.d.ts.map +1 -1
- 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-jvm.d.ts.map +1 -1
- 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-python.d.ts.map +1 -1
- 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/pypiClassifiers.d.ts +80 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +21 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +34 -1
- 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/helpers/remote/dependency-track.d.ts +45 -4
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- 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 +12 -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/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +43 -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 +43 -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 +190 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
- 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/osinfo.d.ts.map +1 -1
- 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/inventory/toolRequirements.d.ts +216 -0
- package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- 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/annotationBuilder.d.ts +53 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
- package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +58 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
- package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
- package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
- package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
- package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +35 -2
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +131 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -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
|
+
);
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
import * as toml from "smol-toml";
|
|
6
|
+
|
|
7
|
+
import { readEnvironmentVariable } from "../core/activity.js";
|
|
8
|
+
import { safeExistsSync } from "../core/fs.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Crate metadata read from the local Cargo registry, without any network.
|
|
12
|
+
*
|
|
13
|
+
* Cargo keeps two things under `$CARGO_HOME/registry` that together answer
|
|
14
|
+
* most of what the crates.io API is asked for:
|
|
15
|
+
*
|
|
16
|
+
* - `src/<registry>/<name>-<version>/Cargo.toml` — the manifest Cargo
|
|
17
|
+
* normalised at publish time, carrying the crate's license, description,
|
|
18
|
+
* repository, homepage and minimum toolchain.
|
|
19
|
+
* - `index/<registry>/.cache/<a>/<b>/<name>` — the sparse index cache, one
|
|
20
|
+
* JSON line per published version with its checksum, feature table, yanked
|
|
21
|
+
* flag and publish time.
|
|
22
|
+
*
|
|
23
|
+
* Reading them first matters because crates.io's crawler policy allows one
|
|
24
|
+
* request per second: a 200-crate lockfile is several minutes of waiting that
|
|
25
|
+
* a populated `~/.cargo` makes unnecessary.
|
|
26
|
+
*
|
|
27
|
+
* What the local cache cannot supply is publisher identity. The index records
|
|
28
|
+
* when a version was published but not by whom, so the publisher-drift and
|
|
29
|
+
* ownership signals in `collectCargoRegistryProvenanceProperties` have no
|
|
30
|
+
* local equivalent. A component served from here is marked
|
|
31
|
+
* `cdx:cargo:metadataSource=local-registry` so a consumer can tell why those
|
|
32
|
+
* properties are absent, and `CARGO_METADATA_SOURCE=registry` forces the
|
|
33
|
+
* network path for runs that need them.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* One crate version as the local registry describes it.
|
|
38
|
+
*
|
|
39
|
+
* @typedef {Object} CargoCacheMetadata
|
|
40
|
+
* @property {string} [description] Crate description.
|
|
41
|
+
* @property {string} [license] SPDX license expression.
|
|
42
|
+
* @property {string} [repository] Source repository URL.
|
|
43
|
+
* @property {string} [homepage] Homepage URL.
|
|
44
|
+
* @property {string} [rustVersion] Minimum supported Rust version.
|
|
45
|
+
* @property {string} [checksum] SHA-256 of the published `.crate` archive.
|
|
46
|
+
* @property {Object} [features] Feature table.
|
|
47
|
+
* @property {boolean} [yanked] Whether this version was yanked.
|
|
48
|
+
* @property {string} [publishTime] RFC 3339 publish timestamp.
|
|
49
|
+
* @property {string} [latestVersion] Newest non-yanked version in the index.
|
|
50
|
+
* @property {string[]} sources Which local files answered, for provenance.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/** Parsed index entries, keyed by crate name. A scan asks for the same crate
|
|
54
|
+
* repeatedly across workspace members, and these files hold every version ever
|
|
55
|
+
* published — serde's is over 300 lines. */
|
|
56
|
+
let _indexCache = new Map();
|
|
57
|
+
|
|
58
|
+
/** Registry directories discovered under the Cargo home, memoized per run. */
|
|
59
|
+
let _registryDirs;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Reset memoized state. Tests that point `CARGO_HOME` at a fixture need the
|
|
63
|
+
* next call to look again rather than reuse the previous run's answer.
|
|
64
|
+
*
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
export function resetCargoCacheState() {
|
|
68
|
+
_indexCache = new Map();
|
|
69
|
+
_registryDirs = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether the local registry should be consulted before crates.io.
|
|
74
|
+
*
|
|
75
|
+
* @returns {boolean} false when `CARGO_METADATA_SOURCE=registry` opts out.
|
|
76
|
+
*/
|
|
77
|
+
export function localCargoMetadataEnabled() {
|
|
78
|
+
return (
|
|
79
|
+
readEnvironmentVariable("CARGO_METADATA_SOURCE")?.toLowerCase() !==
|
|
80
|
+
"registry"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the Cargo registry root.
|
|
86
|
+
*
|
|
87
|
+
* @returns {string} Absolute path to `$CARGO_HOME/registry`.
|
|
88
|
+
*/
|
|
89
|
+
export function getCargoRegistryDir() {
|
|
90
|
+
return resolve(
|
|
91
|
+
readEnvironmentVariable("CARGO_HOME") || join(homedir(), ".cargo"),
|
|
92
|
+
"registry",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* List the per-registry directories under `src` and `index`.
|
|
98
|
+
*
|
|
99
|
+
* Each configured registry gets its own directory named after a hash of its
|
|
100
|
+
* URL (`index.crates.io-1949cf8c6b5b557f`). A machine that also pulls from a
|
|
101
|
+
* private registry has more than one, and the crate being looked up may live
|
|
102
|
+
* in any of them, so all are searched.
|
|
103
|
+
*
|
|
104
|
+
* @returns {{src: string[], index: string[]}} Absolute directory paths.
|
|
105
|
+
*/
|
|
106
|
+
function registryDirs() {
|
|
107
|
+
if (_registryDirs) {
|
|
108
|
+
return _registryDirs;
|
|
109
|
+
}
|
|
110
|
+
const root = getCargoRegistryDir();
|
|
111
|
+
_registryDirs = { src: [], index: [] };
|
|
112
|
+
for (const kind of ["src", "index"]) {
|
|
113
|
+
const base = join(root, kind);
|
|
114
|
+
try {
|
|
115
|
+
for (const entry of readdirSync(base, { withFileTypes: true })) {
|
|
116
|
+
if (entry.isDirectory()) {
|
|
117
|
+
_registryDirs[kind].push(join(base, entry.name));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
// No Cargo home, or no registry of this kind: the caller falls back to
|
|
122
|
+
// the network.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return _registryDirs;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Read everything the local registry knows about one crate version.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} name Crate name.
|
|
132
|
+
* @param {string} version Exact version.
|
|
133
|
+
* @returns {CargoCacheMetadata|undefined} Metadata, or undefined when neither
|
|
134
|
+
* local source has this crate.
|
|
135
|
+
*/
|
|
136
|
+
export function readCargoCacheMetadata(name, version) {
|
|
137
|
+
if (!name || !version) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const sources = [];
|
|
141
|
+
const result = { sources };
|
|
142
|
+
const manifest = readExtractedManifest(name, version);
|
|
143
|
+
if (manifest) {
|
|
144
|
+
Object.assign(result, manifest.values);
|
|
145
|
+
sources.push(manifest.source);
|
|
146
|
+
}
|
|
147
|
+
const index = readIndexEntry(name, version);
|
|
148
|
+
if (index) {
|
|
149
|
+
// The manifest is authoritative for the fields both carry, since it is the
|
|
150
|
+
// file Cargo itself compiles against; the index adds what it alone knows.
|
|
151
|
+
for (const [key, value] of Object.entries(index.values)) {
|
|
152
|
+
if (result[key] === undefined) {
|
|
153
|
+
result[key] = value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
sources.push(index.source);
|
|
157
|
+
}
|
|
158
|
+
return sources.length ? result : undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Read the normalised `Cargo.toml` of an extracted crate.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} name Crate name.
|
|
165
|
+
* @param {string} version Exact version.
|
|
166
|
+
* @returns {{values: Object, source: string}|undefined}
|
|
167
|
+
*/
|
|
168
|
+
function readExtractedManifest(name, version) {
|
|
169
|
+
for (const dir of registryDirs().src) {
|
|
170
|
+
const manifestFile = join(dir, `${name}-${version}`, "Cargo.toml");
|
|
171
|
+
if (!safeExistsSync(manifestFile)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
let parsed;
|
|
175
|
+
try {
|
|
176
|
+
parsed = toml.parse(readFileSync(manifestFile, "utf-8"));
|
|
177
|
+
} catch {
|
|
178
|
+
// A manifest that does not parse is not worth a warning: the network
|
|
179
|
+
// path still has the answer.
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const pkg = parsed?.package;
|
|
183
|
+
if (!pkg || typeof pkg !== "object") {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const values = {};
|
|
187
|
+
assignString(values, "description", pkg.description);
|
|
188
|
+
assignString(values, "repository", pkg.repository);
|
|
189
|
+
assignString(values, "homepage", pkg.homepage);
|
|
190
|
+
assignString(values, "rustVersion", pkg["rust-version"]);
|
|
191
|
+
// A crate may carry `license` or, for a non-SPDX license, `license-file`.
|
|
192
|
+
// Only the expression is usable as a component license.
|
|
193
|
+
assignString(values, "license", pkg.license);
|
|
194
|
+
if (parsed.features && typeof parsed.features === "object") {
|
|
195
|
+
values.features = parsed.features;
|
|
196
|
+
}
|
|
197
|
+
return { values, source: manifestFile };
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Read one version's entry from the sparse index cache.
|
|
204
|
+
*
|
|
205
|
+
* The cache file is a small binary envelope: a version byte, the index format
|
|
206
|
+
* version, then NUL-separated fields holding the registry revision followed by
|
|
207
|
+
* alternating version strings and JSON blobs. Rather than decode the header,
|
|
208
|
+
* every NUL-separated chunk that parses as JSON carrying a `vers` field is
|
|
209
|
+
* taken, which keeps working if Cargo revises the envelope.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} name Crate name.
|
|
212
|
+
* @param {string} version Exact version.
|
|
213
|
+
* @returns {{values: Object, source: string}|undefined}
|
|
214
|
+
*/
|
|
215
|
+
function readIndexEntry(name, version) {
|
|
216
|
+
const entries = indexEntriesFor(name);
|
|
217
|
+
if (!entries) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
const match = entries.byVersion.get(version);
|
|
221
|
+
if (!match) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const values = {};
|
|
225
|
+
assignString(values, "checksum", match.cksum);
|
|
226
|
+
if (match.features && typeof match.features === "object") {
|
|
227
|
+
values.features = match.features;
|
|
228
|
+
}
|
|
229
|
+
if (typeof match.yanked === "boolean") {
|
|
230
|
+
values.yanked = match.yanked;
|
|
231
|
+
}
|
|
232
|
+
assignString(values, "publishTime", match.pubtime);
|
|
233
|
+
assignString(values, "rustVersion", match.rust_version);
|
|
234
|
+
assignString(values, "latestVersion", entries.latestVersion);
|
|
235
|
+
return { values, source: entries.source };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Parse and memoize the index cache file for one crate.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} name Crate name.
|
|
242
|
+
* @returns {{byVersion: Map<string, Object>, latestVersion: string|undefined, source: string}|undefined}
|
|
243
|
+
*/
|
|
244
|
+
function indexEntriesFor(name) {
|
|
245
|
+
if (_indexCache.has(name)) {
|
|
246
|
+
return _indexCache.get(name);
|
|
247
|
+
}
|
|
248
|
+
const parsed = parseIndexCacheFile(name);
|
|
249
|
+
_indexCache.set(name, parsed);
|
|
250
|
+
return parsed;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Locate and parse a crate's index cache file.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} name Crate name.
|
|
257
|
+
* @returns {{byVersion: Map<string, Object>, latestVersion: string|undefined, source: string}|undefined}
|
|
258
|
+
*/
|
|
259
|
+
function parseIndexCacheFile(name) {
|
|
260
|
+
for (const dir of registryDirs().index) {
|
|
261
|
+
const path = indexCachePath(dir, name);
|
|
262
|
+
if (!path) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
let raw;
|
|
266
|
+
try {
|
|
267
|
+
if (!statSync(path).isFile()) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
raw = readFileSync(path);
|
|
271
|
+
} catch {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const byVersion = new Map();
|
|
275
|
+
for (const chunk of raw.toString("utf-8").split("\u0000")) {
|
|
276
|
+
if (!chunk.startsWith("{")) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
let entry;
|
|
280
|
+
try {
|
|
281
|
+
entry = JSON.parse(chunk);
|
|
282
|
+
} catch {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (typeof entry?.vers === "string" && entry.vers) {
|
|
286
|
+
byVersion.set(entry.vers, entry);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (!byVersion.size) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
byVersion,
|
|
294
|
+
latestVersion: newestVersion(byVersion),
|
|
295
|
+
source: path,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Build the `.cache` path Cargo uses for a crate name.
|
|
303
|
+
*
|
|
304
|
+
* Names are bucketed by length: one character goes under `1/`, two under `2/`,
|
|
305
|
+
* three under `3/<first letter>/`, and four or more under the first two
|
|
306
|
+
* characters then the next two.
|
|
307
|
+
*
|
|
308
|
+
* @param {string} indexDir The registry's index directory.
|
|
309
|
+
* @param {string} name Crate name.
|
|
310
|
+
* @returns {string|undefined} Absolute path, or undefined for an unusable name.
|
|
311
|
+
*/
|
|
312
|
+
function indexCachePath(indexDir, name) {
|
|
313
|
+
const lower = name.toLowerCase();
|
|
314
|
+
if (!lower.length) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
const cacheDir = join(indexDir, ".cache");
|
|
318
|
+
if (lower.length === 1) {
|
|
319
|
+
return join(cacheDir, "1", lower);
|
|
320
|
+
}
|
|
321
|
+
if (lower.length === 2) {
|
|
322
|
+
return join(cacheDir, "2", lower);
|
|
323
|
+
}
|
|
324
|
+
if (lower.length === 3) {
|
|
325
|
+
return join(cacheDir, "3", lower.slice(0, 1), lower);
|
|
326
|
+
}
|
|
327
|
+
return join(cacheDir, lower.slice(0, 2), lower.slice(2, 4), lower);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Pick the newest non-yanked version from an index file.
|
|
332
|
+
*
|
|
333
|
+
* The index is written in publish order, so the last entry that was not yanked
|
|
334
|
+
* is the version crates.io would call newest. No semver comparison is done:
|
|
335
|
+
* a patch release for an older line is published after the newer line and
|
|
336
|
+
* would win a naive ordering, but it is also what the registry reports as most
|
|
337
|
+
* recent, so publish order is the honest reading.
|
|
338
|
+
*
|
|
339
|
+
* @param {Map<string, Object>} byVersion Parsed entries in file order.
|
|
340
|
+
* @returns {string|undefined} Newest version.
|
|
341
|
+
*/
|
|
342
|
+
function newestVersion(byVersion) {
|
|
343
|
+
let newest;
|
|
344
|
+
for (const [version, entry] of byVersion) {
|
|
345
|
+
if (entry?.yanked !== true) {
|
|
346
|
+
newest = version;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return newest;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Copy a value onto the target when it is a usable string.
|
|
354
|
+
*
|
|
355
|
+
* @param {Object} target Accumulator.
|
|
356
|
+
* @param {string} key Field name.
|
|
357
|
+
* @param {*} value Candidate value.
|
|
358
|
+
* @returns {void}
|
|
359
|
+
*/
|
|
360
|
+
function assignString(target, key, value) {
|
|
361
|
+
if (typeof value === "string" && value.length) {
|
|
362
|
+
target[key] = value;
|
|
363
|
+
}
|
|
364
|
+
}
|
package/lib/ecosystems/caxa.js
CHANGED
|
@@ -51,16 +51,24 @@ export async function parseCaxaMetadata(mfile) {
|
|
|
51
51
|
value: "true",
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
|
+
// The binary-analysis method names the executable the component was found
|
|
55
|
+
// in, which only a parent component can supply. Any `*metadata.json` in
|
|
56
|
+
// the scanned tree reaches this parser, so a document with components and
|
|
57
|
+
// no parent is ordinary input rather than a broken caxa build.
|
|
58
|
+
const methods = [];
|
|
59
|
+
if (parentComponent?.name) {
|
|
60
|
+
methods.push({
|
|
61
|
+
technique: "binary-analysis",
|
|
62
|
+
confidence: 1,
|
|
63
|
+
value: parentComponent.name,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
54
66
|
comp.evidence = {
|
|
55
67
|
identity: {
|
|
56
68
|
field: "purl",
|
|
57
69
|
confidence: 1,
|
|
58
70
|
methods: [
|
|
59
|
-
|
|
60
|
-
technique: "binary-analysis",
|
|
61
|
-
confidence: 1,
|
|
62
|
-
value: parentComponent.name,
|
|
63
|
-
},
|
|
71
|
+
...methods,
|
|
64
72
|
{
|
|
65
73
|
technique: "manifest-analysis",
|
|
66
74
|
confidence: 1,
|
|
@@ -24,7 +24,11 @@ import { CPP_STD_MODULES } from "../core/state.js";
|
|
|
24
24
|
import { findAppModules } from "../inventory/atomUtils.js";
|
|
25
25
|
import { getOSPackageForFile } from "../inventory/osPackageResolver.js";
|
|
26
26
|
import { locateGenericPackage } from "../inventory/purl.js";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
parseCmakeLikeFile,
|
|
29
|
+
parseCUsageSlice,
|
|
30
|
+
parseXmakeRequiresLock,
|
|
31
|
+
} from "./parsers-misc.js";
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Method to find c/c++ modules by collecting usages with atom
|
|
@@ -72,11 +76,22 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
72
76
|
if (vcPkgData.homepage) {
|
|
73
77
|
parentComponent.homepage = { url: vcPkgData.homepage };
|
|
74
78
|
}
|
|
79
|
+
// The builtin baseline is the vcpkg commit that pins every port version,
|
|
80
|
+
// so it identifies the version set the manifest resolves against.
|
|
81
|
+
if (typeof vcPkgData["builtin-baseline"] === "string") {
|
|
82
|
+
parentComponent.properties = [
|
|
83
|
+
{
|
|
84
|
+
name: "cdx:vcpkg:baseline",
|
|
85
|
+
value: vcPkgData["builtin-baseline"],
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
75
89
|
// Are there any dependencies declared in vcpkg.json
|
|
76
90
|
if (vcPkgData.dependencies && Array.isArray(vcPkgData.dependencies)) {
|
|
77
91
|
for (const avcdep of vcPkgData.dependencies) {
|
|
78
92
|
let avcpkgName;
|
|
79
93
|
let scope;
|
|
94
|
+
let vcpkgFeatures;
|
|
80
95
|
if (typeof avcdep === "string" || avcdep instanceof String) {
|
|
81
96
|
avcpkgName = avcdep;
|
|
82
97
|
} else if (Object.keys(avcdep).length && avcdep.name) {
|
|
@@ -84,6 +99,9 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
84
99
|
if (avcdep.host) {
|
|
85
100
|
scope = "optional";
|
|
86
101
|
}
|
|
102
|
+
vcpkgFeatures = Array.isArray(avcdep.features)
|
|
103
|
+
? avcdep.features.join(",")
|
|
104
|
+
: undefined;
|
|
87
105
|
}
|
|
88
106
|
// Is this a dependency we haven't seen before including the all lower and upper case version?
|
|
89
107
|
if (
|
|
@@ -106,6 +124,12 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
106
124
|
purl: pkgPurl,
|
|
107
125
|
scope,
|
|
108
126
|
"bom-ref": decodeURIComponent(pkgPurl),
|
|
127
|
+
properties: [
|
|
128
|
+
{ name: "cdx:vcpkg:declared", value: "true" },
|
|
129
|
+
...(vcpkgFeatures
|
|
130
|
+
? [{ name: "cdx:vcpkg:features", value: vcpkgFeatures }]
|
|
131
|
+
: []),
|
|
132
|
+
],
|
|
109
133
|
evidence: {
|
|
110
134
|
identity: {
|
|
111
135
|
field: "purl",
|
|
@@ -129,7 +153,21 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
129
153
|
}
|
|
130
154
|
}
|
|
131
155
|
} // if
|
|
132
|
-
}
|
|
156
|
+
}
|
|
157
|
+
// xmake locks its resolved requirements in `xmake-requires.lock`; the
|
|
158
|
+
// entries are additions to whatever other evidence collectors found.
|
|
159
|
+
const xmakeLockFile = join(src, "xmake-requires.lock");
|
|
160
|
+
if (safeExistsSync(xmakeLockFile)) {
|
|
161
|
+
const xmakePkgs = parseXmakeRequiresLock(xmakeLockFile);
|
|
162
|
+
for (const apkg of xmakePkgs) {
|
|
163
|
+
if (!pkgAddedMap[apkg.name]) {
|
|
164
|
+
pkgList.push(apkg);
|
|
165
|
+
dependsOn.add(apkg["bom-ref"]);
|
|
166
|
+
pkgAddedMap[apkg.name] = true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!parentComponent && safeExistsSync(join(src, "CMakeLists.txt"))) {
|
|
133
171
|
const retMap = parseCmakeLikeFile(join(src, "CMakeLists.txt"), pkgType);
|
|
134
172
|
if (retMap.parentComponent && Object.keys(retMap.parentComponent).length) {
|
|
135
173
|
parentComponent = retMap.parentComponent;
|