@cdxgen/cdxgen 13.1.0 → 13.3.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 +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -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 +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- 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 +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- 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/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- 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/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- 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 +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- 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 +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.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/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.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/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- 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 +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- 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/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- 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-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- 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-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- 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/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +81 -2
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.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/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.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/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.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/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.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/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.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/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.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/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.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/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
|
@@ -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
|
+
}
|