@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
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// import parsers-js directly instead of having parsePkgJson threaded in as an
|
|
7
7
|
// injected helper from the CLI.
|
|
8
8
|
|
|
9
|
-
import { join, resolve } from "node:path";
|
|
9
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
|
|
11
|
-
import { DEBUG_MODE } from "../core/activity.js";
|
|
12
11
|
import { safeExistsSync } from "../core/fs.js";
|
|
13
12
|
import { createOccurrenceEvidence } from "../inventory/evidenceUtils.js";
|
|
13
|
+
import { isPkgTypeOnlyImport, setNpmTypeOnlyProperty } from "./npmutils.js";
|
|
14
14
|
import { parsePkgJson } from "./parsers-js.js";
|
|
15
15
|
|
|
16
16
|
const NPM_BIN_IMPORT_PREFIX = "cdx:npm:bin/";
|
|
@@ -104,13 +104,95 @@ function applyNpmBinCommandEvidence(pkg, allImports) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Full npm package name (`@scope/name` or `name`) of a component or manifest.
|
|
109
|
+
*
|
|
110
|
+
* @param {{group?: string, name?: string}} pkg Component or parsed manifest.
|
|
111
|
+
* @returns {string} The package name npm installs it under.
|
|
112
|
+
*/
|
|
113
|
+
function npmPackageName({ group, name }) {
|
|
114
|
+
const scope = group?.startsWith("@") ? group.substring(1) : group;
|
|
115
|
+
return scope ? `@${scope}/${name}` : name;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Fill in a component's missing npm metadata (description, author, license,
|
|
120
|
+
* homepage, repository) from the package.json of its installed copy.
|
|
121
|
+
*
|
|
122
|
+
* `internal:LocalNodeModulesPath` holds either an absolute install directory
|
|
123
|
+
* (pnpm) or an arborist `node.location`. A location that starts with
|
|
124
|
+
* `node_modules/` is relative to the directory of the component's
|
|
125
|
+
* `internal:SrcFile`. Other relative locations, such as a workspace member or a
|
|
126
|
+
* package nested under one, have no recorded base and are skipped; the deep npm
|
|
127
|
+
* parse reads those manifests from the arborist node itself. The manifest is
|
|
128
|
+
* used only when it names the component's package and version, and it fills
|
|
129
|
+
* only the fields the component lacks.
|
|
130
|
+
*
|
|
131
|
+
* @param {object} pkg Package component to enrich in place.
|
|
132
|
+
*/
|
|
133
|
+
async function fillMissingMetadataFromLocalNodeModules(pkg) {
|
|
134
|
+
if (pkg.description || !Array.isArray(pkg.properties)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const propValue = (propName) =>
|
|
138
|
+
pkg.properties.find((property) => property.name === propName)?.value;
|
|
139
|
+
const localNodeModulesPath = propValue("internal:LocalNodeModulesPath");
|
|
140
|
+
if (!localNodeModulesPath) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let pkgJsonFile;
|
|
144
|
+
if (isAbsolute(localNodeModulesPath)) {
|
|
145
|
+
pkgJsonFile = join(localNodeModulesPath, "package.json");
|
|
146
|
+
} else {
|
|
147
|
+
const srcFile = propValue("internal:SrcFile");
|
|
148
|
+
if (!srcFile || !localNodeModulesPath.startsWith("node_modules/")) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
pkgJsonFile = resolve(
|
|
152
|
+
dirname(srcFile),
|
|
153
|
+
localNodeModulesPath,
|
|
154
|
+
"package.json",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (!safeExistsSync(pkgJsonFile)) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const lnmPkgList = await parsePkgJson(pkgJsonFile, true);
|
|
161
|
+
const lnmMetadata = lnmPkgList?.length === 1 ? lnmPkgList[0] : undefined;
|
|
162
|
+
if (
|
|
163
|
+
!lnmMetadata ||
|
|
164
|
+
npmPackageName(lnmMetadata) !== npmPackageName(pkg) ||
|
|
165
|
+
lnmMetadata.version !== pkg.version
|
|
166
|
+
) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!pkg.description && lnmMetadata.description) {
|
|
170
|
+
pkg.description = lnmMetadata.description;
|
|
171
|
+
}
|
|
172
|
+
if (!pkg.author && lnmMetadata.author) {
|
|
173
|
+
pkg.author = lnmMetadata.author;
|
|
174
|
+
}
|
|
175
|
+
if (!pkg.license && lnmMetadata.license) {
|
|
176
|
+
pkg.license = lnmMetadata.license;
|
|
177
|
+
}
|
|
178
|
+
if (!pkg.homepage && lnmMetadata.homepage) {
|
|
179
|
+
pkg.homepage = lnmMetadata.homepage;
|
|
180
|
+
}
|
|
181
|
+
if (!pkg.repository && lnmMetadata.repository) {
|
|
182
|
+
pkg.repository = lnmMetadata.repository;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
107
186
|
/**
|
|
108
187
|
* Attach JS/TS import and export usage evidence to matching package components.
|
|
109
188
|
*
|
|
110
189
|
* For each component, resolves its module aliases (including the deno jsr
|
|
111
190
|
* specifier), records imported/exported modules as properties, attaches
|
|
112
|
-
* occurrence evidence, and
|
|
113
|
-
* package is used
|
|
191
|
+
* occurrence evidence, and promotes the component scope to "required" when the
|
|
192
|
+
* package is used. A package with no observed usage keeps the scope its
|
|
193
|
+
* manifest parser assigned: static import analysis cannot see packages loaded
|
|
194
|
+
* from configuration, bundler entrypoints, stylesheets, or plugins, so a
|
|
195
|
+
* missing import is not evidence that a package is optional.
|
|
114
196
|
*
|
|
115
197
|
* @param {Array<object>} pkgList Package components to enrich.
|
|
116
198
|
* @param {object} allImports Map of import specifier to usage evidence objects.
|
|
@@ -128,15 +210,16 @@ export async function addEvidenceForImports(
|
|
|
128
210
|
const impPkgs = Object.keys(allImports);
|
|
129
211
|
const exportedPkgs = Object.keys(allExports);
|
|
130
212
|
for (const pkg of pkgList) {
|
|
131
|
-
if (impPkgs?.length) {
|
|
132
|
-
// Assume that all packages are optional until we see an evidence
|
|
133
|
-
pkg.scope = "optional";
|
|
134
|
-
}
|
|
135
213
|
const { group, name } = pkg;
|
|
136
214
|
// Evidence belonging to a type must be associated with the package
|
|
137
215
|
if (group === "@types") {
|
|
138
216
|
continue;
|
|
139
217
|
}
|
|
218
|
+
// Runs ahead of the alias loop, which a matching import ends with a break,
|
|
219
|
+
// so that every component is enriched whichever alias the source imports.
|
|
220
|
+
if (deep) {
|
|
221
|
+
await fillMissingMetadataFromLocalNodeModules(pkg);
|
|
222
|
+
}
|
|
140
223
|
const aliases = group?.length
|
|
141
224
|
? [name, `${group}/${name}`, `@${group}/${name}`]
|
|
142
225
|
: [name];
|
|
@@ -155,7 +238,6 @@ export async function addEvidenceForImports(
|
|
|
155
238
|
aliases.push(jsrSpecifier);
|
|
156
239
|
}
|
|
157
240
|
}
|
|
158
|
-
let isImported = false;
|
|
159
241
|
for (const alias of aliases) {
|
|
160
242
|
const isWasmAlias = /\.wasm([?#].*)?$/i.test(alias);
|
|
161
243
|
const all_includes = impPkgs.filter(
|
|
@@ -213,11 +295,22 @@ export async function addEvidenceForImports(
|
|
|
213
295
|
}
|
|
214
296
|
// Identify all the imported modules of a component
|
|
215
297
|
if (impPkgs.includes(alias) || all_includes.length) {
|
|
216
|
-
isImported = true;
|
|
217
298
|
let importedModules = new Set();
|
|
218
299
|
let wasmExportedModules = new Set();
|
|
219
300
|
const seenOccurrenceLocations = new Set();
|
|
220
|
-
|
|
301
|
+
// A package imported only for its types is erased at compile time.
|
|
302
|
+
// Scoping it excluded here, rather than when the BOM is assembled,
|
|
303
|
+
// keeps propagateRequiredScopeFromDependencies from promoting what it
|
|
304
|
+
// depends on. A package whose bin command is used is still required.
|
|
305
|
+
if (
|
|
306
|
+
isPkgTypeOnlyImport(allImports, group, name) &&
|
|
307
|
+
!getNpmBinCommandEvidenceKeys(allImports, pkg).length
|
|
308
|
+
) {
|
|
309
|
+
pkg.scope = "excluded";
|
|
310
|
+
setNpmTypeOnlyProperty(pkg);
|
|
311
|
+
} else {
|
|
312
|
+
pkg.scope = "required";
|
|
313
|
+
}
|
|
221
314
|
for (const subevidence of all_includes) {
|
|
222
315
|
const evidences = allImports[subevidence];
|
|
223
316
|
for (const evidence of evidences) {
|
|
@@ -273,39 +366,6 @@ export async function addEvidenceForImports(
|
|
|
273
366
|
}
|
|
274
367
|
break;
|
|
275
368
|
}
|
|
276
|
-
if (
|
|
277
|
-
impPkgs?.length > 0 &&
|
|
278
|
-
!isImported &&
|
|
279
|
-
DEBUG_MODE &&
|
|
280
|
-
pkg?.scope !== "optional"
|
|
281
|
-
) {
|
|
282
|
-
console.debug(
|
|
283
|
-
`\x1b[1;35mNotice: Package ${pkg.name} has no usage in code. Check if it is needed.\x1b[0m`,
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
// Capture metadata such as description from local node_modules in deep mode
|
|
287
|
-
if (deep && !pkg.description && pkg.properties) {
|
|
288
|
-
let localNodeModulesPath;
|
|
289
|
-
for (const aprop of pkg.properties) {
|
|
290
|
-
if (aprop.name === "internal:LocalNodeModulesPath") {
|
|
291
|
-
localNodeModulesPath = resolve(join(aprop.value, "package.json"));
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
if (localNodeModulesPath && safeExistsSync(localNodeModulesPath)) {
|
|
296
|
-
const lnmPkgList = await parsePkgJson(localNodeModulesPath, true);
|
|
297
|
-
if (lnmPkgList && lnmPkgList.length === 1) {
|
|
298
|
-
const lnmMetadata = lnmPkgList[0];
|
|
299
|
-
if (lnmMetadata && Object.keys(lnmMetadata).length) {
|
|
300
|
-
pkg.description = lnmMetadata.description;
|
|
301
|
-
pkg.author = lnmMetadata.author;
|
|
302
|
-
pkg.license = lnmMetadata.license;
|
|
303
|
-
pkg.homepage = lnmMetadata.homepage;
|
|
304
|
-
pkg.repository = lnmMetadata.repository;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
369
|
} // for alias
|
|
310
370
|
// Trim the properties
|
|
311
371
|
applyNpmBinCommandEvidence(pkg, allImports);
|
package/lib/ecosystems/kosi.js
CHANGED
|
@@ -10,8 +10,13 @@ import {
|
|
|
10
10
|
safeRmSync,
|
|
11
11
|
safeSpawnSync,
|
|
12
12
|
} from "../core/fs.js";
|
|
13
|
+
import { resolvePluginSourceDir } from "../core/pluginRun.js";
|
|
13
14
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
parsePurlLoose,
|
|
17
|
+
tryBuildPurl,
|
|
18
|
+
tryParsePurl,
|
|
19
|
+
} from "../inventory/purl.js";
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* kosi — JS bridge to the Kotlin-native evidence binary (thirdparty/kosi in
|
|
@@ -46,7 +51,10 @@ export function kosiWorkspaceComponent(workspaceModule) {
|
|
|
46
51
|
if (!workspaceModule?.purl) {
|
|
47
52
|
return undefined;
|
|
48
53
|
}
|
|
49
|
-
|
|
54
|
+
// The version is read through the purl parser: cutting the text after `@`
|
|
55
|
+
// kept its escaping (`1.0.0%2Bbuild`) and any `?type=jar` qualifiers.
|
|
56
|
+
const version =
|
|
57
|
+
parsePurlLoose(workspaceModule.purl)?.version || "unspecified";
|
|
50
58
|
const cleanName = (workspaceModule.name || "").replace(/^['"]|['"]+$/g, "");
|
|
51
59
|
const purl =
|
|
52
60
|
tryParsePurl(workspaceModule.purl) ||
|
|
@@ -59,7 +67,7 @@ export function kosiWorkspaceComponent(workspaceModule) {
|
|
|
59
67
|
name: cleanName || workspaceModule.name,
|
|
60
68
|
version,
|
|
61
69
|
purl,
|
|
62
|
-
"bom-ref": purl,
|
|
70
|
+
"bom-ref": decodeURIComponent(purl),
|
|
63
71
|
};
|
|
64
72
|
}
|
|
65
73
|
|
|
@@ -167,14 +175,25 @@ export function analyzeKosiProject(src, options = {}) {
|
|
|
167
175
|
);
|
|
168
176
|
return undefined;
|
|
169
177
|
}
|
|
178
|
+
// cdxgen may run with its working directory already inside the project
|
|
179
|
+
// while the source argument is relative to a repository root; a plain
|
|
180
|
+
// resolve would join the two into a directory that does not exist.
|
|
181
|
+
const analysisDir = resolvePluginSourceDir(src, [
|
|
182
|
+
"pom.xml",
|
|
183
|
+
"build.gradle",
|
|
184
|
+
"build.gradle.kts",
|
|
185
|
+
"settings.gradle",
|
|
186
|
+
"settings.gradle.kts",
|
|
187
|
+
"classpath.txt",
|
|
188
|
+
]);
|
|
170
189
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "kosi-"));
|
|
171
190
|
try {
|
|
172
191
|
const outputFile = join(tempDir, "kosi-all.json");
|
|
173
|
-
const allArgs = kosiArgs(kosi,
|
|
192
|
+
const allArgs = kosiArgs(kosi, analysisDir, outputFile, "all");
|
|
174
193
|
if (DEBUG_MODE) {
|
|
175
194
|
console.log("Executing", kosi.cmd, allArgs.join(" "));
|
|
176
195
|
}
|
|
177
|
-
const report = runKosi(kosi,
|
|
196
|
+
const report = runKosi(kosi, analysisDir, allArgs, outputFile, options);
|
|
178
197
|
if (!report) {
|
|
179
198
|
return undefined;
|
|
180
199
|
}
|
|
@@ -184,10 +203,15 @@ export function analyzeKosiProject(src, options = {}) {
|
|
|
184
203
|
// all-pass's. Losing it would silently drop the filtered evidence,
|
|
185
204
|
// so a failure here names itself but keeps the all-pass evidence.
|
|
186
205
|
const reachableFile = join(tempDir, "kosi-reachable.json");
|
|
187
|
-
const reachableArgs = kosiArgs(
|
|
206
|
+
const reachableArgs = kosiArgs(
|
|
207
|
+
kosi,
|
|
208
|
+
analysisDir,
|
|
209
|
+
reachableFile,
|
|
210
|
+
"reachable",
|
|
211
|
+
);
|
|
188
212
|
const reachableReport = runKosi(
|
|
189
213
|
kosi,
|
|
190
|
-
|
|
214
|
+
analysisDir,
|
|
191
215
|
reachableArgs,
|
|
192
216
|
reachableFile,
|
|
193
217
|
options,
|
|
@@ -2,8 +2,11 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
5
|
+
import { clean } from "semver";
|
|
5
6
|
|
|
6
7
|
import { safeExistsSync } from "../core/fs.js";
|
|
8
|
+
import { parsePurlLoose } from "../inventory/purl.js";
|
|
9
|
+
import { spdxLicenses } from "../inventory/spdx.js";
|
|
7
10
|
import { parseNpmrc, parseNpmrcFromEnv } from "../parsers/npmrc.js";
|
|
8
11
|
|
|
9
12
|
const npmPackageHydrationFields = [
|
|
@@ -17,6 +20,9 @@ const npmPackageHydrationFields = [
|
|
|
17
20
|
"homepage",
|
|
18
21
|
"keywords",
|
|
19
22
|
"license",
|
|
23
|
+
// Legacy plural form kept by long-lived packages such as fuzzy 0.1.3: the
|
|
24
|
+
// manifest carries `licenses` instead of `license` (issue 4466).
|
|
25
|
+
"licenses",
|
|
20
26
|
"repository",
|
|
21
27
|
];
|
|
22
28
|
|
|
@@ -84,6 +90,89 @@ export function setNpmPeerProperty(pkg) {
|
|
|
84
90
|
addComponentProperty(pkg, "cdx:npm:package:peer", "true");
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Marks an npm component as imported only for its TypeScript types.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} pkg Component object to annotate
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
export function setNpmTypeOnlyProperty(pkg) {
|
|
100
|
+
addComponentProperty(pkg, "cdx:npm:package:type-only", "true");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clean an npm version the way `npm publish` does before it records a release.
|
|
105
|
+
*
|
|
106
|
+
* Publishing a directory runs the manifest through `npm pkg fix`
|
|
107
|
+
* (`fixVersionField` in `@npmcli/package-json`, which calls `semver.clean` in
|
|
108
|
+
* loose mode) and then through libnpmpublish, which calls `semver.clean` again
|
|
109
|
+
* in strict mode and records the result as the packument version, the
|
|
110
|
+
* dist-tag, the `_id` and the tarball file name. Semver build metadata
|
|
111
|
+
* (`1.0.0+build.5`), a leading `v` or `=`, surrounding whitespace and leading
|
|
112
|
+
* zeros are dropped; a prerelease is kept.
|
|
113
|
+
*
|
|
114
|
+
* The `package.json` inside the published tarball keeps the original string,
|
|
115
|
+
* and so do the lockfiles npm, pnpm, yarn and bun write for a project, a
|
|
116
|
+
* workspace member or a git, file or link dependency. The cleaned form is the
|
|
117
|
+
* version the npm registry knows the package by.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} version Version as a manifest or lockfile records it
|
|
120
|
+
* @returns {string|null} The cleaned version, or null when npm would reject `version` as invalid semver
|
|
121
|
+
*/
|
|
122
|
+
export function cleanNpmVersion(version) {
|
|
123
|
+
if (typeof version !== "string" || !version.trim()) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const looseClean = clean(version, { loose: true });
|
|
127
|
+
if (!looseClean) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return clean(looseClean);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Record the version npm would publish when it differs from the recorded one.
|
|
135
|
+
*
|
|
136
|
+
* Sets `cdx:npm:cleanedVersion` on an npm component whose version is valid
|
|
137
|
+
* semver but not in the form {@link cleanNpmVersion} produces, for example
|
|
138
|
+
* `1.0.0+build.5` (published as `1.0.0`). The component's `version` and `purl`
|
|
139
|
+
* keep the recorded string: that is what the manifest and lockfile say. A
|
|
140
|
+
* component whose version is already clean, or not semver at all (a git
|
|
141
|
+
* commit, a `file:` path), is left without the property, and a stale value from
|
|
142
|
+
* an earlier pass is replaced.
|
|
143
|
+
*
|
|
144
|
+
* @param {object} component Component or package object with `version` and `purl`
|
|
145
|
+
* @returns {object} The same component, for chaining
|
|
146
|
+
*/
|
|
147
|
+
export function setNpmCleanedVersionProperty(component) {
|
|
148
|
+
if (!component || typeof component !== "object") {
|
|
149
|
+
return component;
|
|
150
|
+
}
|
|
151
|
+
const purl = component.purl;
|
|
152
|
+
if (typeof purl !== "string" || !purl.startsWith("pkg:npm/")) {
|
|
153
|
+
return component;
|
|
154
|
+
}
|
|
155
|
+
const cleaned = cleanNpmVersion(component.version);
|
|
156
|
+
const wanted = cleaned && cleaned !== component.version ? cleaned : undefined;
|
|
157
|
+
const existing = component.properties?.filter(
|
|
158
|
+
(property) => property?.name === "cdx:npm:cleanedVersion",
|
|
159
|
+
);
|
|
160
|
+
if (
|
|
161
|
+
(!existing?.length && !wanted) ||
|
|
162
|
+
(existing?.length === 1 && existing[0].value === wanted)
|
|
163
|
+
) {
|
|
164
|
+
return component;
|
|
165
|
+
}
|
|
166
|
+
const properties = (component.properties || []).filter(
|
|
167
|
+
(property) => property?.name !== "cdx:npm:cleanedVersion",
|
|
168
|
+
);
|
|
169
|
+
if (wanted) {
|
|
170
|
+
properties.push({ name: "cdx:npm:cleanedVersion", value: wanted });
|
|
171
|
+
}
|
|
172
|
+
component.properties = properties;
|
|
173
|
+
return component;
|
|
174
|
+
}
|
|
175
|
+
|
|
87
176
|
/**
|
|
88
177
|
* Helper function to create a properly encoded workspace PURL
|
|
89
178
|
*
|
|
@@ -130,14 +219,18 @@ export function findMatchingNpmWorkspace(workspacePackages, packageName) {
|
|
|
130
219
|
return undefined;
|
|
131
220
|
}
|
|
132
221
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
222
|
+
// Compare the parsed coordinates: a prefix match on the purl text would let
|
|
223
|
+
// `ws` match `wsmember`, and the reference may be escaped or decoded.
|
|
224
|
+
return workspacePackages.find((workspacePackage) => {
|
|
225
|
+
const parts = parsePurlLoose(workspacePackage);
|
|
226
|
+
if (parts?.type !== "npm") {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
const fullName = parts.namespace
|
|
230
|
+
? `${parts.namespace}/${parts.name}`
|
|
231
|
+
: parts.name;
|
|
232
|
+
return fullName === packageName;
|
|
233
|
+
});
|
|
141
234
|
}
|
|
142
235
|
|
|
143
236
|
/**
|
|
@@ -201,6 +294,12 @@ export function collectNpmManifestSources(node) {
|
|
|
201
294
|
return manifestSources;
|
|
202
295
|
}
|
|
203
296
|
for (const edge of node.edgesIn) {
|
|
297
|
+
// Arborist adds a `workspace` edge from the root to every member, with an
|
|
298
|
+
// absolute `file:` spec it builds from the checkout path. No manifest
|
|
299
|
+
// declares it, and copying it would put the local path into the BOM.
|
|
300
|
+
if (edge?.workspace || edge?.type === "workspace") {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
204
303
|
const manifestSource = classifyNpmManifestSource(edge?.spec);
|
|
205
304
|
if (!manifestSource) {
|
|
206
305
|
continue;
|
|
@@ -215,6 +314,66 @@ export function collectNpmManifestSources(node) {
|
|
|
215
314
|
return manifestSources;
|
|
216
315
|
}
|
|
217
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Whether a value of the legacy `licenses` field names a license: a non-empty
|
|
319
|
+
* string, or an object with a non-empty `type`.
|
|
320
|
+
*
|
|
321
|
+
* @param {*} entry One value of the legacy field
|
|
322
|
+
* @returns {boolean} True when the entry names a license
|
|
323
|
+
*/
|
|
324
|
+
function isLegacyNpmLicenseEntry(entry) {
|
|
325
|
+
if (typeof entry === "string") {
|
|
326
|
+
return entry.trim() !== "";
|
|
327
|
+
}
|
|
328
|
+
return typeof entry?.type === "string" && entry.type.trim() !== "";
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Resolve the license of an npm-style manifest, honouring the legacy plural
|
|
333
|
+
* `licenses` field that packages published before the SPDX convention still
|
|
334
|
+
* carry (issue 4466). A modern `license` value is returned unchanged. The
|
|
335
|
+
* legacy field may hold one entry or an array of them, each a string or a
|
|
336
|
+
* `{ type, url }` object; one entry is returned as it is. npm documents an
|
|
337
|
+
* array of several entries as a choice between them, so when every entry is an
|
|
338
|
+
* SPDX identifier the array becomes the equivalent `MIT OR Apache-2.0`
|
|
339
|
+
* expression, and otherwise stays a list for `getLicenses` to convert entry by
|
|
340
|
+
* entry.
|
|
341
|
+
*
|
|
342
|
+
* @param {object} pkgData Parsed package.json (or bower.json) manifest
|
|
343
|
+
* @returns {string|object|Array|undefined} The effective license value
|
|
344
|
+
*/
|
|
345
|
+
export function resolveNpmLicense(pkgData) {
|
|
346
|
+
if (!pkgData || typeof pkgData !== "object") {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
const license = pkgData.license;
|
|
350
|
+
// An empty or null license is no declaration and defers to `licenses`.
|
|
351
|
+
if (
|
|
352
|
+
license !== undefined &&
|
|
353
|
+
license !== null &&
|
|
354
|
+
!(typeof license === "string" && license.trim() === "")
|
|
355
|
+
) {
|
|
356
|
+
return license;
|
|
357
|
+
}
|
|
358
|
+
const legacyEntries = (
|
|
359
|
+
Array.isArray(pkgData.licenses) ? pkgData.licenses : [pkgData.licenses]
|
|
360
|
+
).filter(isLegacyNpmLicenseEntry);
|
|
361
|
+
if (legacyEntries.length <= 1) {
|
|
362
|
+
return legacyEntries[0];
|
|
363
|
+
}
|
|
364
|
+
const licenseIds = [
|
|
365
|
+
...new Set(
|
|
366
|
+
legacyEntries.map((entry) =>
|
|
367
|
+
(typeof entry === "string" ? entry : entry.type).trim(),
|
|
368
|
+
),
|
|
369
|
+
),
|
|
370
|
+
];
|
|
371
|
+
if (licenseIds.every((licenseId) => spdxLicenses.includes(licenseId))) {
|
|
372
|
+
return licenseIds.join(" OR ");
|
|
373
|
+
}
|
|
374
|
+
return legacyEntries;
|
|
375
|
+
}
|
|
376
|
+
|
|
218
377
|
/**
|
|
219
378
|
* Hydrates sparse npm package metadata from the installed package.json in deep mode.
|
|
220
379
|
* Existing metadata on the Arborist node wins over on-disk values.
|
|
@@ -520,7 +679,9 @@ export function parsePnpmGitLockKey(lockKey) {
|
|
|
520
679
|
* @returns {Object<string, object>} Map of lookup key to git package reference.
|
|
521
680
|
*/
|
|
522
681
|
export function buildPnpmGitPkgRefs(packages, snapshots, npmrcConfig = {}) {
|
|
523
|
-
|
|
682
|
+
// A null prototype keeps inherited Object properties (such as `constructor`)
|
|
683
|
+
// from masquerading as lookup hits for keys that were never registered.
|
|
684
|
+
const gitPkgRefs = Object.create(null);
|
|
524
685
|
const registerEntry = (lockKey, packageNode) => {
|
|
525
686
|
const resolution = packageNode?.resolution;
|
|
526
687
|
const parsed = parsePnpmGitLockKey(lockKey);
|
|
@@ -531,6 +692,10 @@ export function buildPnpmGitPkgRefs(packages, snapshots, npmrcConfig = {}) {
|
|
|
531
692
|
return;
|
|
532
693
|
}
|
|
533
694
|
const { group, name, gitSpec, fullName, packageName } = parsed;
|
|
695
|
+
if (name === "__proto__" || packageName === "__proto__") {
|
|
696
|
+
// A crafted lock key cannot become a lookup entry under this name.
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
534
699
|
const version = packageNode?.version || resolution?.commit || "";
|
|
535
700
|
const repo = resolution?.repo || "";
|
|
536
701
|
const commit = resolution?.commit || "";
|