@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
package/lib/audit/index.js
CHANGED
|
@@ -390,6 +390,27 @@ function targetSlug(target) {
|
|
|
390
390
|
return `${target.type}-${normalized || "package"}-${version}-${digest}`;
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Build a deterministic directory name for a repository checkout, keyed by
|
|
395
|
+
* the repository URL and the ref it is cloned at.
|
|
396
|
+
*
|
|
397
|
+
* Keying by the coordinates rather than the package means the packages of
|
|
398
|
+
* one monorepo share a single checkout instead of each cloning the whole
|
|
399
|
+
* repository, while a different ref, which is a different snapshot of the
|
|
400
|
+
* tree, never shares one.
|
|
401
|
+
*
|
|
402
|
+
* @param {string} repoUrl Repository URL
|
|
403
|
+
* @param {string | undefined} gitRef Ref the checkout is cloned at
|
|
404
|
+
* @returns {string} slug string
|
|
405
|
+
*/
|
|
406
|
+
function repositoryCheckoutSlug(repoUrl, gitRef) {
|
|
407
|
+
const digest = createHash("sha256")
|
|
408
|
+
.update(`${repoUrl}\n${gitRef || ""}`)
|
|
409
|
+
.digest("hex")
|
|
410
|
+
.slice(0, 16);
|
|
411
|
+
return `repo-${digest}`;
|
|
412
|
+
}
|
|
413
|
+
|
|
393
414
|
/**
|
|
394
415
|
* Ensure a parent directory exists before writing a file.
|
|
395
416
|
*
|
|
@@ -1378,7 +1399,12 @@ async function ensureCheckout(target, resolution, workspaceDir, gitRef) {
|
|
|
1378
1399
|
if (!safeExistsSync(resolvedWorkspaceDir)) {
|
|
1379
1400
|
safeMkdirSync(resolvedWorkspaceDir, { recursive: true });
|
|
1380
1401
|
}
|
|
1381
|
-
|
|
1402
|
+
// One checkout per repository and ref, so packages of the same monorepo
|
|
1403
|
+
// share it; each package still keeps its own cache and report directory.
|
|
1404
|
+
const cloneDir = join(
|
|
1405
|
+
resolvedWorkspaceDir,
|
|
1406
|
+
repositoryCheckoutSlug(resolution.repoUrl, gitRef),
|
|
1407
|
+
);
|
|
1382
1408
|
if (safeExistsSync(join(cloneDir, ".git"))) {
|
|
1383
1409
|
return {
|
|
1384
1410
|
cleanup: false,
|
|
@@ -2635,11 +2661,17 @@ export async function runAuditFromBoms(inputBoms, options) {
|
|
|
2635
2661
|
total: extractedTargets.targets.length,
|
|
2636
2662
|
type: "run:start",
|
|
2637
2663
|
});
|
|
2638
|
-
// Every target's repository lookup is known here, before the first
|
|
2639
|
-
// so they are resolved in one round rather than one per iteration
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2664
|
+
// Every target's repository lookup is known here, before the first
|
|
2665
|
+
// clone, so they are resolved in one round rather than one per iteration
|
|
2666
|
+
// below. A target whose cached child BOM already answers it is resolved
|
|
2667
|
+
// from the workspace alone, so only the targets that will be cloned take
|
|
2668
|
+
// part in the round.
|
|
2669
|
+
const purlsToResolve = extractedTargets.targets
|
|
2670
|
+
.filter((target) => !loadCachedChildBom(options.workspaceDir, target))
|
|
2671
|
+
.map((target) => target.purl);
|
|
2672
|
+
if (purlsToResolve.length) {
|
|
2673
|
+
await prefetchGitUrlSources(purlsToResolve);
|
|
2674
|
+
}
|
|
2643
2675
|
}
|
|
2644
2676
|
for (const [index, target] of extractedTargets.targets.entries()) {
|
|
2645
2677
|
const targetIndex = index + 1;
|
package/lib/cli/bomAssembly.js
CHANGED
|
@@ -25,7 +25,10 @@ import {
|
|
|
25
25
|
CDXGEN_VERSION,
|
|
26
26
|
frameworksList,
|
|
27
27
|
} from "../core/state.js";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
isPkgTypeOnlyImport,
|
|
30
|
+
setNpmCleanedVersionProperty,
|
|
31
|
+
} from "../ecosystems/npmutils.js";
|
|
29
32
|
import { parsePackageJsonName } from "../ecosystems/parsers-js.js";
|
|
30
33
|
import {
|
|
31
34
|
AI_INSTRUCTION_FILE_KINDS,
|
|
@@ -50,8 +53,7 @@ import {
|
|
|
50
53
|
import { enrichComponentWithMcpMetadata } from "../inventory/mcp.js";
|
|
51
54
|
import {
|
|
52
55
|
applyPurl,
|
|
53
|
-
|
|
54
|
-
isValidPurl,
|
|
56
|
+
canonicalPurlFromLooseString,
|
|
55
57
|
npmPurl,
|
|
56
58
|
tryBuildPurl,
|
|
57
59
|
} from "../inventory/purl.js";
|
|
@@ -182,6 +184,31 @@ export const determineParentComponent = (options) => {
|
|
|
182
184
|
return parentComponent;
|
|
183
185
|
};
|
|
184
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Tells whether a `--project-name`/`--project-version` override names the very
|
|
189
|
+
* project the generator detected, rather than a larger project containing it.
|
|
190
|
+
*
|
|
191
|
+
* The override is typed `application` because it can name anything. When its
|
|
192
|
+
* name, group, and version match the detected parent, the two describe the same
|
|
193
|
+
* project, and the detected parent is the better description of it: it carries
|
|
194
|
+
* the ecosystem purl and the manifest metadata. Treating it as a subproject
|
|
195
|
+
* would list the project as a dependency of itself under a second purl.
|
|
196
|
+
*
|
|
197
|
+
* @param {object|undefined} overrideParent Parent built from the options
|
|
198
|
+
* @param {object|undefined} detectedParent Parent the generator derived
|
|
199
|
+
* @returns {boolean} true when both describe the same project
|
|
200
|
+
*/
|
|
201
|
+
const overrideNamesDetectedParent = (overrideParent, detectedParent) => {
|
|
202
|
+
if (!overrideParent || !detectedParent?.purl) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return (
|
|
206
|
+
overrideParent.name === detectedParent.name &&
|
|
207
|
+
(overrideParent.group || "") === (detectedParent.group || "") &&
|
|
208
|
+
`${overrideParent.version}` === `${detectedParent.version}`
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
185
212
|
/**
|
|
186
213
|
* Re-anchor a dependency graph onto a caller-supplied parent component.
|
|
187
214
|
*
|
|
@@ -338,7 +365,8 @@ export const componentToSimpleFullName = (comp) => {
|
|
|
338
365
|
/**
|
|
339
366
|
* Strips transient keys (`evidence`, `_integrity`, `license`, `qualifiers`,
|
|
340
367
|
* `repository`, `homepage`) from a parent component while preserving licenses
|
|
341
|
-
* and external references (bug #1519).
|
|
368
|
+
* and external references (bug #1519). External references whose URL is not a
|
|
369
|
+
* valid IRI reference are dropped.
|
|
342
370
|
*
|
|
343
371
|
* @param {object} comp Parent component to clean in place
|
|
344
372
|
* @returns {object} The cleaned parent component
|
|
@@ -356,10 +384,15 @@ export const cleanParentComponent = (comp) => {
|
|
|
356
384
|
}
|
|
357
385
|
delete comp.license;
|
|
358
386
|
delete comp.qualifiers;
|
|
359
|
-
|
|
387
|
+
// Components get their references validated in listComponents. The parent
|
|
388
|
+
// skips that conversion, so validate here too: an unusable URL taken from a
|
|
389
|
+
// manifest must not make the whole document invalid.
|
|
390
|
+
if (comp.repository || comp.homepage || comp.externalReferences?.length) {
|
|
360
391
|
const externalReferences = addExternalReferences(comp);
|
|
361
392
|
if (externalReferences?.length) {
|
|
362
393
|
comp.externalReferences = externalReferences;
|
|
394
|
+
} else {
|
|
395
|
+
delete comp.externalReferences;
|
|
363
396
|
}
|
|
364
397
|
}
|
|
365
398
|
delete comp.repository;
|
|
@@ -454,12 +487,18 @@ export function addMetadata(parentComponent = {}, options = {}, context = {}) {
|
|
|
454
487
|
// name-based bom-ref, and blindly copying that here manufactured invalid
|
|
455
488
|
// purls such as `"purl": "swift-smoke"`. CycloneDX requires `purl` to be
|
|
456
489
|
// a valid Package URL when present, so absent is the only correct answer.
|
|
490
|
+
// A bom-ref holds the decoded purl (`pkg:npm/app@1.0.0+build`), so it is
|
|
491
|
+
// split on its separators and rebuilt rather than escaped wholesale,
|
|
492
|
+
// which would also escape the `@` before the version.
|
|
457
493
|
if (!parentComponent["purl"] && parentComponent["bom-ref"]) {
|
|
458
|
-
const candidate =
|
|
459
|
-
|
|
494
|
+
const candidate = canonicalPurlFromLooseString(
|
|
495
|
+
parentComponent["bom-ref"],
|
|
496
|
+
);
|
|
497
|
+
if (candidate) {
|
|
460
498
|
parentComponent["purl"] = candidate;
|
|
461
499
|
}
|
|
462
500
|
}
|
|
501
|
+
setNpmCleanedVersionProperty(parentComponent);
|
|
463
502
|
}
|
|
464
503
|
if (parentComponent?.components) {
|
|
465
504
|
parentComponent.components = listComponents(
|
|
@@ -841,6 +880,7 @@ export function addExternalReferences(opkg) {
|
|
|
841
880
|
}
|
|
842
881
|
}
|
|
843
882
|
return externalReferences
|
|
883
|
+
.filter((reference) => typeof reference?.url === "string")
|
|
844
884
|
.map((reference) => ({ ...reference, url: reference.url.trim() }))
|
|
845
885
|
.filter((reference) => isValidIriReference(reference.url));
|
|
846
886
|
}
|
|
@@ -1014,13 +1054,16 @@ export function addComponent(
|
|
|
1014
1054
|
let purl = pkg.purl;
|
|
1015
1055
|
if (!purl && ptype) {
|
|
1016
1056
|
try {
|
|
1057
|
+
// Raw values: cdx-purl escapes each component itself, so escaping
|
|
1058
|
+
// them here first turned `@scope` into `%2540scope` and
|
|
1059
|
+
// `libstdc++6` into `libstdc%252B%252B6`.
|
|
1017
1060
|
purl = new Purl({
|
|
1018
1061
|
type: ptype,
|
|
1019
|
-
namespace:
|
|
1020
|
-
name
|
|
1062
|
+
namespace: group || null,
|
|
1063
|
+
name,
|
|
1021
1064
|
version: version || null,
|
|
1022
1065
|
qualifiers: pkg.qualifiers || null,
|
|
1023
|
-
subpath:
|
|
1066
|
+
subpath: pkg.subpath || null,
|
|
1024
1067
|
});
|
|
1025
1068
|
} catch {
|
|
1026
1069
|
// purl construction may fail for types requiring a namespace (maven,
|
|
@@ -1071,9 +1114,10 @@ export function addComponent(
|
|
|
1071
1114
|
} else {
|
|
1072
1115
|
compScope = "required";
|
|
1073
1116
|
}
|
|
1074
|
-
} else if (impPkgs.length && compScope !== "excluded") {
|
|
1075
|
-
compScope = "optional";
|
|
1076
1117
|
}
|
|
1118
|
+
// A package missing from the imports keeps its scope. Usage analysis
|
|
1119
|
+
// only promotes: the ecosystem passes have already scoped the package
|
|
1120
|
+
// list, and a missing import is not evidence that a package is optional.
|
|
1077
1121
|
}
|
|
1078
1122
|
let component = {
|
|
1079
1123
|
author,
|
|
@@ -1089,6 +1133,11 @@ export function addComponent(
|
|
|
1089
1133
|
purl: purlString,
|
|
1090
1134
|
externalReferences: addExternalReferences(pkg),
|
|
1091
1135
|
};
|
|
1136
|
+
// A CPE from the collector (caxa records one for the bundled Node.js
|
|
1137
|
+
// runtime) is how NVD-based scanners match the component.
|
|
1138
|
+
if (typeof pkg.cpe === "string" && pkg.cpe.length) {
|
|
1139
|
+
component.cpe = pkg.cpe;
|
|
1140
|
+
}
|
|
1092
1141
|
if (options.specVersion >= 1.5) {
|
|
1093
1142
|
component.pedigree = pkg.pedigree || undefined;
|
|
1094
1143
|
}
|
|
@@ -1102,6 +1151,13 @@ export function addComponent(
|
|
|
1102
1151
|
// than bundled with the product. The schema defaults it to false, so it
|
|
1103
1152
|
// is only worth emitting when true.
|
|
1104
1153
|
component.isExternal = pkg.isExternal ? true : undefined;
|
|
1154
|
+
// A range describes an external dependency named without a resolved
|
|
1155
|
+
// version, such as the Ruby a gem requires. The schema allows it only
|
|
1156
|
+
// on an external component, and never next to a version.
|
|
1157
|
+
component.versionRange =
|
|
1158
|
+
pkg.isExternal && !pkg.version
|
|
1159
|
+
? pkg.versionRange || undefined
|
|
1160
|
+
: undefined;
|
|
1105
1161
|
}
|
|
1106
1162
|
component["type"] = determinePackageType(pkg);
|
|
1107
1163
|
// `version` is optional in every spec version cdxgen emits. An empty string
|
|
@@ -1131,9 +1187,16 @@ export function addComponent(
|
|
|
1131
1187
|
if (options.specVersion >= 1.6 && component.author) {
|
|
1132
1188
|
const authorsList = [];
|
|
1133
1189
|
for (const aauthor of component.author.split(",")) {
|
|
1134
|
-
|
|
1190
|
+
// Registries hand out comma separated lists without a consistent
|
|
1191
|
+
// spacing, so `A, B` and `A,B` must produce the same names
|
|
1192
|
+
const name = aauthor.trim();
|
|
1193
|
+
if (name.length) {
|
|
1194
|
+
authorsList.push({ name });
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (authorsList.length) {
|
|
1198
|
+
component.authors = authorsList;
|
|
1135
1199
|
}
|
|
1136
|
-
component.authors = authorsList;
|
|
1137
1200
|
delete component.author;
|
|
1138
1201
|
}
|
|
1139
1202
|
// Downgrade authors section for < 1.5 :(
|
|
@@ -1169,6 +1232,7 @@ export function addComponent(
|
|
|
1169
1232
|
if (pkg.properties?.length) {
|
|
1170
1233
|
component.properties = pkg.properties;
|
|
1171
1234
|
}
|
|
1235
|
+
setNpmCleanedVersionProperty(component);
|
|
1172
1236
|
if (isTypeOnlyPkg) {
|
|
1173
1237
|
component.properties = component.properties || [];
|
|
1174
1238
|
if (
|
|
@@ -1285,6 +1349,25 @@ export function determinePackageType(pkg) {
|
|
|
1285
1349
|
) {
|
|
1286
1350
|
return "library";
|
|
1287
1351
|
}
|
|
1352
|
+
// A gem is a framework only when the curated list names that very gem:
|
|
1353
|
+
// the prefix matches below took rails-html-sanitizer and sinatra-contrib
|
|
1354
|
+
// for their frameworks, and the bare words meant for other ecosystems
|
|
1355
|
+
// turned childprocess ("chi") and jquery-rails ("jquery") into
|
|
1356
|
+
// frameworks. Any other gem is a library, unless it ships executables,
|
|
1357
|
+
// which makes it an application (discussions 4405 and 4408). A
|
|
1358
|
+
// description or keyword that merely mentions a framework is no evidence
|
|
1359
|
+
// of one: every gem whose summary says "tracing framework" would
|
|
1360
|
+
// otherwise become a framework component.
|
|
1361
|
+
if (purl.type === "gem") {
|
|
1362
|
+
if (frameworksList.all.includes(`pkg:gem/${purl.name}`)) {
|
|
1363
|
+
return "framework";
|
|
1364
|
+
}
|
|
1365
|
+
const hasExecutables = pkg.properties?.some(
|
|
1366
|
+
(prop) =>
|
|
1367
|
+
prop.name === "cdx:gem:executables" && prop.value?.trim().length,
|
|
1368
|
+
);
|
|
1369
|
+
return hasExecutables ? "application" : "library";
|
|
1370
|
+
}
|
|
1288
1371
|
for (const cf of frameworksList.all) {
|
|
1289
1372
|
if (
|
|
1290
1373
|
pkg.purl.startsWith(cf) ||
|
|
@@ -1466,7 +1549,14 @@ export const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1466
1549
|
}
|
|
1467
1550
|
const nsMapping = context.nsMapping || {};
|
|
1468
1551
|
const services = context.services || [];
|
|
1469
|
-
|
|
1552
|
+
let overrideParent = determineParentComponent(options);
|
|
1553
|
+
// A caller-supplied parentComponent is always honoured as given
|
|
1554
|
+
if (
|
|
1555
|
+
overrideParent !== options.parentComponent &&
|
|
1556
|
+
overrideNamesDetectedParent(overrideParent, context.parentComponent)
|
|
1557
|
+
) {
|
|
1558
|
+
overrideParent = undefined;
|
|
1559
|
+
}
|
|
1470
1560
|
const parentComponent = overrideParent || context.parentComponent;
|
|
1471
1561
|
const metadata = addMetadata(parentComponent, options, context);
|
|
1472
1562
|
const components = listComponents(options, allImports, pkgInfo, ptype);
|
package/lib/cli/cliOptions.js
CHANGED
|
@@ -15,6 +15,10 @@ import { join, resolve } from "node:path";
|
|
|
15
15
|
|
|
16
16
|
import { isSecureMode, readEnvironmentVariable } from "../core/activity.js";
|
|
17
17
|
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
18
|
+
import {
|
|
19
|
+
METADATA_PROPERTY_CONFIG_KEYS,
|
|
20
|
+
parseMetadataProperties,
|
|
21
|
+
} from "../core/metadataProperties.js";
|
|
18
22
|
import {
|
|
19
23
|
DEFAULT_CDX_SPEC_VERSION,
|
|
20
24
|
normalizeCycloneDxComponentTypeFilter,
|
|
@@ -120,6 +124,80 @@ export function isUserProvided(value, yaDefault) {
|
|
|
120
124
|
return value !== undefined && value !== yaDefault;
|
|
121
125
|
}
|
|
122
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Combine the custom metadata properties of every source (discussion 4391):
|
|
129
|
+
* the `--metadata-property` flag, the CDXGEN_METADATA_PROPERTY and
|
|
130
|
+
* CDXGEN_METADATA_PROPERTIES environment variables, and a config file's
|
|
131
|
+
* `metadata-property` or `metadataProperties` entry.
|
|
132
|
+
*
|
|
133
|
+
* yargs would let the highest-precedence source replace the others outright,
|
|
134
|
+
* so each is read on its own and they are merged instead. A name set by more
|
|
135
|
+
* than one source takes its values from the one that wins in the usual order
|
|
136
|
+
* — the command line, then the environment, then the config file — while a
|
|
137
|
+
* source may repeat a name for a multi-valued property.
|
|
138
|
+
*
|
|
139
|
+
* @param {object} args Parsed yargs argv
|
|
140
|
+
* @param {object} [context]
|
|
141
|
+
* @param {string[]} [context.argv] Raw command-line arguments, which tell a
|
|
142
|
+
* flag on the command line from one yargs filled in from elsewhere
|
|
143
|
+
* @param {object} [context.projectConfig] The sanitized config file contents.
|
|
144
|
+
* Without it, a programmatic caller's `args` are taken as the config.
|
|
145
|
+
* @returns {{name: string, value: string}[]} The merged properties
|
|
146
|
+
*/
|
|
147
|
+
export function resolveMetadataProperties(
|
|
148
|
+
args,
|
|
149
|
+
{ argv = process.argv, projectConfig } = {},
|
|
150
|
+
) {
|
|
151
|
+
const flagGiven = argv.some((argument) =>
|
|
152
|
+
["--metadata-property", "--metadataProperty"].includes(
|
|
153
|
+
`${argument}`.split("=")[0],
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
const fromConfig = projectConfig
|
|
157
|
+
? METADATA_PROPERTY_CONFIG_KEYS.flatMap((key) =>
|
|
158
|
+
parseMetadataProperties(projectConfig[key]),
|
|
159
|
+
)
|
|
160
|
+
: flagGiven
|
|
161
|
+
? []
|
|
162
|
+
: [
|
|
163
|
+
...parseMetadataProperties(args?.metadataProperty),
|
|
164
|
+
...parseMetadataProperties(args?.metadataProperties),
|
|
165
|
+
];
|
|
166
|
+
const sources = [
|
|
167
|
+
flagGiven ? parseMetadataProperties(args?.metadataProperty) : [],
|
|
168
|
+
[
|
|
169
|
+
...parseMetadataProperties(
|
|
170
|
+
readEnvironmentVariable("CDXGEN_METADATA_PROPERTY"),
|
|
171
|
+
),
|
|
172
|
+
...parseMetadataProperties(
|
|
173
|
+
readEnvironmentVariable("CDXGEN_METADATA_PROPERTIES"),
|
|
174
|
+
),
|
|
175
|
+
],
|
|
176
|
+
fromConfig,
|
|
177
|
+
];
|
|
178
|
+
const properties = [];
|
|
179
|
+
const claimedNames = new Set();
|
|
180
|
+
for (const source of sources) {
|
|
181
|
+
const sourceNames = new Set();
|
|
182
|
+
for (const aprop of source) {
|
|
183
|
+
if (
|
|
184
|
+
claimedNames.has(aprop.name) ||
|
|
185
|
+
properties.some(
|
|
186
|
+
(prop) => prop.name === aprop.name && prop.value === aprop.value,
|
|
187
|
+
)
|
|
188
|
+
) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
properties.push(aprop);
|
|
192
|
+
sourceNames.add(aprop.name);
|
|
193
|
+
}
|
|
194
|
+
for (const name of sourceNames) {
|
|
195
|
+
claimedNames.add(name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return properties;
|
|
199
|
+
}
|
|
200
|
+
|
|
123
201
|
/**
|
|
124
202
|
* Apply command-name-based defaults to the raw parsed args. When cdxgen is
|
|
125
203
|
* invoked via a symlink or alias (e.g. `obom`, `cbom`, `spdxgen`, `aibom`),
|
|
@@ -162,6 +240,8 @@ export function applyCommandNameDefaults(args, invokedCommandName) {
|
|
|
162
240
|
* @param {object} context
|
|
163
241
|
* @param {string} context.filePath Resolved source path
|
|
164
242
|
* @param {boolean} context.isRemoteOrPurl True when source is a URL or purl
|
|
243
|
+
* @param {string[]} [context.argv] Raw command-line arguments
|
|
244
|
+
* @param {object} [context.projectConfig] Sanitized config file contents
|
|
165
245
|
* @returns {object} The initial options object
|
|
166
246
|
*/
|
|
167
247
|
/**
|
|
@@ -221,8 +301,12 @@ function resolveIntrospectOption(args, argv) {
|
|
|
221
301
|
|
|
222
302
|
export function buildInitialOptions(
|
|
223
303
|
args,
|
|
224
|
-
{ filePath, isRemoteOrPurl, argv = process.argv },
|
|
304
|
+
{ filePath, isRemoteOrPurl, argv = process.argv, projectConfig },
|
|
225
305
|
) {
|
|
306
|
+
const metadataProperties = resolveMetadataProperties(args, {
|
|
307
|
+
argv,
|
|
308
|
+
projectConfig,
|
|
309
|
+
});
|
|
226
310
|
const res = Object.assign({}, args, {
|
|
227
311
|
projectType: args.type,
|
|
228
312
|
multiProject: args.recurse,
|
|
@@ -235,6 +319,9 @@ export function buildInitialOptions(
|
|
|
235
319
|
include: args.include || args.includeRegex,
|
|
236
320
|
noIgnore: args.noIgnore,
|
|
237
321
|
gitAiNotesRef: args.gitAiNotesRef || "refs/notes/ai",
|
|
322
|
+
metadataProperties: metadataProperties.length
|
|
323
|
+
? metadataProperties
|
|
324
|
+
: undefined,
|
|
238
325
|
// Introspection: the flag or the documented environment opt-in. An
|
|
239
326
|
// explicit --no-introspect stays `false` and beats both, while an
|
|
240
327
|
// unmentioned flag stays undefined so a profile can still enable it.
|
|
@@ -598,6 +685,9 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
598
685
|
* passed `--spec-version`
|
|
599
686
|
* @param {boolean} [context.isDryRun=false] Dry-run mode
|
|
600
687
|
* @param {boolean} [context.isSecureMode] Secure mode
|
|
688
|
+
* @param {object} [context.projectConfig] Sanitized config file contents,
|
|
689
|
+
* whose metadata properties merge
|
|
690
|
+
* with the other sources'
|
|
601
691
|
* @returns {{ options: object, warnings: OptionsWarning[] }}
|
|
602
692
|
*/
|
|
603
693
|
export function buildOptionsFromArgs(args, context) {
|
|
@@ -607,12 +697,14 @@ export function buildOptionsFromArgs(args, context) {
|
|
|
607
697
|
isRemoteOrPurl,
|
|
608
698
|
userSetSpecVersion,
|
|
609
699
|
isDryRun = false,
|
|
700
|
+
projectConfig,
|
|
610
701
|
} = context;
|
|
611
702
|
|
|
612
703
|
const expandedArgs = applyCommandNameDefaults(args, invokedCommandName);
|
|
613
704
|
const options = buildInitialOptions(expandedArgs, {
|
|
614
705
|
filePath,
|
|
615
706
|
isRemoteOrPurl,
|
|
707
|
+
projectConfig,
|
|
616
708
|
});
|
|
617
709
|
const phase3Warnings = applyPostConstructionOverrides(options, {
|
|
618
710
|
invokedCommandName,
|