@cdxgen/cdxgen 13.3.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 +9 -6
- package/bin/cdxgen.js +90 -26
- package/bin/evinse.js +18 -1
- package/bin/repl.js +86 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +61 -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/index.js +113 -54
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +665 -138
- package/lib/cli/managedBom.js +320 -200
- package/lib/cli/nativeBom.js +139 -30
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +17 -9
- package/lib/core/fs.js +3 -2
- package/lib/core/httpClient.js +159 -24
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- 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 +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +314 -30
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +567 -151
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +324 -61
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +182 -55
- package/lib/ecosystems/parsers-python.js +141 -10
- package/lib/ecosystems/parsers-rust.js +4 -4
- 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 +67 -17
- package/lib/ecosystems/rusi.js +22 -9
- 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 +578 -69
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +171 -48
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/dosai.js +248 -44
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +52 -6
- package/lib/inventory/evidenceUtils.js +154 -109
- package/lib/inventory/fetchBatch.js +574 -38
- 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/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +8 -8
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +88 -7
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/docker.js +33 -32
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- 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 +15 -1
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +21 -21
- package/types/lib/audit/index.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.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/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/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.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/cmakeResolver.d.ts +5 -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 +18 -2
- 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 +50 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +15 -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 +44 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- 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 +3 -1
- 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-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 +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- 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 +75 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- 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 +82 -10
- 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/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/dosai.d.ts +57 -8
- 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/rustFormulationParser.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 +12 -0
- 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/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -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 +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- 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/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/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/lib/cli/managedBom.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
hasAnyProjectType,
|
|
14
14
|
isFeatureEnabled,
|
|
15
15
|
shouldFetchLicense,
|
|
16
|
+
shouldFetchPackageMetadata,
|
|
16
17
|
} from "../core/env.js";
|
|
17
18
|
import {
|
|
18
19
|
checksumFile,
|
|
@@ -33,14 +34,18 @@ import {
|
|
|
33
34
|
parseBdistMetadata,
|
|
34
35
|
} from "../ecosystems/ecosystems.js";
|
|
35
36
|
import {
|
|
37
|
+
agreedNugetVersions,
|
|
38
|
+
dotnetManifestProjectDir,
|
|
36
39
|
getCentralPackageVersions,
|
|
37
|
-
|
|
40
|
+
indexPropsProperties,
|
|
38
41
|
parseCsPkgData,
|
|
39
42
|
parseCsPkgLockData,
|
|
40
43
|
parseCsProjAssetsData,
|
|
41
44
|
parseCsProjData,
|
|
42
45
|
parseNupkg,
|
|
43
46
|
parsePaketLockData,
|
|
47
|
+
projectPropertyCandidates,
|
|
48
|
+
resolveInstalledNugetVersion,
|
|
44
49
|
} from "../ecosystems/parsers-dotnet.js";
|
|
45
50
|
import { parseMinJs } from "../ecosystems/parsers-js.js";
|
|
46
51
|
import {
|
|
@@ -52,7 +57,9 @@ import {
|
|
|
52
57
|
} from "../ecosystems/parsers-misc.js";
|
|
53
58
|
import { parseCpanSnapshot } from "../ecosystems/parsers-perl.js";
|
|
54
59
|
import {
|
|
60
|
+
enrichFromInstalledMetadata,
|
|
55
61
|
generatePixiLockFile,
|
|
62
|
+
installedMetadataKey,
|
|
56
63
|
parseMojoProject,
|
|
57
64
|
parsePiplockData,
|
|
58
65
|
parsePixiLockFile,
|
|
@@ -63,7 +70,7 @@ import {
|
|
|
63
70
|
parseSetupPyFile,
|
|
64
71
|
} from "../ecosystems/parsers-python.js";
|
|
65
72
|
import { parseRenvLock } from "../ecosystems/parsers-r.js";
|
|
66
|
-
import {
|
|
73
|
+
import { parseTerraformWorkspace } from "../ecosystems/parsers-terraform.js";
|
|
67
74
|
import { collectEmbeddedSboms } from "../ecosystems/pep770.js";
|
|
68
75
|
import { isPyLockFile } from "../ecosystems/pylockutils.js";
|
|
69
76
|
import {
|
|
@@ -82,6 +89,7 @@ import {
|
|
|
82
89
|
parseGemfileLockData,
|
|
83
90
|
parseGemspecData,
|
|
84
91
|
} from "../ecosystems/rubyutils.js";
|
|
92
|
+
import { getTerraformRegistryMetadata } from "../ecosystems/terraformRegistry.js";
|
|
85
93
|
import {
|
|
86
94
|
collectAiInventory,
|
|
87
95
|
summarizeAiInventory,
|
|
@@ -105,6 +113,7 @@ import {
|
|
|
105
113
|
collectDosaiServiceComponents,
|
|
106
114
|
collectDosaiServicesFromMethods,
|
|
107
115
|
createDosaiMethodsSlice,
|
|
116
|
+
dosaiRunWasStopped,
|
|
108
117
|
isDosaiDotnetLanguage,
|
|
109
118
|
normalizeDosaiServiceMap,
|
|
110
119
|
readDosaiMethodsReport,
|
|
@@ -404,6 +413,17 @@ export async function createPythonBom(path, options) {
|
|
|
404
413
|
!pyLockFiles?.length;
|
|
405
414
|
const poetryMode = poetryFiles?.length;
|
|
406
415
|
|
|
416
|
+
// The installed dist-info METADATA files describe the distributions this
|
|
417
|
+
// machine holds. They are read once, before any lockfile is parsed, so the
|
|
418
|
+
// lockfile entries they match are enriched before PyPI is asked and the
|
|
419
|
+
// registry is left only what the installations do not answer.
|
|
420
|
+
const installedLists = (metadataFiles || []).map((mf) =>
|
|
421
|
+
parseBdistMetadata(mf),
|
|
422
|
+
);
|
|
423
|
+
const installedMetadata = installedLists
|
|
424
|
+
.map((dlist) => dlist?.[0])
|
|
425
|
+
.filter((md) => md?.name);
|
|
426
|
+
|
|
407
427
|
// TODO: Support for nested directories
|
|
408
428
|
const setupPy = join(path, "setup.py");
|
|
409
429
|
const setupPyMode = safeExistsSync(setupPy);
|
|
@@ -416,7 +436,9 @@ export async function createPythonBom(path, options) {
|
|
|
416
436
|
if (DEBUG_MODE) {
|
|
417
437
|
console.log(`Parsing ${f}`);
|
|
418
438
|
}
|
|
419
|
-
let retMap = await parsePyLockData(lockData, f
|
|
439
|
+
let retMap = await parsePyLockData(lockData, f, undefined, {
|
|
440
|
+
installedMetadata,
|
|
441
|
+
});
|
|
420
442
|
if (retMap?.workspaceWarningShown) {
|
|
421
443
|
deferFailOnError(options, {
|
|
422
444
|
ecosystem: "python",
|
|
@@ -508,7 +530,11 @@ export async function createPythonBom(path, options) {
|
|
|
508
530
|
`exported-${basename(basePath)}-reqs.txt`,
|
|
509
531
|
);
|
|
510
532
|
safeWriteSync(tmpReqFile, exportedReqs);
|
|
511
|
-
const dlist = await parseReqFile(
|
|
533
|
+
const dlist = await parseReqFile(
|
|
534
|
+
tmpReqFile,
|
|
535
|
+
false,
|
|
536
|
+
installedMetadata,
|
|
537
|
+
);
|
|
512
538
|
if (dlist?.length) {
|
|
513
539
|
pkgList = pkgList.concat(dlist);
|
|
514
540
|
}
|
|
@@ -518,15 +544,6 @@ export async function createPythonBom(path, options) {
|
|
|
518
544
|
}
|
|
519
545
|
options.parentComponent = parentComponent;
|
|
520
546
|
} // poetryMode
|
|
521
|
-
if (metadataFiles?.length) {
|
|
522
|
-
// dist-info directories
|
|
523
|
-
for (const mf of metadataFiles) {
|
|
524
|
-
const dlist = parseBdistMetadata(mf);
|
|
525
|
-
if (dlist?.length) {
|
|
526
|
-
pkgList = pkgList.concat(dlist);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
547
|
// .whl files. Zip file containing dist-info directory
|
|
531
548
|
if (whlFiles?.length) {
|
|
532
549
|
for (const wf of whlFiles) {
|
|
@@ -592,7 +609,7 @@ export async function createPythonBom(path, options) {
|
|
|
592
609
|
let dlist;
|
|
593
610
|
try {
|
|
594
611
|
const lockData = JSON.parse(readFileSync(piplockFile));
|
|
595
|
-
dlist = await parsePiplockData(lockData);
|
|
612
|
+
dlist = await parsePiplockData(lockData, installedMetadata);
|
|
596
613
|
} catch (err) {
|
|
597
614
|
recordDegradation("python.lockfile-unparseable.pipenv", {
|
|
598
615
|
ecosystem: "python",
|
|
@@ -617,7 +634,7 @@ export async function createPythonBom(path, options) {
|
|
|
617
634
|
} else if (reqDirFiles?.length) {
|
|
618
635
|
for (const j in reqDirFiles) {
|
|
619
636
|
const f = reqDirFiles[j];
|
|
620
|
-
const dlist = await parseReqFile(f, false);
|
|
637
|
+
const dlist = await parseReqFile(f, false, installedMetadata);
|
|
621
638
|
if (dlist?.length) {
|
|
622
639
|
pkgList = pkgList.concat(dlist);
|
|
623
640
|
}
|
|
@@ -625,13 +642,30 @@ export async function createPythonBom(path, options) {
|
|
|
625
642
|
metadataFilename = reqDirFiles.join(", ");
|
|
626
643
|
} else if (reqFiles?.length) {
|
|
627
644
|
for (const f of reqFiles) {
|
|
628
|
-
const dlist = await parseReqFile(f, false);
|
|
645
|
+
const dlist = await parseReqFile(f, false, installedMetadata);
|
|
629
646
|
if (dlist?.length) {
|
|
630
647
|
pkgList = pkgList.concat(dlist);
|
|
631
648
|
}
|
|
632
649
|
}
|
|
633
650
|
}
|
|
634
651
|
}
|
|
652
|
+
if (metadataFiles?.length) {
|
|
653
|
+
// dist-info directories, after every manifest block has added its
|
|
654
|
+
// entries. An installation whose package a lockfile, Pipfile.lock or
|
|
655
|
+
// requirements entry already lists enriched that entry, so it is not
|
|
656
|
+
// also emitted as a component of its own; an installation nothing else
|
|
657
|
+
// covers still is one. This pass is the emission decision only: each
|
|
658
|
+
// parser enriched its own entries before it asked PyPI.
|
|
659
|
+
const enriched = enrichFromInstalledMetadata(pkgList, installedMetadata);
|
|
660
|
+
for (const dlist of installedLists) {
|
|
661
|
+
if (dlist?.length) {
|
|
662
|
+
if (enriched.has(installedMetadataKey(dlist[0]))) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
pkgList = pkgList.concat(dlist);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
635
669
|
|
|
636
670
|
const parentDependsOn = new Set();
|
|
637
671
|
|
|
@@ -742,79 +776,81 @@ export async function createPythonBom(path, options) {
|
|
|
742
776
|
);
|
|
743
777
|
}
|
|
744
778
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
779
|
+
}
|
|
780
|
+
// ATOM parsedeps block
|
|
781
|
+
// Atom parsedeps slices can be used to identify packages that are not declared in manifests
|
|
782
|
+
// Since it is a slow operation, we only use it as a fallback or in deep mode
|
|
783
|
+
// This change was made in 10.9.2 release onwards. It reads the sources and
|
|
784
|
+
// installs nothing, so it runs whether or not dependencies are installed.
|
|
785
|
+
if (options.deep || !pkgList.length) {
|
|
786
|
+
if (!pkgList.length) {
|
|
787
|
+
thoughtLog(
|
|
788
|
+
"I couldn't find any components yet. Let's try static analysis with atom parsedeps command.",
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
const retMap = await getPyModules(path, pkgList, options);
|
|
792
|
+
// We need to patch the existing package list to add ImportedModules for evinse to work
|
|
793
|
+
if (retMap.modList?.length) {
|
|
794
|
+
const iSymbolsMap = {};
|
|
795
|
+
retMap.modList.forEach((v) => {
|
|
796
|
+
iSymbolsMap[v.name] = v.importedSymbols;
|
|
797
|
+
iSymbolsMap[v.name.replace(/_/g, "-")] = v.importedSymbols;
|
|
798
|
+
});
|
|
799
|
+
for (const apkg of pkgList) {
|
|
800
|
+
if (iSymbolsMap[apkg.name]) {
|
|
801
|
+
apkg.scope = "required";
|
|
802
|
+
apkg.properties = apkg.properties || [];
|
|
803
|
+
apkg.properties.push({
|
|
804
|
+
name: "internal:ImportedModules",
|
|
805
|
+
value: iSymbolsMap[apkg.name],
|
|
806
|
+
});
|
|
772
807
|
}
|
|
773
808
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
parentDependsOn.add(pypiBomRef(p.name, p.version));
|
|
809
|
+
}
|
|
810
|
+
if (retMap.pkgList?.length) {
|
|
811
|
+
pkgList = pkgList.concat(retMap.pkgList);
|
|
812
|
+
for (const p of retMap.pkgList) {
|
|
813
|
+
if (
|
|
814
|
+
!p.version ||
|
|
815
|
+
(parentComponent &&
|
|
816
|
+
p.name === parentComponent.name &&
|
|
817
|
+
(p.version === parentComponent.version || p.version === "latest"))
|
|
818
|
+
) {
|
|
819
|
+
continue;
|
|
787
820
|
}
|
|
788
|
-
|
|
789
|
-
if (retMap.dependenciesList) {
|
|
790
|
-
dependencies = mergeDependencies(
|
|
791
|
-
dependencies,
|
|
792
|
-
retMap.dependenciesList,
|
|
793
|
-
parentComponent,
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
if (retMap.allImports) {
|
|
797
|
-
allImports = { ...allImports, ...retMap.allImports };
|
|
821
|
+
parentDependsOn.add(pypiBomRef(p.name, p.version));
|
|
798
822
|
}
|
|
799
823
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
824
|
+
if (retMap.dependenciesList) {
|
|
825
|
+
dependencies = mergeDependencies(
|
|
826
|
+
dependencies,
|
|
827
|
+
retMap.dependenciesList,
|
|
828
|
+
parentComponent,
|
|
829
|
+
);
|
|
807
830
|
}
|
|
808
|
-
if (
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
831
|
+
if (retMap.allImports) {
|
|
832
|
+
allImports = { ...allImports, ...retMap.allImports };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// ATOM parsedeps block
|
|
836
|
+
let parentPresent = false;
|
|
837
|
+
for (const d of dependencies) {
|
|
838
|
+
if (d.ref === parentComponent["bom-ref"]) {
|
|
839
|
+
parentPresent = true;
|
|
840
|
+
break;
|
|
816
841
|
}
|
|
817
842
|
}
|
|
843
|
+
// Without an install, the parent's dependencies are known only when the
|
|
844
|
+
// import scan found some: an empty list would claim it has none.
|
|
845
|
+
if (!parentPresent && (options.installDeps || parentDependsOn.size)) {
|
|
846
|
+
const pdependencies = {
|
|
847
|
+
ref: parentComponent["bom-ref"],
|
|
848
|
+
dependsOn: Array.from(parentDependsOn).filter(
|
|
849
|
+
(r) => parentComponent && r !== parentComponent["bom-ref"],
|
|
850
|
+
),
|
|
851
|
+
};
|
|
852
|
+
dependencies.splice(0, 0, pdependencies);
|
|
853
|
+
}
|
|
818
854
|
}
|
|
819
855
|
// Final fallback is to manually parse setup.py if we still
|
|
820
856
|
// have an empty list
|
|
@@ -1403,7 +1439,7 @@ export async function createRubyBom(path, options) {
|
|
|
1403
1439
|
console.log(`Parsing ${f}`);
|
|
1404
1440
|
}
|
|
1405
1441
|
const gemLockData = readFileSync(f, { encoding: "utf-8" });
|
|
1406
|
-
const retMap = await parseGemfileLockData(gemLockData, f);
|
|
1442
|
+
const retMap = await parseGemfileLockData(gemLockData, f, { gemHome });
|
|
1407
1443
|
if (retMap.pkgList?.length) {
|
|
1408
1444
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
1409
1445
|
pkgList = trimComponents(pkgList);
|
|
@@ -1457,7 +1493,7 @@ export async function createRubyBom(path, options) {
|
|
|
1457
1493
|
const gpkgList = await parseGemspecData(
|
|
1458
1494
|
gemspecData,
|
|
1459
1495
|
f,
|
|
1460
|
-
isRootGemspec ? { lockedVersions } : {},
|
|
1496
|
+
isRootGemspec ? { lockedVersions, projectGemspec: true } : {},
|
|
1461
1497
|
);
|
|
1462
1498
|
if (!gpkgList.length) {
|
|
1463
1499
|
continue;
|
|
@@ -1821,49 +1857,88 @@ export async function createCsharpBom(path, options) {
|
|
|
1821
1857
|
const parentDependsOn = new Set();
|
|
1822
1858
|
if (nupkgFiles.length && projAssetsFiles.length === 0) {
|
|
1823
1859
|
manifestFiles = manifestFiles.concat(nupkgFiles);
|
|
1824
|
-
//
|
|
1825
|
-
//
|
|
1826
|
-
|
|
1827
|
-
|
|
1860
|
+
// A nuspec states the range each dependency accepts, not the version
|
|
1861
|
+
// installed. The installed packages are the components, and a dependency
|
|
1862
|
+
// edge points at the installed version its range resolves to; a minimum
|
|
1863
|
+
// version nobody installed is not a component.
|
|
1864
|
+
const installedVersions = {};
|
|
1865
|
+
const installedNames = {};
|
|
1866
|
+
let dependencyRanges = {};
|
|
1828
1867
|
for (const nf of nupkgFiles) {
|
|
1829
1868
|
if (DEBUG_MODE) {
|
|
1830
1869
|
console.log(`Parsing ${nf}`);
|
|
1831
1870
|
}
|
|
1832
1871
|
const retMap = await parseNupkg(nf);
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1872
|
+
const installed = retMap?.pkgList?.[0];
|
|
1873
|
+
if (installed) {
|
|
1874
|
+
pkgList.push(installed);
|
|
1875
|
+
parentDependsOn.add(installed["bom-ref"]);
|
|
1876
|
+
const key = installed.name.toLowerCase();
|
|
1877
|
+
installedNames[key] ??= installed.name;
|
|
1878
|
+
installedVersions[key] ??= [];
|
|
1879
|
+
if (installed.version) {
|
|
1880
|
+
installedVersions[key].push(installed.version);
|
|
1838
1881
|
}
|
|
1839
1882
|
}
|
|
1840
|
-
if (retMap?.
|
|
1841
|
-
|
|
1883
|
+
if (retMap?.dependencyRanges) {
|
|
1884
|
+
dependencyRanges = { ...dependencyRanges, ...retMap.dependencyRanges };
|
|
1842
1885
|
}
|
|
1843
1886
|
} // end for
|
|
1844
|
-
for (const
|
|
1845
|
-
const dependsOn =
|
|
1846
|
-
|
|
1847
|
-
const
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1887
|
+
for (const [ref, ranges] of Object.entries(dependencyRanges)) {
|
|
1888
|
+
const dependsOn = new Set();
|
|
1889
|
+
for (const [name, range] of Object.entries(ranges)) {
|
|
1890
|
+
const key = name.toLowerCase();
|
|
1891
|
+
const version = resolveInstalledNugetVersion(
|
|
1892
|
+
range,
|
|
1893
|
+
installedVersions[key],
|
|
1894
|
+
);
|
|
1895
|
+
const purl = version ? nugetPurl(installedNames[key], version) : null;
|
|
1896
|
+
if (purl) {
|
|
1897
|
+
dependsOn.add(decodeURIComponent(purl));
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
dependencies.push({ ref, dependsOn: [...dependsOn].sort() });
|
|
1851
1901
|
}
|
|
1852
1902
|
}
|
|
1903
|
+
// Versions each project restored, by project directory and lowercased
|
|
1904
|
+
// package id. Two projects of one tree routinely restore different versions
|
|
1905
|
+
// of a package, so a version missing from one project's manifest is taken
|
|
1906
|
+
// from that project's restore output, never from another project's.
|
|
1907
|
+
// The restore output also spells each package id the way its nuspec does.
|
|
1908
|
+
const projectVersions = {};
|
|
1909
|
+
const projectNames = {};
|
|
1910
|
+
const recordProjectVersions = (projectDir, pkgs) => {
|
|
1911
|
+
projectVersions[projectDir] ??= {};
|
|
1912
|
+
projectNames[projectDir] ??= {};
|
|
1913
|
+
for (const p of pkgs || []) {
|
|
1914
|
+
if (
|
|
1915
|
+
p?.name &&
|
|
1916
|
+
p.version &&
|
|
1917
|
+
!["application", "project"].includes(p.type)
|
|
1918
|
+
) {
|
|
1919
|
+
projectVersions[projectDir][p.name.toLowerCase()] ??= p.version;
|
|
1920
|
+
projectNames[projectDir][p.name.toLowerCase()] ??= p.name;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
// Direct dependencies of each project directory from its lock file or
|
|
1925
|
+
// packages.config, attached to that project once the project files are read.
|
|
1926
|
+
const rootsByProjectDir = {};
|
|
1927
|
+
const recordRoots = (projectDir, pkgs) => {
|
|
1928
|
+
rootsByProjectDir[projectDir] ??= new Set();
|
|
1929
|
+
for (const p of pkgs || []) {
|
|
1930
|
+
if (p?.["bom-ref"]) {
|
|
1931
|
+
rootsByProjectDir[projectDir].add(p["bom-ref"]);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1853
1935
|
// In a multi-project scan, different projects may use different manifest
|
|
1854
1936
|
// formats: modern SDK-style (project.assets.json / packages.lock.json) or
|
|
1855
1937
|
// legacy (packages.config). Apply the assets > lock > config precedence per
|
|
1856
1938
|
// project directory instead of across the whole scan, so dependencies from
|
|
1857
1939
|
// one project's manifest are not dropped just because another format exists
|
|
1858
1940
|
// elsewhere in the scan.
|
|
1859
|
-
const assetsProjectDirs = new Set(
|
|
1860
|
-
projAssetsFiles.map((af) => {
|
|
1861
|
-
// project.assets.json is generated under <project>/obj/
|
|
1862
|
-
const d = dirname(af);
|
|
1863
|
-
return basename(d) === "obj" ? dirname(d) : d;
|
|
1864
|
-
}),
|
|
1865
|
-
);
|
|
1866
|
-
const lockProjectDirs = new Set(pkgLockFiles.map((af) => dirname(af)));
|
|
1941
|
+
const assetsProjectDirs = new Set();
|
|
1867
1942
|
// project.assets.json parsing
|
|
1868
1943
|
if (projAssetsFiles.length) {
|
|
1869
1944
|
manifestFiles = manifestFiles.concat(projAssetsFiles);
|
|
@@ -1875,6 +1950,11 @@ export async function createCsharpBom(path, options) {
|
|
|
1875
1950
|
const results = parseCsProjAssetsData(pkgData, af);
|
|
1876
1951
|
const deps = results["dependenciesList"];
|
|
1877
1952
|
const dlist = results["pkgList"];
|
|
1953
|
+
// Restore output names its project; an artifacts layout keeps it outside
|
|
1954
|
+
// the project directory.
|
|
1955
|
+
const projectDir = dotnetManifestProjectDir(af, results.projectPath);
|
|
1956
|
+
assetsProjectDirs.add(projectDir);
|
|
1957
|
+
recordProjectVersions(projectDir, dlist);
|
|
1878
1958
|
if (dlist?.length) {
|
|
1879
1959
|
pkgList = pkgList.concat(dlist);
|
|
1880
1960
|
}
|
|
@@ -1907,9 +1987,12 @@ export async function createCsharpBom(path, options) {
|
|
|
1907
1987
|
});
|
|
1908
1988
|
}
|
|
1909
1989
|
}
|
|
1990
|
+
const lockProjectDirs = new Set(
|
|
1991
|
+
pkgLockFiles.map((af) => dotnetManifestProjectDir(af)),
|
|
1992
|
+
);
|
|
1910
1993
|
// packages.lock.json parsing for any project without a project.assets.json
|
|
1911
1994
|
const pkgLockToParse = pkgLockFiles.filter(
|
|
1912
|
-
(af) => !assetsProjectDirs.has(
|
|
1995
|
+
(af) => !assetsProjectDirs.has(dotnetManifestProjectDir(af)),
|
|
1913
1996
|
);
|
|
1914
1997
|
if (pkgLockToParse.length) {
|
|
1915
1998
|
manifestFiles = manifestFiles.concat(pkgLockToParse);
|
|
@@ -1923,37 +2006,29 @@ export async function createCsharpBom(path, options) {
|
|
|
1923
2006
|
const deps = results["dependenciesList"];
|
|
1924
2007
|
const dlist = results["pkgList"];
|
|
1925
2008
|
const rootList = results["rootList"];
|
|
2009
|
+
const projectDir = dotnetManifestProjectDir(af);
|
|
2010
|
+
recordProjectVersions(projectDir, dlist);
|
|
1926
2011
|
if (dlist?.length) {
|
|
1927
2012
|
pkgList = pkgList.concat(dlist);
|
|
1928
2013
|
}
|
|
1929
2014
|
if (deps?.length) {
|
|
1930
2015
|
dependencies = mergeDependencies(dependencies, deps, parentComponent);
|
|
1931
2016
|
}
|
|
1932
|
-
|
|
1933
|
-
// list after processing all lock files
|
|
1934
|
-
if (rootList?.length) {
|
|
1935
|
-
for (const p of rootList) {
|
|
1936
|
-
parentDependsOn.add(p["bom-ref"]);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
2017
|
+
recordRoots(projectDir, rootList);
|
|
1939
2018
|
}
|
|
1940
2019
|
}
|
|
1941
2020
|
// packages.config parsing for any project without a modern manifest
|
|
1942
2021
|
// (project.assets.json / packages.lock.json) covering the same directory
|
|
1943
2022
|
const pkgConfigToParse = pkgConfigFiles.filter((f) => {
|
|
1944
|
-
const projDir =
|
|
2023
|
+
const projDir = dotnetManifestProjectDir(f);
|
|
1945
2024
|
return !assetsProjectDirs.has(projDir) && !lockProjectDirs.has(projDir);
|
|
1946
2025
|
});
|
|
1947
2026
|
if (pkgConfigToParse.length) {
|
|
1948
2027
|
manifestFiles = manifestFiles.concat(pkgConfigToParse);
|
|
1949
|
-
//
|
|
1950
|
-
//
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
if (p.name && p.version) {
|
|
1954
|
-
resolvedVersions[p.name] = p.version;
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
2028
|
+
// A templated or missing version is backfilled only from a version every
|
|
2029
|
+
// other manifest of the scan agrees on: these projects restored nothing of
|
|
2030
|
+
// their own, and another project's version may not be theirs.
|
|
2031
|
+
const resolvedVersions = agreedNugetVersions(pkgList);
|
|
1957
2032
|
// packages.config parsing
|
|
1958
2033
|
for (const f of pkgConfigToParse) {
|
|
1959
2034
|
if (DEBUG_MODE) {
|
|
@@ -1961,11 +2036,11 @@ export async function createCsharpBom(path, options) {
|
|
|
1961
2036
|
}
|
|
1962
2037
|
pkgData = readFileSync(f, { encoding: "utf-8" });
|
|
1963
2038
|
const dlist = parseCsPkgData(pkgData, f, resolvedVersions);
|
|
2039
|
+
const projectDir = dotnetManifestProjectDir(f);
|
|
2040
|
+
recordProjectVersions(projectDir, dlist);
|
|
1964
2041
|
if (dlist?.length) {
|
|
1965
2042
|
pkgList = pkgList.concat(dlist);
|
|
1966
|
-
|
|
1967
|
-
parentDependsOn.add(d["bom-ref"]);
|
|
1968
|
-
}
|
|
2043
|
+
recordRoots(projectDir, dlist);
|
|
1969
2044
|
}
|
|
1970
2045
|
}
|
|
1971
2046
|
}
|
|
@@ -1988,27 +2063,26 @@ export async function createCsharpBom(path, options) {
|
|
|
1988
2063
|
}
|
|
1989
2064
|
}
|
|
1990
2065
|
}
|
|
1991
|
-
const
|
|
2066
|
+
const projectRefByDir = {};
|
|
1992
2067
|
let services = [];
|
|
1993
2068
|
if (csProjFiles.length) {
|
|
1994
2069
|
manifestFiles = manifestFiles.concat(csProjFiles);
|
|
1995
|
-
// Parsing csproj is quite error-prone. Some project files may not have
|
|
1996
|
-
//
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2070
|
+
// Parsing csproj is quite error-prone. Some project files may not have
|
|
2071
|
+
// versions specified. Such a version comes from what this project restored,
|
|
2072
|
+
// then from central package management, and only then from a version the
|
|
2073
|
+
// whole scan agrees on.
|
|
2074
|
+
const agreedVersions = agreedNugetVersions(pkgList);
|
|
2002
2075
|
let msbuildInstalled = false;
|
|
2003
|
-
|
|
2004
|
-
const result = safeSpawnSync(DOTNET_CMD, ["msbuild --version"], {
|
|
2076
|
+
const result = safeSpawnSync(DOTNET_CMD, ["msbuild", "--version"], {
|
|
2005
2077
|
shell: isWin,
|
|
2006
2078
|
});
|
|
2007
|
-
if (result.status === 0 && !result.error && result.stdout
|
|
2079
|
+
if (result.status === 0 && !result.error && result.stdout?.trim()) {
|
|
2008
2080
|
msbuildInstalled = true;
|
|
2009
|
-
} else {
|
|
2010
|
-
pkgVersionLabelCandidates = getPropertyGroupTextNodes(propsFiles);
|
|
2011
2081
|
}
|
|
2082
|
+
// `$(Name)` version labels resolve from the props files that can apply to
|
|
2083
|
+
// each project (beside and above it), read once for the whole scan.
|
|
2084
|
+
// msbuild evaluates only what they leave unresolved.
|
|
2085
|
+
const propsIndex = indexPropsProperties(propsFiles);
|
|
2012
2086
|
// Directory.Packages.props can sit above the directory being scanned, so it is
|
|
2013
2087
|
// resolved per project file by walking up rather than from the props glob below.
|
|
2014
2088
|
// The cache keeps a repository with many projects from re-parsing the same file.
|
|
@@ -2019,15 +2093,19 @@ export async function createCsharpBom(path, options) {
|
|
|
2019
2093
|
console.log(`Parsing ${f}`);
|
|
2020
2094
|
}
|
|
2021
2095
|
const csProjData = readFileSync(f, { encoding: "utf-8" });
|
|
2096
|
+
const projectDir = dotnetManifestProjectDir(f);
|
|
2022
2097
|
const retMap = parseCsProjData(
|
|
2023
2098
|
csProjData,
|
|
2024
2099
|
f,
|
|
2025
|
-
|
|
2100
|
+
projectVersions[projectDir] || {},
|
|
2026
2101
|
msbuildInstalled,
|
|
2027
|
-
|
|
2102
|
+
projectPropertyCandidates(f, propsIndex),
|
|
2028
2103
|
getCentralPackageVersions(f, centralVersionsCache),
|
|
2104
|
+
agreedVersions,
|
|
2105
|
+
projectNames[projectDir] || {},
|
|
2029
2106
|
);
|
|
2030
2107
|
if (retMap?.parentComponent?.purl) {
|
|
2108
|
+
projectRefByDir[projectDir] ??= retMap.parentComponent["bom-ref"];
|
|
2031
2109
|
// If there are multiple project files, track the parent components using nested components
|
|
2032
2110
|
if (csProjFiles.length > 1) {
|
|
2033
2111
|
if (!parentComponent.components) {
|
|
@@ -2051,6 +2129,25 @@ export async function createCsharpBom(path, options) {
|
|
|
2051
2129
|
}
|
|
2052
2130
|
}
|
|
2053
2131
|
}
|
|
2132
|
+
// A lock file's or packages.config's direct dependencies belong to the
|
|
2133
|
+
// project in that directory; without a project file there, to the parent.
|
|
2134
|
+
for (const [projectDir, roots] of Object.entries(rootsByProjectDir)) {
|
|
2135
|
+
if (!roots.size) {
|
|
2136
|
+
continue;
|
|
2137
|
+
}
|
|
2138
|
+
const projectRef = projectRefByDir[projectDir];
|
|
2139
|
+
if (projectRef && projectRef !== parentComponent?.["bom-ref"]) {
|
|
2140
|
+
dependencies = mergeDependencies(
|
|
2141
|
+
dependencies,
|
|
2142
|
+
[{ ref: projectRef, dependsOn: [...roots].sort() }],
|
|
2143
|
+
parentComponent,
|
|
2144
|
+
);
|
|
2145
|
+
} else {
|
|
2146
|
+
for (const ref of roots) {
|
|
2147
|
+
parentDependsOn.add(ref);
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2054
2151
|
if (pkgList.length) {
|
|
2055
2152
|
pkgList = trimComponents(pkgList);
|
|
2056
2153
|
// Perform deep analysis using dosai
|
|
@@ -2059,44 +2156,60 @@ export async function createCsharpBom(path, options) {
|
|
|
2059
2156
|
// as-is: join would append it to the scan path, which is never a valid
|
|
2060
2157
|
// path on Windows and silently loses the .NET evidence elsewhere
|
|
2061
2158
|
// (issue 4394). A relative value still resolves against the scan path.
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2159
|
+
// Without --deps-slices-file the slice goes to a directory of this run:
|
|
2160
|
+
// a fixed name in the shared temp directory was reused by every later
|
|
2161
|
+
// scan, of any project, as if it were that project's evidence.
|
|
2162
|
+
const slicesDir = options.depsSlicesFile
|
|
2163
|
+
? undefined
|
|
2164
|
+
: safeMkdtempSync(join(getTmpDir(), "cdxgen-dosai-"));
|
|
2165
|
+
const slicesFile = options.depsSlicesFile
|
|
2166
|
+
? resolve(path, options.depsSlicesFile)
|
|
2167
|
+
: join(slicesDir, "dosai.json");
|
|
2168
|
+
try {
|
|
2169
|
+
// Create the slices file if it doesn't exist
|
|
2170
|
+
if (!safeExistsSync(slicesFile)) {
|
|
2171
|
+
thoughtLog(
|
|
2172
|
+
"Alright, the next step is to invoke the dosai command to identify evidence of occurrences for various components.",
|
|
2173
|
+
);
|
|
2174
|
+
const sliceResult = createDosaiMethodsSlice(
|
|
2175
|
+
resolve(path),
|
|
2176
|
+
resolve(slicesFile),
|
|
2177
|
+
options,
|
|
2178
|
+
);
|
|
2179
|
+
// A stopped run has already said so and what to change; the debug
|
|
2180
|
+
// output would show no dosai error for it (issue 4438).
|
|
2181
|
+
if (!sliceResult && !dosaiRunWasStopped(resolve(slicesFile))) {
|
|
2182
|
+
// Visible without debug output: otherwise the components silently
|
|
2183
|
+
// lose the dependency-slice evidence while the scan still exits 0.
|
|
2184
|
+
// --evidence (which implies --deep) runs dosai again during evinse
|
|
2185
|
+
// and can still add occurrences, so this names only what is lost.
|
|
2186
|
+
// Debug output comes from the environment, not a CLI flag (issue
|
|
2187
|
+
// 4438).
|
|
2188
|
+
console.warn(
|
|
2189
|
+
"Slicing with dosai was unsuccessful, so the .NET components get no occurrence, reachability, or services evidence from the dependency slice. Re-run with CDXGEN_DEBUG_MODE=debug for the dosai errors.",
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
2192
|
+
} else if (DEBUG_MODE) {
|
|
2193
|
+
console.log(
|
|
2194
|
+
`Reusing the existing dosai methods slice "${slicesFile}" without checking whether it honours the current --exclude patterns. Delete the file to force a fresh run.`,
|
|
2084
2195
|
);
|
|
2085
2196
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2197
|
+
pkgList = addEvidenceForDotnet(pkgList, slicesFile, {
|
|
2198
|
+
srcPath: resolve(path),
|
|
2199
|
+
});
|
|
2200
|
+
const methodsSlice = readDosaiMethodsReport(slicesFile, {
|
|
2201
|
+
sections: ["Metadata", "Services", "ApiEndpoints"],
|
|
2202
|
+
});
|
|
2203
|
+
// Provider-backed Services[] first so bom-refs, trust zones, and data
|
|
2204
|
+
// classifications survive; ApiEndpoints derivation fills in the rest.
|
|
2205
|
+
const servicesMap = collectDosaiServiceComponents(methodsSlice, {});
|
|
2206
|
+
collectDosaiServicesFromMethods(methodsSlice, servicesMap);
|
|
2207
|
+
services = normalizeDosaiServiceMap(servicesMap);
|
|
2208
|
+
} finally {
|
|
2209
|
+
if (slicesDir) {
|
|
2210
|
+
safeRmSync(slicesDir, { recursive: true, force: true });
|
|
2211
|
+
}
|
|
2090
2212
|
}
|
|
2091
|
-
pkgList = addEvidenceForDotnet(pkgList, slicesFile);
|
|
2092
|
-
const methodsSlice = readDosaiMethodsReport(slicesFile, {
|
|
2093
|
-
sections: ["Metadata", "Services", "ApiEndpoints"],
|
|
2094
|
-
});
|
|
2095
|
-
// Provider-backed Services[] first so bom-refs, trust zones, and data
|
|
2096
|
-
// classifications survive; ApiEndpoints derivation fills in the rest.
|
|
2097
|
-
const servicesMap = collectDosaiServiceComponents(methodsSlice, {});
|
|
2098
|
-
collectDosaiServicesFromMethods(methodsSlice, servicesMap);
|
|
2099
|
-
services = normalizeDosaiServiceMap(servicesMap);
|
|
2100
2213
|
}
|
|
2101
2214
|
}
|
|
2102
2215
|
// Parent dependency tree. A single project file already contributed an
|
|
@@ -2342,34 +2455,41 @@ export function createPerlBom(path, options) {
|
|
|
2342
2455
|
/**
|
|
2343
2456
|
* Function to create bom string for Terraform and OpenTofu projects.
|
|
2344
2457
|
*
|
|
2345
|
-
* The
|
|
2346
|
-
*
|
|
2458
|
+
* The workspace assembly turns the provider lock file, the module manifest
|
|
2459
|
+
* `init` writes and the configuration files into module and provider
|
|
2460
|
+
* components with a dependency graph and offline licenses. When metadata
|
|
2461
|
+
* fetching is enabled, the public registries add licenses, repository URLs
|
|
2462
|
+
* and registry flags to components the offline pass could not resolve.
|
|
2347
2463
|
*
|
|
2348
2464
|
* @param {string} path to the project
|
|
2349
2465
|
* @param {Object} options Parse options from the cli
|
|
2350
|
-
* @returns {Object} BOM object
|
|
2466
|
+
* @returns {Promise<Object>} BOM object
|
|
2351
2467
|
*/
|
|
2352
|
-
export function createTerraformBom(path, options) {
|
|
2353
|
-
|
|
2354
|
-
const lockFiles = getAllFiles(
|
|
2468
|
+
export async function createTerraformBom(path, options) {
|
|
2469
|
+
const parentComponent = createDefaultParentComponent(
|
|
2355
2470
|
path,
|
|
2356
|
-
|
|
2471
|
+
"generic",
|
|
2357
2472
|
options,
|
|
2358
2473
|
);
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
}
|
|
2474
|
+
const { pkgList, dependencies, parentProperties, srcFiles } =
|
|
2475
|
+
parseTerraformWorkspace(path, options, parentComponent["bom-ref"]);
|
|
2476
|
+
// No module or provider found (for example, resources only, or a stray
|
|
2477
|
+
// .tf in a JS repo): emit nothing, including no root applications, so
|
|
2478
|
+
// multi-type scans do not gain an empty Terraform parent.
|
|
2479
|
+
if (!pkgList.some((p) => p.type === "library")) {
|
|
2480
|
+
return {};
|
|
2367
2481
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2482
|
+
parentComponent.properties = [
|
|
2483
|
+
...(parentComponent.properties || []),
|
|
2484
|
+
...parentProperties,
|
|
2485
|
+
];
|
|
2486
|
+
if (pkgList.length && shouldFetchPackageMetadata()) {
|
|
2487
|
+
await getTerraformRegistryMetadata(pkgList);
|
|
2373
2488
|
}
|
|
2374
|
-
return {
|
|
2489
|
+
return buildBomNSData(options, pkgList, "terraform", {
|
|
2490
|
+
src: path,
|
|
2491
|
+
filename: srcFiles.join(", "),
|
|
2492
|
+
dependencies,
|
|
2493
|
+
parentComponent,
|
|
2494
|
+
});
|
|
2375
2495
|
}
|