@cdxgen/cdxgen 13.2.0 → 13.4.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 +33 -14
- package/bin/cdxgen.js +310 -138
- package/bin/evinse.js +22 -0
- package/bin/repl.js +86 -0
- package/bin/sign.js +119 -15
- package/bin/tracebom.js +69 -5
- package/bin/validate.js +107 -19
- package/bin/verify.js +172 -63
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +85 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/bomAssembly.js +105 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +156 -58
- package/lib/cli/jsBom.js +99 -53
- package/lib/cli/jvmBom.js +708 -147
- package/lib/cli/managedBom.js +415 -224
- package/lib/cli/nativeBom.js +264 -44
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +22 -15
- package/lib/core/fs.js +176 -7
- package/lib/core/httpClient.js +159 -24
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +274 -108
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/caxa.js +434 -2
- package/lib/ecosystems/cmakeResolver.js +76 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +399 -51
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1983 -793
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +356 -37
- package/lib/ecosystems/jsEvidence.js +104 -44
- package/lib/ecosystems/kosi.js +31 -7
- package/lib/ecosystems/npmutils.js +174 -9
- package/lib/ecosystems/parsers-dotnet.js +616 -159
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +371 -70
- package/lib/ecosystems/parsers-js.js +263 -76
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +929 -216
- package/lib/ecosystems/parsers-python.js +167 -24
- package/lib/ecosystems/parsers-rust.js +28 -22
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +1492 -182
- package/lib/ecosystems/rusi.js +54 -10
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +1008 -191
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +1101 -222
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +703 -43
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cdxrs.js +103 -12
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +798 -57
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +90 -6
- package/lib/inventory/evidenceUtils.js +166 -113
- package/lib/inventory/fetchBatch.js +690 -55
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/protobom.js +8 -1
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +45 -27
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +295 -25
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +467 -46
- package/lib/managers/oci.js +63 -3
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +466 -0
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +83 -16
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +64 -11
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +2 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +23 -23
- package/types/lib/audit/index.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.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- 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 +9 -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/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.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/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.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/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- 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 +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- 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 +28 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- 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 +70 -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 +13 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +57 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +54 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +16 -5
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +164 -3
- 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/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +132 -15
- 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/bomSigner.d.ts +182 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +219 -5
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.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/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.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 +121 -9
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.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/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +59 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- 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 +17 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +77 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- 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/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +18 -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
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -3,8 +3,8 @@ import { dirname } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { build } from "@cdxgen/cdx-purl";
|
|
5
5
|
|
|
6
|
-
import { DEBUG_MODE
|
|
7
|
-
import {
|
|
6
|
+
import { DEBUG_MODE } from "../core/activity.js";
|
|
7
|
+
import { shouldFetchPackageMetadata } from "../core/env.js";
|
|
8
8
|
import { safeExistsSync } from "../core/fs.js";
|
|
9
9
|
import { getNpmMetadata } from "./ecosystems.js";
|
|
10
10
|
import {
|
|
@@ -21,6 +21,15 @@ import {
|
|
|
21
21
|
|
|
22
22
|
const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/";
|
|
23
23
|
|
|
24
|
+
// The dependency groups bun writes for a package or workspace, with the kind
|
|
25
|
+
// of edge each one declares.
|
|
26
|
+
const DEPENDENCY_GROUP_KINDS = [
|
|
27
|
+
["dependencies", "dependency"],
|
|
28
|
+
["optionalDependencies", "optional"],
|
|
29
|
+
["peerDependencies", "peer"],
|
|
30
|
+
["devDependencies", "dev"],
|
|
31
|
+
];
|
|
32
|
+
|
|
24
33
|
/**
|
|
25
34
|
* Split a bun.lock package descriptor (eg `@babel/parser@7.29.7`,
|
|
26
35
|
* `left-pad@1.3.0` or `foo@git+https://github.com/foo/bar#abcdef`) into its
|
|
@@ -53,10 +62,36 @@ export function parseBunDescriptor(descriptor) {
|
|
|
53
62
|
return { group, name, version };
|
|
54
63
|
}
|
|
55
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Split a bun.lock package key into its package-name segments. A key is the
|
|
67
|
+
* dependency path bun nested the package under (eg `@isaacs/cliui/wrap-ansi`),
|
|
68
|
+
* where every segment is a package name, so scoped names (`@scope/name`)
|
|
69
|
+
* count as a single segment despite containing `/`.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} key The lockfile package key.
|
|
72
|
+
* @returns {string[]} The package names composing the key.
|
|
73
|
+
*/
|
|
74
|
+
function keySegments(key) {
|
|
75
|
+
const parts = key.split("/");
|
|
76
|
+
const segments = [];
|
|
77
|
+
for (let i = 0; i < parts.length; i++) {
|
|
78
|
+
if (parts[i].startsWith("@") && i + 1 < parts.length) {
|
|
79
|
+
segments.push(`${parts[i]}/${parts[i + 1]}`);
|
|
80
|
+
i++;
|
|
81
|
+
} else {
|
|
82
|
+
segments.push(parts[i]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return segments;
|
|
86
|
+
}
|
|
87
|
+
|
|
56
88
|
/**
|
|
57
89
|
* Determine whether a bun version specifier points at a non-registry source
|
|
58
90
|
* (git, tarball URL, workspace or local path).
|
|
59
91
|
*
|
|
92
|
+
* Bun writes a local tarball as a bare path (`localdep@./localdep.tgz`) rather
|
|
93
|
+
* than a `file:` specifier, so relative and absolute paths count too.
|
|
94
|
+
*
|
|
60
95
|
* @param {string} version The specifier extracted from the descriptor.
|
|
61
96
|
* @returns {boolean} True when the specifier is not a plain semver version.
|
|
62
97
|
*/
|
|
@@ -65,6 +100,9 @@ function isNonRegistrySpecifier(version) {
|
|
|
65
100
|
return false;
|
|
66
101
|
}
|
|
67
102
|
return (
|
|
103
|
+
version.startsWith("./") ||
|
|
104
|
+
version.startsWith("../") ||
|
|
105
|
+
version.startsWith("/") ||
|
|
68
106
|
version.startsWith("git") ||
|
|
69
107
|
version.includes("://") ||
|
|
70
108
|
version.startsWith("github:") ||
|
|
@@ -77,14 +115,42 @@ function isNonRegistrySpecifier(version) {
|
|
|
77
115
|
}
|
|
78
116
|
|
|
79
117
|
/**
|
|
80
|
-
*
|
|
118
|
+
* Normalize a lockfile `os`/`cpu` value. Bun writes a single string for the
|
|
119
|
+
* common one-value case (eg `"darwin"`), a negated string (eg `"!linux"`), an
|
|
120
|
+
* array for several values and the string `"none"` when the manifest named no
|
|
121
|
+
* platform bun recognizes (eg `cpu: ["wasm32"]`), which carries no usable
|
|
122
|
+
* value, so both shapes have to be handled (see `Negatable.toJson` in bun's
|
|
123
|
+
* npm.zig, `Negatable::to_json` in the Rust port).
|
|
124
|
+
*
|
|
125
|
+
* @param {string|string[]} value The raw `os`/`cpu` metadata value.
|
|
126
|
+
* @returns {string|undefined} Comma-joined value, or undefined when absent.
|
|
127
|
+
*/
|
|
128
|
+
function normalizeOsCpu(value) {
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
return value.length ? value.join(", ") : undefined;
|
|
131
|
+
}
|
|
132
|
+
if (typeof value === "string" && value.length && value !== "none") {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Parse a bun text lockfile (`bun.lock`, lockfileVersion 1-3; v1 was written
|
|
140
|
+
* by the Zig implementation in bun 1.2.x-1.3.x and v2/v3 by the Rust one in
|
|
141
|
+
* bun 1.4+ - the content shape is identical, only parse strictness changed).
|
|
81
142
|
*
|
|
82
143
|
* Bun's text lockfile is JSONC (JSON with trailing commas). It records the
|
|
83
144
|
* workspace roots under `workspaces` and the fully resolved dependency tree
|
|
84
|
-
* under `packages`, where
|
|
85
|
-
* `
|
|
86
|
-
*
|
|
87
|
-
*
|
|
145
|
+
* under `packages`, where nested duplicate versions are keyed by their
|
|
146
|
+
* dependency path (eg `"parent/child"`). The entry layout after the leading
|
|
147
|
+
* `"name@version"` descriptor depends on the resolution type (see the
|
|
148
|
+
* Stringifier in bun's bun.lock.rs):
|
|
149
|
+
* - npm: `["name@version", tarballUrlOrEmpty, { dependencies, bin, os, ... }, "sha512-..."]`
|
|
150
|
+
* - git/github: `["name@git+repo", { ... }, ".bun-tag", "sha512-..."]`
|
|
151
|
+
* - tarball/folder/symlink: `["name@url", { ... }, "sha512-..."]`
|
|
152
|
+
* - workspace: `["name@workspace:path"]`
|
|
153
|
+
* so the elements are recognised by shape instead of fixed indices.
|
|
88
154
|
*
|
|
89
155
|
* The binary lockfile (`bun.lockb`) is intentionally not supported - callers
|
|
90
156
|
* should ask users to regenerate it with `bun install --save-text-lockfile`.
|
|
@@ -126,35 +192,65 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
126
192
|
// nested duplicate versions by dependency path (eg `parent/child`), which we
|
|
127
193
|
// handle per-lookup in resolveDepRef below.
|
|
128
194
|
const infoForKey = new Map();
|
|
129
|
-
const refToKey = new Map();
|
|
130
195
|
for (const [key, entry] of Object.entries(packages)) {
|
|
131
196
|
if (!Array.isArray(entry) || !entry.length) {
|
|
132
197
|
continue;
|
|
133
198
|
}
|
|
134
199
|
const descriptor = entry[0];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
200
|
+
if (typeof descriptor !== "string") {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
138
203
|
const { group, name, version } = parseBunDescriptor(descriptor);
|
|
139
|
-
if (!name || !version) {
|
|
204
|
+
if (!name || !version || version === "root:") {
|
|
140
205
|
continue;
|
|
141
206
|
}
|
|
207
|
+
const isWorkspaceDep = version.startsWith("workspace:");
|
|
142
208
|
const isGitDep =
|
|
143
209
|
version.startsWith("git") ||
|
|
144
210
|
version.startsWith("github:") ||
|
|
145
211
|
version.startsWith("gitlab:") ||
|
|
146
212
|
version.startsWith("bitbucket:");
|
|
213
|
+
const isNonRegistry = isGitDep || isNonRegistrySpecifier(version);
|
|
214
|
+
// The elements after the descriptor depend on the resolution type, so
|
|
215
|
+
// recognise them by shape: the metadata object, the integrity hash and,
|
|
216
|
+
// for npm packages on a custom registry, the tarball URL. The git
|
|
217
|
+
// `.bun-tag` string is not needed.
|
|
218
|
+
let meta = {};
|
|
219
|
+
let integrity;
|
|
220
|
+
let tarballUrl;
|
|
221
|
+
let sawMeta = false;
|
|
222
|
+
for (const el of entry.slice(1)) {
|
|
223
|
+
if (el && typeof el === "object" && !Array.isArray(el)) {
|
|
224
|
+
if (!sawMeta) {
|
|
225
|
+
meta = el;
|
|
226
|
+
sawMeta = true;
|
|
227
|
+
}
|
|
228
|
+
} else if (typeof el === "string" && el.length) {
|
|
229
|
+
if (/^sha\d+-/.test(el)) {
|
|
230
|
+
integrity = integrity || el;
|
|
231
|
+
} else if (!isNonRegistry) {
|
|
232
|
+
tarballUrl = tarballUrl || el;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Workspace members point back at their manifest via `workspace:<path>`;
|
|
237
|
+
// use the version declared there for the component identity.
|
|
238
|
+
let componentVersion = version;
|
|
239
|
+
if (isWorkspaceDep) {
|
|
240
|
+
const wsEntry = workspaces[version.slice("workspace:".length)];
|
|
241
|
+
componentVersion = wsEntry?.version || null;
|
|
242
|
+
}
|
|
147
243
|
let qualifiers = null;
|
|
148
244
|
if (isGitDep) {
|
|
149
245
|
qualifiers = buildNpmGitPurlQualifiers(version, group, npmrcConfig);
|
|
150
|
-
} else if (
|
|
246
|
+
} else if (isNonRegistry && !isWorkspaceDep) {
|
|
151
247
|
qualifiers = { download_url: version };
|
|
152
248
|
}
|
|
153
249
|
const purlString = build({
|
|
154
250
|
type: "npm",
|
|
155
251
|
namespace: group || null,
|
|
156
252
|
name: name,
|
|
157
|
-
version:
|
|
253
|
+
version: componentVersion,
|
|
158
254
|
qualifiers: qualifiers || null,
|
|
159
255
|
});
|
|
160
256
|
const bomRef = decodeURIComponent(purlString);
|
|
@@ -162,116 +258,178 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
162
258
|
group,
|
|
163
259
|
name,
|
|
164
260
|
version,
|
|
165
|
-
|
|
261
|
+
componentVersion,
|
|
262
|
+
registry: tarballUrl || "",
|
|
166
263
|
meta,
|
|
167
264
|
integrity,
|
|
168
265
|
purlString,
|
|
169
266
|
bomRef,
|
|
170
267
|
isGitDep,
|
|
171
|
-
|
|
268
|
+
isWorkspaceDep,
|
|
269
|
+
isNonRegistry,
|
|
172
270
|
});
|
|
173
|
-
if (!refToKey.has(bomRef)) {
|
|
174
|
-
refToKey.set(bomRef, key);
|
|
175
|
-
}
|
|
176
271
|
}
|
|
177
272
|
|
|
178
|
-
// Resolve a dependency name referenced from `parentKey` to the bom-ref of
|
|
179
|
-
// concrete package bun installed for it.
|
|
180
|
-
//
|
|
273
|
+
// Resolve a dependency name referenced from `parentKey` to the bom-ref of
|
|
274
|
+
// the concrete package bun installed for it. Bun walks up the parent's key
|
|
275
|
+
// one package segment at a time (eg for `a/@scope/b/c` it tries
|
|
276
|
+
// `a/@scope/b/c/<dep>`, `a/@scope/b/<dep>`, `a/<dep>`, then `<dep>`) and
|
|
277
|
+
// uses the first entry it finds, so a version nested at an intermediate
|
|
278
|
+
// level must win over the top-level one.
|
|
181
279
|
const resolveDepRef = (parentKey, depName) => {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
280
|
+
const segments = parentKey ? keySegments(parentKey) : [];
|
|
281
|
+
for (let i = segments.length; i >= 0; i--) {
|
|
282
|
+
const key = [...segments.slice(0, i), depName].join("/");
|
|
283
|
+
if (infoForKey.has(key)) {
|
|
284
|
+
return infoForKey.get(key).bomRef;
|
|
285
|
+
}
|
|
188
286
|
}
|
|
189
287
|
return undefined;
|
|
190
288
|
};
|
|
191
289
|
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
//
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
for (const depName of Object.keys(depBlock)) {
|
|
290
|
+
// Resolve the dependency groups an entry declares to the packages bun
|
|
291
|
+
// installed for them, tagging each edge with how it was declared. A name
|
|
292
|
+
// listed in `optionalPeers` is an optional peer: bun binds it to a package
|
|
293
|
+
// some other edge installed, but never installs one for it. Registry
|
|
294
|
+
// packages never carry devDependencies (bun does not install them), while
|
|
295
|
+
// workspaces and `file:` folders do.
|
|
296
|
+
const addEdges = (edges, parentKey, meta) => {
|
|
297
|
+
const optionalPeers = new Set(
|
|
298
|
+
Array.isArray(meta.optionalPeers) ? meta.optionalPeers : [],
|
|
299
|
+
);
|
|
300
|
+
for (const [group, kind] of DEPENDENCY_GROUP_KINDS) {
|
|
301
|
+
for (const depName of Object.keys(meta[group] || {})) {
|
|
205
302
|
const ref = resolveDepRef(parentKey, depName);
|
|
206
|
-
if (ref) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
303
|
+
if (!ref) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const edgeKind =
|
|
307
|
+
kind === "peer" && optionalPeers.has(depName) ? "optionalPeer" : kind;
|
|
308
|
+
if (!edges.has(ref)) {
|
|
309
|
+
edges.set(ref, new Set());
|
|
211
310
|
}
|
|
311
|
+
edges.get(ref).add(edgeKind);
|
|
212
312
|
}
|
|
213
|
-
}
|
|
214
|
-
collect(meta.dependencies, null);
|
|
215
|
-
collect(meta.optionalDependencies, optionalRefs);
|
|
216
|
-
collect(meta.peerDependencies, peerRefs);
|
|
217
|
-
return refs;
|
|
313
|
+
}
|
|
218
314
|
};
|
|
219
315
|
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
316
|
+
// Collect the edges of every package, unioned per bom-ref since the same
|
|
317
|
+
// version can be nested under several keys (or aliases). A workspace
|
|
318
|
+
// member's own entry has no metadata: its dependencies come from its
|
|
319
|
+
// `workspaces` entry and resolve under the member's package name (bun nests
|
|
320
|
+
// a member's own versions there, eg `pkg-a/strip-ansi`). Bun marks each
|
|
321
|
+
// copy installed from a parent's bundleDependencies, so a version counts as
|
|
322
|
+
// bundled only when no copy of it was installed on its own.
|
|
323
|
+
const edgesByRef = new Map();
|
|
324
|
+
const memberRefs = new Set();
|
|
325
|
+
const bundledRefs = new Set();
|
|
326
|
+
const unbundledRefs = new Set();
|
|
327
|
+
for (const [key, info] of infoForKey.entries()) {
|
|
328
|
+
if (!edgesByRef.has(info.bomRef)) {
|
|
329
|
+
edgesByRef.set(info.bomRef, new Map());
|
|
232
330
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
optionalRefs.add(ref);
|
|
238
|
-
}
|
|
331
|
+
if (info.meta.bundled === true) {
|
|
332
|
+
bundledRefs.add(info.bomRef);
|
|
333
|
+
} else {
|
|
334
|
+
unbundledRefs.add(info.bomRef);
|
|
239
335
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
336
|
+
const edges = edgesByRef.get(info.bomRef);
|
|
337
|
+
if (info.isWorkspaceDep) {
|
|
338
|
+
memberRefs.add(info.bomRef);
|
|
339
|
+
const wsEntry = workspaces[info.version.slice("workspace:".length)];
|
|
340
|
+
if (wsEntry) {
|
|
341
|
+
const fullName = info.group ? `${info.group}/${info.name}` : info.name;
|
|
342
|
+
addEdges(edges, wsEntry.name || fullName, wsEntry);
|
|
244
343
|
}
|
|
344
|
+
} else {
|
|
345
|
+
addEdges(edges, key, info.meta);
|
|
245
346
|
}
|
|
246
347
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!key) {
|
|
257
|
-
continue;
|
|
348
|
+
// The root resolves at the top level. Bun makes every workspace member a
|
|
349
|
+
// dependency of the root, whether or not the root declares it.
|
|
350
|
+
const rootEdges = new Map();
|
|
351
|
+
if (workspaces[""]) {
|
|
352
|
+
addEdges(rootEdges, "", workspaces[""]);
|
|
353
|
+
}
|
|
354
|
+
for (const ref of memberRefs) {
|
|
355
|
+
if (!rootEdges.has(ref)) {
|
|
356
|
+
rootEdges.set(ref, new Set());
|
|
258
357
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
358
|
+
rootEdges.get(ref).add("workspace");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Walk the graph from the root following only the given edge kinds.
|
|
362
|
+
const reachableVia = (kinds) => {
|
|
363
|
+
const seen = new Set();
|
|
364
|
+
const stack = [];
|
|
365
|
+
const visit = (edges) => {
|
|
366
|
+
for (const [ref, edgeKinds] of edges) {
|
|
367
|
+
if (seen.has(ref)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
for (const kind of edgeKinds) {
|
|
371
|
+
if (kinds.includes(kind)) {
|
|
372
|
+
seen.add(ref);
|
|
373
|
+
stack.push(ref);
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
262
377
|
}
|
|
378
|
+
};
|
|
379
|
+
visit(rootEdges);
|
|
380
|
+
while (stack.length) {
|
|
381
|
+
visit(edgesByRef.get(stack.pop()));
|
|
263
382
|
}
|
|
264
|
-
|
|
383
|
+
return seen;
|
|
384
|
+
};
|
|
385
|
+
// Like npm's dev/optional/peer flags, a package is development-only,
|
|
386
|
+
// optional or peer-only when every path from the root reaches it through
|
|
387
|
+
// that kind of edge. Production follows required peers (bun auto-installs
|
|
388
|
+
// them) but not optional peers, which never install anything themselves.
|
|
389
|
+
const installedRefs = reachableVia([
|
|
390
|
+
"dependency",
|
|
391
|
+
"optional",
|
|
392
|
+
"peer",
|
|
393
|
+
"optionalPeer",
|
|
394
|
+
"dev",
|
|
395
|
+
"workspace",
|
|
396
|
+
]);
|
|
397
|
+
const prodRefs = reachableVia([
|
|
398
|
+
"dependency",
|
|
399
|
+
"optional",
|
|
400
|
+
"peer",
|
|
401
|
+
"workspace",
|
|
402
|
+
]);
|
|
403
|
+
const nonOptionalRefs = reachableVia([
|
|
404
|
+
"dependency",
|
|
405
|
+
"peer",
|
|
406
|
+
"dev",
|
|
407
|
+
"workspace",
|
|
408
|
+
]);
|
|
409
|
+
const nonPeerRefs = reachableVia([
|
|
410
|
+
"dependency",
|
|
411
|
+
"optional",
|
|
412
|
+
"dev",
|
|
413
|
+
"workspace",
|
|
414
|
+
]);
|
|
265
415
|
|
|
266
416
|
// Second pass: emit the package list and dependency graph.
|
|
267
417
|
const seenRefs = new Set();
|
|
268
|
-
for (const
|
|
418
|
+
for (const info of infoForKey.values()) {
|
|
269
419
|
if (seenRefs.has(info.bomRef)) {
|
|
270
420
|
continue;
|
|
271
421
|
}
|
|
272
422
|
seenRefs.add(info.bomRef);
|
|
273
|
-
const {
|
|
274
|
-
|
|
423
|
+
const {
|
|
424
|
+
group,
|
|
425
|
+
name,
|
|
426
|
+
version,
|
|
427
|
+
componentVersion,
|
|
428
|
+
registry,
|
|
429
|
+
meta,
|
|
430
|
+
integrity,
|
|
431
|
+
purlString,
|
|
432
|
+
} = info;
|
|
275
433
|
const properties = [{ name: "internal:SrcFile", value: bunLockFile }];
|
|
276
434
|
const externalReferences = [];
|
|
277
435
|
|
|
@@ -286,7 +444,7 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
286
444
|
defaultRegistry,
|
|
287
445
|
group,
|
|
288
446
|
name,
|
|
289
|
-
|
|
447
|
+
componentVersion,
|
|
290
448
|
);
|
|
291
449
|
}
|
|
292
450
|
if (resolvedUrl) {
|
|
@@ -318,6 +476,13 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
318
476
|
if (info.isNonRegistry) {
|
|
319
477
|
properties.push({ name: "cdx:npm:isRegistryDependency", value: "false" });
|
|
320
478
|
}
|
|
479
|
+
if (info.isWorkspaceDep) {
|
|
480
|
+
properties.push({ name: "cdx:npm:isWorkspace", value: "true" });
|
|
481
|
+
}
|
|
482
|
+
// npm lockfiles record the same bundleDependencies marker as `inBundle`.
|
|
483
|
+
if (bundledRefs.has(info.bomRef) && !unbundledRefs.has(info.bomRef)) {
|
|
484
|
+
properties.push({ name: "cdx:npm:inBundle", value: "true" });
|
|
485
|
+
}
|
|
321
486
|
if (meta.bin) {
|
|
322
487
|
const binValue =
|
|
323
488
|
typeof meta.bin === "object"
|
|
@@ -326,17 +491,19 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
326
491
|
properties.push({ name: "cdx:npm:bin", value: binValue });
|
|
327
492
|
properties.push({ name: "cdx:npm:has_binary", value: "true" });
|
|
328
493
|
}
|
|
329
|
-
|
|
330
|
-
|
|
494
|
+
const osValue = normalizeOsCpu(meta.os);
|
|
495
|
+
if (osValue) {
|
|
496
|
+
properties.push({ name: "cdx:npm:os", value: osValue });
|
|
331
497
|
}
|
|
332
|
-
|
|
333
|
-
|
|
498
|
+
const cpuValue = normalizeOsCpu(meta.cpu);
|
|
499
|
+
if (cpuValue) {
|
|
500
|
+
properties.push({ name: "cdx:npm:cpu", value: cpuValue });
|
|
334
501
|
}
|
|
335
502
|
|
|
336
503
|
const pkgObj = {
|
|
337
504
|
group: group || "",
|
|
338
505
|
name,
|
|
339
|
-
version,
|
|
506
|
+
version: componentVersion ?? undefined,
|
|
340
507
|
purl: purlString,
|
|
341
508
|
"bom-ref": info.bomRef,
|
|
342
509
|
_integrity: integrity || undefined,
|
|
@@ -360,21 +527,22 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
360
527
|
}
|
|
361
528
|
// Packages that are not reachable through production dependencies are
|
|
362
529
|
// development-only tooling.
|
|
363
|
-
|
|
530
|
+
const ref = info.bomRef;
|
|
531
|
+
if (!prodRefs.has(ref)) {
|
|
364
532
|
pkgObj.scope = "optional";
|
|
365
533
|
setNpmDevelopmentProperty(pkgObj);
|
|
366
534
|
}
|
|
367
|
-
if (
|
|
535
|
+
if (installedRefs.has(ref) && !nonOptionalRefs.has(ref)) {
|
|
368
536
|
pkgObj.scope = "optional";
|
|
369
537
|
setNpmOptionalProperty(pkgObj);
|
|
370
538
|
}
|
|
371
|
-
if (
|
|
539
|
+
if (installedRefs.has(ref) && !nonPeerRefs.has(ref)) {
|
|
372
540
|
setNpmPeerProperty(pkgObj);
|
|
373
541
|
}
|
|
374
542
|
pkgList.push(pkgObj);
|
|
375
543
|
dependenciesList.push({
|
|
376
|
-
ref
|
|
377
|
-
dependsOn: [...
|
|
544
|
+
ref,
|
|
545
|
+
dependsOn: [...edgesByRef.get(ref).keys()].sort(),
|
|
378
546
|
});
|
|
379
547
|
}
|
|
380
548
|
|
|
@@ -382,15 +550,13 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
382
550
|
if (options.parentComponent?.["bom-ref"]) {
|
|
383
551
|
dependenciesList.push({
|
|
384
552
|
ref: options.parentComponent["bom-ref"],
|
|
385
|
-
dependsOn: [...
|
|
553
|
+
dependsOn: [...rootEdges.keys()].sort(),
|
|
386
554
|
});
|
|
387
555
|
}
|
|
388
556
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
readEnvironmentVariable("FETCH_LICENSE") === "true"
|
|
393
|
-
) {
|
|
557
|
+
// The shared metadata gate: FETCH_LICENSE and the provenance fetch that
|
|
558
|
+
// --bom-audit turns on both reach the registry from here.
|
|
559
|
+
if (shouldFetchPackageMetadata()) {
|
|
394
560
|
if (DEBUG_MODE) {
|
|
395
561
|
console.log(
|
|
396
562
|
`About to fetch npm registry metadata for ${pkgList.length} packages in parseBunLock`,
|