@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/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 {
|
|
@@ -73,12 +80,16 @@ import {
|
|
|
73
80
|
getPyModules,
|
|
74
81
|
} from "../ecosystems/pythonutils.js";
|
|
75
82
|
import {
|
|
83
|
+
addRubyGemsDistributionUrls,
|
|
84
|
+
addRubyRuntimeComponent,
|
|
76
85
|
collectGemModuleNames,
|
|
86
|
+
describeProjectGem,
|
|
77
87
|
enrichGemsFromLocalCache,
|
|
78
88
|
parseBundleConfig,
|
|
79
89
|
parseGemfileLockData,
|
|
80
90
|
parseGemspecData,
|
|
81
91
|
} from "../ecosystems/rubyutils.js";
|
|
92
|
+
import { getTerraformRegistryMetadata } from "../ecosystems/terraformRegistry.js";
|
|
82
93
|
import {
|
|
83
94
|
collectAiInventory,
|
|
84
95
|
summarizeAiInventory,
|
|
@@ -102,9 +113,10 @@ import {
|
|
|
102
113
|
collectDosaiServiceComponents,
|
|
103
114
|
collectDosaiServicesFromMethods,
|
|
104
115
|
createDosaiMethodsSlice,
|
|
116
|
+
dosaiRunWasStopped,
|
|
105
117
|
isDosaiDotnetLanguage,
|
|
106
118
|
normalizeDosaiServiceMap,
|
|
107
|
-
|
|
119
|
+
readDosaiMethodsReport,
|
|
108
120
|
} from "../inventory/dosai.js";
|
|
109
121
|
import { addEvidenceForDotnet } from "../inventory/evidenceUtils.js";
|
|
110
122
|
import { nugetPurl, pypiBomRef } from "../inventory/purl.js";
|
|
@@ -401,6 +413,17 @@ export async function createPythonBom(path, options) {
|
|
|
401
413
|
!pyLockFiles?.length;
|
|
402
414
|
const poetryMode = poetryFiles?.length;
|
|
403
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
|
+
|
|
404
427
|
// TODO: Support for nested directories
|
|
405
428
|
const setupPy = join(path, "setup.py");
|
|
406
429
|
const setupPyMode = safeExistsSync(setupPy);
|
|
@@ -413,7 +436,9 @@ export async function createPythonBom(path, options) {
|
|
|
413
436
|
if (DEBUG_MODE) {
|
|
414
437
|
console.log(`Parsing ${f}`);
|
|
415
438
|
}
|
|
416
|
-
let retMap = await parsePyLockData(lockData, f
|
|
439
|
+
let retMap = await parsePyLockData(lockData, f, undefined, {
|
|
440
|
+
installedMetadata,
|
|
441
|
+
});
|
|
417
442
|
if (retMap?.workspaceWarningShown) {
|
|
418
443
|
deferFailOnError(options, {
|
|
419
444
|
ecosystem: "python",
|
|
@@ -505,7 +530,11 @@ export async function createPythonBom(path, options) {
|
|
|
505
530
|
`exported-${basename(basePath)}-reqs.txt`,
|
|
506
531
|
);
|
|
507
532
|
safeWriteSync(tmpReqFile, exportedReqs);
|
|
508
|
-
const dlist = await parseReqFile(
|
|
533
|
+
const dlist = await parseReqFile(
|
|
534
|
+
tmpReqFile,
|
|
535
|
+
false,
|
|
536
|
+
installedMetadata,
|
|
537
|
+
);
|
|
509
538
|
if (dlist?.length) {
|
|
510
539
|
pkgList = pkgList.concat(dlist);
|
|
511
540
|
}
|
|
@@ -515,15 +544,6 @@ export async function createPythonBom(path, options) {
|
|
|
515
544
|
}
|
|
516
545
|
options.parentComponent = parentComponent;
|
|
517
546
|
} // poetryMode
|
|
518
|
-
if (metadataFiles?.length) {
|
|
519
|
-
// dist-info directories
|
|
520
|
-
for (const mf of metadataFiles) {
|
|
521
|
-
const dlist = parseBdistMetadata(mf);
|
|
522
|
-
if (dlist?.length) {
|
|
523
|
-
pkgList = pkgList.concat(dlist);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
547
|
// .whl files. Zip file containing dist-info directory
|
|
528
548
|
if (whlFiles?.length) {
|
|
529
549
|
for (const wf of whlFiles) {
|
|
@@ -573,14 +593,23 @@ export async function createPythonBom(path, options) {
|
|
|
573
593
|
}
|
|
574
594
|
if (requirementsMode || pipenvMode) {
|
|
575
595
|
if (pipenvMode) {
|
|
576
|
-
//
|
|
577
|
-
|
|
596
|
+
// `pipenv install` builds and installs the project's own dependencies
|
|
597
|
+
// (including `-e .` self-references that execute setup.py), so unlike
|
|
598
|
+
// the lockfile parsing below it must honor --no-install-deps and the
|
|
599
|
+
// pre-build lifecycle.
|
|
600
|
+
if (options.installDeps !== false) {
|
|
601
|
+
safeSpawnSync("pipenv", ["install"], { cwd: path, shell: isWin });
|
|
602
|
+
} else {
|
|
603
|
+
console.log(
|
|
604
|
+
"Skipping 'pipenv install' since dependency installation is disabled.",
|
|
605
|
+
);
|
|
606
|
+
}
|
|
578
607
|
const piplockFile = join(path, "Pipfile.lock");
|
|
579
608
|
if (safeExistsSync(piplockFile)) {
|
|
580
609
|
let dlist;
|
|
581
610
|
try {
|
|
582
611
|
const lockData = JSON.parse(readFileSync(piplockFile));
|
|
583
|
-
dlist = await parsePiplockData(lockData);
|
|
612
|
+
dlist = await parsePiplockData(lockData, installedMetadata);
|
|
584
613
|
} catch (err) {
|
|
585
614
|
recordDegradation("python.lockfile-unparseable.pipenv", {
|
|
586
615
|
ecosystem: "python",
|
|
@@ -605,7 +634,7 @@ export async function createPythonBom(path, options) {
|
|
|
605
634
|
} else if (reqDirFiles?.length) {
|
|
606
635
|
for (const j in reqDirFiles) {
|
|
607
636
|
const f = reqDirFiles[j];
|
|
608
|
-
const dlist = await parseReqFile(f, false);
|
|
637
|
+
const dlist = await parseReqFile(f, false, installedMetadata);
|
|
609
638
|
if (dlist?.length) {
|
|
610
639
|
pkgList = pkgList.concat(dlist);
|
|
611
640
|
}
|
|
@@ -613,13 +642,30 @@ export async function createPythonBom(path, options) {
|
|
|
613
642
|
metadataFilename = reqDirFiles.join(", ");
|
|
614
643
|
} else if (reqFiles?.length) {
|
|
615
644
|
for (const f of reqFiles) {
|
|
616
|
-
const dlist = await parseReqFile(f, false);
|
|
645
|
+
const dlist = await parseReqFile(f, false, installedMetadata);
|
|
617
646
|
if (dlist?.length) {
|
|
618
647
|
pkgList = pkgList.concat(dlist);
|
|
619
648
|
}
|
|
620
649
|
}
|
|
621
650
|
}
|
|
622
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
|
+
}
|
|
623
669
|
|
|
624
670
|
const parentDependsOn = new Set();
|
|
625
671
|
|
|
@@ -730,79 +776,81 @@ export async function createPythonBom(path, options) {
|
|
|
730
776
|
);
|
|
731
777
|
}
|
|
732
778
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
+
});
|
|
760
807
|
}
|
|
761
808
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
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;
|
|
775
820
|
}
|
|
776
|
-
|
|
777
|
-
if (retMap.dependenciesList) {
|
|
778
|
-
dependencies = mergeDependencies(
|
|
779
|
-
dependencies,
|
|
780
|
-
retMap.dependenciesList,
|
|
781
|
-
parentComponent,
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
if (retMap.allImports) {
|
|
785
|
-
allImports = { ...allImports, ...retMap.allImports };
|
|
821
|
+
parentDependsOn.add(pypiBomRef(p.name, p.version));
|
|
786
822
|
}
|
|
787
823
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
824
|
+
if (retMap.dependenciesList) {
|
|
825
|
+
dependencies = mergeDependencies(
|
|
826
|
+
dependencies,
|
|
827
|
+
retMap.dependenciesList,
|
|
828
|
+
parentComponent,
|
|
829
|
+
);
|
|
795
830
|
}
|
|
796
|
-
if (
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
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;
|
|
804
841
|
}
|
|
805
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
|
+
}
|
|
806
854
|
}
|
|
807
855
|
// Final fallback is to manually parse setup.py if we still
|
|
808
856
|
// have an empty list
|
|
@@ -1352,6 +1400,7 @@ export async function createRubyBom(path, options) {
|
|
|
1352
1400
|
let pkgList = [];
|
|
1353
1401
|
let dependencies = [];
|
|
1354
1402
|
let rootList = [];
|
|
1403
|
+
const projectGemRefs = [];
|
|
1355
1404
|
const parentComponent = createDefaultParentComponent(path, "gem", options);
|
|
1356
1405
|
const gemFileMode = gemFiles.length;
|
|
1357
1406
|
const gemLockMode = gemLockFiles.length;
|
|
@@ -1390,7 +1439,7 @@ export async function createRubyBom(path, options) {
|
|
|
1390
1439
|
console.log(`Parsing ${f}`);
|
|
1391
1440
|
}
|
|
1392
1441
|
const gemLockData = readFileSync(f, { encoding: "utf-8" });
|
|
1393
|
-
const retMap = await parseGemfileLockData(gemLockData, f);
|
|
1442
|
+
const retMap = await parseGemfileLockData(gemLockData, f, { gemHome });
|
|
1394
1443
|
if (retMap.pkgList?.length) {
|
|
1395
1444
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
1396
1445
|
pkgList = trimComponents(pkgList);
|
|
@@ -1405,10 +1454,18 @@ export async function createRubyBom(path, options) {
|
|
|
1405
1454
|
if (retMap.rootList?.length) {
|
|
1406
1455
|
rootList = rootList.concat(retMap.rootList);
|
|
1407
1456
|
}
|
|
1457
|
+
// `PATH remote: .` is relative to the lockfile, so only the lockfiles
|
|
1458
|
+
// of the scanned directory can serve the gem the root gemspec builds
|
|
1459
|
+
if (
|
|
1460
|
+
retMap.projectGemRefs?.length &&
|
|
1461
|
+
dirname(resolve(f)) === resolve(path)
|
|
1462
|
+
) {
|
|
1463
|
+
projectGemRefs.push(...retMap.projectGemRefs);
|
|
1464
|
+
}
|
|
1408
1465
|
}
|
|
1409
1466
|
}
|
|
1410
1467
|
// Parsing .gemspec files would help us get more metadata such as description, authors, licenses etc
|
|
1411
|
-
let
|
|
1468
|
+
let projectGem;
|
|
1412
1469
|
if (gemspecFiles.length) {
|
|
1413
1470
|
if (!gemLockFiles.length && !hasAnyProjectType(["oci"], options, false)) {
|
|
1414
1471
|
console.log(
|
|
@@ -1420,37 +1477,50 @@ export async function createRubyBom(path, options) {
|
|
|
1420
1477
|
detail: "only gemspec files were available to parse",
|
|
1421
1478
|
});
|
|
1422
1479
|
}
|
|
1480
|
+
// Bundler evaluated the root gemspec to lock it under `PATH remote: .`,
|
|
1481
|
+
// so the locked release stands in for a version computed at runtime. It
|
|
1482
|
+
// is resolved before the merge, so that the gemspec and lockfile
|
|
1483
|
+
// sightings of the gem reconcile into one component.
|
|
1484
|
+
const lockedVersions = Object.create(null);
|
|
1485
|
+
for (const p of pkgList) {
|
|
1486
|
+
if (p.version && projectGemRefs.includes(p["bom-ref"])) {
|
|
1487
|
+
lockedVersions[p.name] = p.version;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1423
1490
|
for (const f of gemspecFiles) {
|
|
1424
1491
|
const gemspecData = readFileSync(f, { encoding: "utf-8" });
|
|
1425
|
-
const
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
) {
|
|
1434
|
-
rootGemspecComponent = gpkgList[0];
|
|
1435
|
-
}
|
|
1492
|
+
const isRootGemspec = dirname(resolve(f)) === resolve(path);
|
|
1493
|
+
const gpkgList = await parseGemspecData(
|
|
1494
|
+
gemspecData,
|
|
1495
|
+
f,
|
|
1496
|
+
isRootGemspec ? { lockedVersions, projectGemspec: true } : {},
|
|
1497
|
+
);
|
|
1498
|
+
if (!gpkgList.length) {
|
|
1499
|
+
continue;
|
|
1436
1500
|
}
|
|
1501
|
+
// The gemspec in the scanned directory is the gem the project builds
|
|
1502
|
+
if (!projectGem && isRootGemspec && gpkgList[0]?.name) {
|
|
1503
|
+
projectGem = gpkgList[0];
|
|
1504
|
+
}
|
|
1505
|
+
pkgList = trimComponents(pkgList.concat(gpkgList));
|
|
1437
1506
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1507
|
+
}
|
|
1508
|
+
// Describe that gem in metadata.component, unless `--project-name` names a
|
|
1509
|
+
// different project or an image is being scanned
|
|
1510
|
+
if (
|
|
1511
|
+
projectGem &&
|
|
1512
|
+
!hasAnyProjectType(["oci"], options, false) &&
|
|
1513
|
+
(!("project-name" in options) || options.projectName === projectGem.name)
|
|
1514
|
+
) {
|
|
1515
|
+
({ pkgList, dependencies, rootList } = describeProjectGem(
|
|
1516
|
+
parentComponent,
|
|
1517
|
+
projectGem,
|
|
1518
|
+
pkgList,
|
|
1519
|
+
dependencies,
|
|
1520
|
+
rootList,
|
|
1521
|
+
projectGemRefs,
|
|
1522
|
+
options,
|
|
1523
|
+
));
|
|
1454
1524
|
}
|
|
1455
1525
|
if (rootList.length) {
|
|
1456
1526
|
dependencies = mergeDependencies(
|
|
@@ -1547,6 +1617,21 @@ export async function createRubyBom(path, options) {
|
|
|
1547
1617
|
// lookup, gives us the sha256 of the exact native build, and unlike the
|
|
1548
1618
|
// registry lookups it also works in dry-run mode.
|
|
1549
1619
|
pkgList = await enrichGemsFromLocalCache(pkgList, { gemHome });
|
|
1620
|
+
// The download URL of a gem from the public registry needs no network call
|
|
1621
|
+
pkgList = addRubyGemsDistributionUrls(pkgList);
|
|
1622
|
+
// Gems declare the Ruby they run on; surface it as the runtime they depend
|
|
1623
|
+
// on. The component says the runtime is external, which only CycloneDX 1.7
|
|
1624
|
+
// can express, and a container image ships its own Ruby.
|
|
1625
|
+
if (
|
|
1626
|
+
options.specVersion >= 1.7 &&
|
|
1627
|
+
!hasAnyProjectType(["oci"], options, false)
|
|
1628
|
+
) {
|
|
1629
|
+
({ pkgList, dependencies } = addRubyRuntimeComponent(
|
|
1630
|
+
pkgList,
|
|
1631
|
+
parentComponent,
|
|
1632
|
+
dependencies,
|
|
1633
|
+
));
|
|
1634
|
+
}
|
|
1550
1635
|
return buildBomNSData(options, pkgList, "gem", {
|
|
1551
1636
|
src: path,
|
|
1552
1637
|
dependencies,
|
|
@@ -1772,49 +1857,88 @@ export async function createCsharpBom(path, options) {
|
|
|
1772
1857
|
const parentDependsOn = new Set();
|
|
1773
1858
|
if (nupkgFiles.length && projAssetsFiles.length === 0) {
|
|
1774
1859
|
manifestFiles = manifestFiles.concat(nupkgFiles);
|
|
1775
|
-
//
|
|
1776
|
-
//
|
|
1777
|
-
|
|
1778
|
-
|
|
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 = {};
|
|
1779
1867
|
for (const nf of nupkgFiles) {
|
|
1780
1868
|
if (DEBUG_MODE) {
|
|
1781
1869
|
console.log(`Parsing ${nf}`);
|
|
1782
1870
|
}
|
|
1783
1871
|
const retMap = await parseNupkg(nf);
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
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);
|
|
1789
1881
|
}
|
|
1790
1882
|
}
|
|
1791
|
-
if (retMap?.
|
|
1792
|
-
|
|
1883
|
+
if (retMap?.dependencyRanges) {
|
|
1884
|
+
dependencyRanges = { ...dependencyRanges, ...retMap.dependencyRanges };
|
|
1793
1885
|
}
|
|
1794
1886
|
} // end for
|
|
1795
|
-
for (const
|
|
1796
|
-
const dependsOn =
|
|
1797
|
-
|
|
1798
|
-
const
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
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() });
|
|
1802
1901
|
}
|
|
1803
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
|
+
};
|
|
1804
1935
|
// In a multi-project scan, different projects may use different manifest
|
|
1805
1936
|
// formats: modern SDK-style (project.assets.json / packages.lock.json) or
|
|
1806
1937
|
// legacy (packages.config). Apply the assets > lock > config precedence per
|
|
1807
1938
|
// project directory instead of across the whole scan, so dependencies from
|
|
1808
1939
|
// one project's manifest are not dropped just because another format exists
|
|
1809
1940
|
// elsewhere in the scan.
|
|
1810
|
-
const assetsProjectDirs = new Set(
|
|
1811
|
-
projAssetsFiles.map((af) => {
|
|
1812
|
-
// project.assets.json is generated under <project>/obj/
|
|
1813
|
-
const d = dirname(af);
|
|
1814
|
-
return basename(d) === "obj" ? dirname(d) : d;
|
|
1815
|
-
}),
|
|
1816
|
-
);
|
|
1817
|
-
const lockProjectDirs = new Set(pkgLockFiles.map((af) => dirname(af)));
|
|
1941
|
+
const assetsProjectDirs = new Set();
|
|
1818
1942
|
// project.assets.json parsing
|
|
1819
1943
|
if (projAssetsFiles.length) {
|
|
1820
1944
|
manifestFiles = manifestFiles.concat(projAssetsFiles);
|
|
@@ -1826,6 +1950,11 @@ export async function createCsharpBom(path, options) {
|
|
|
1826
1950
|
const results = parseCsProjAssetsData(pkgData, af);
|
|
1827
1951
|
const deps = results["dependenciesList"];
|
|
1828
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);
|
|
1829
1958
|
if (dlist?.length) {
|
|
1830
1959
|
pkgList = pkgList.concat(dlist);
|
|
1831
1960
|
}
|
|
@@ -1858,9 +1987,12 @@ export async function createCsharpBom(path, options) {
|
|
|
1858
1987
|
});
|
|
1859
1988
|
}
|
|
1860
1989
|
}
|
|
1990
|
+
const lockProjectDirs = new Set(
|
|
1991
|
+
pkgLockFiles.map((af) => dotnetManifestProjectDir(af)),
|
|
1992
|
+
);
|
|
1861
1993
|
// packages.lock.json parsing for any project without a project.assets.json
|
|
1862
1994
|
const pkgLockToParse = pkgLockFiles.filter(
|
|
1863
|
-
(af) => !assetsProjectDirs.has(
|
|
1995
|
+
(af) => !assetsProjectDirs.has(dotnetManifestProjectDir(af)),
|
|
1864
1996
|
);
|
|
1865
1997
|
if (pkgLockToParse.length) {
|
|
1866
1998
|
manifestFiles = manifestFiles.concat(pkgLockToParse);
|
|
@@ -1874,37 +2006,29 @@ export async function createCsharpBom(path, options) {
|
|
|
1874
2006
|
const deps = results["dependenciesList"];
|
|
1875
2007
|
const dlist = results["pkgList"];
|
|
1876
2008
|
const rootList = results["rootList"];
|
|
2009
|
+
const projectDir = dotnetManifestProjectDir(af);
|
|
2010
|
+
recordProjectVersions(projectDir, dlist);
|
|
1877
2011
|
if (dlist?.length) {
|
|
1878
2012
|
pkgList = pkgList.concat(dlist);
|
|
1879
2013
|
}
|
|
1880
2014
|
if (deps?.length) {
|
|
1881
2015
|
dependencies = mergeDependencies(dependencies, deps, parentComponent);
|
|
1882
2016
|
}
|
|
1883
|
-
|
|
1884
|
-
// list after processing all lock files
|
|
1885
|
-
if (rootList?.length) {
|
|
1886
|
-
for (const p of rootList) {
|
|
1887
|
-
parentDependsOn.add(p["bom-ref"]);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
2017
|
+
recordRoots(projectDir, rootList);
|
|
1890
2018
|
}
|
|
1891
2019
|
}
|
|
1892
2020
|
// packages.config parsing for any project without a modern manifest
|
|
1893
2021
|
// (project.assets.json / packages.lock.json) covering the same directory
|
|
1894
2022
|
const pkgConfigToParse = pkgConfigFiles.filter((f) => {
|
|
1895
|
-
const projDir =
|
|
2023
|
+
const projDir = dotnetManifestProjectDir(f);
|
|
1896
2024
|
return !assetsProjectDirs.has(projDir) && !lockProjectDirs.has(projDir);
|
|
1897
2025
|
});
|
|
1898
2026
|
if (pkgConfigToParse.length) {
|
|
1899
2027
|
manifestFiles = manifestFiles.concat(pkgConfigToParse);
|
|
1900
|
-
//
|
|
1901
|
-
//
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
if (p.name && p.version) {
|
|
1905
|
-
resolvedVersions[p.name] = p.version;
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
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);
|
|
1908
2032
|
// packages.config parsing
|
|
1909
2033
|
for (const f of pkgConfigToParse) {
|
|
1910
2034
|
if (DEBUG_MODE) {
|
|
@@ -1912,11 +2036,11 @@ export async function createCsharpBom(path, options) {
|
|
|
1912
2036
|
}
|
|
1913
2037
|
pkgData = readFileSync(f, { encoding: "utf-8" });
|
|
1914
2038
|
const dlist = parseCsPkgData(pkgData, f, resolvedVersions);
|
|
2039
|
+
const projectDir = dotnetManifestProjectDir(f);
|
|
2040
|
+
recordProjectVersions(projectDir, dlist);
|
|
1915
2041
|
if (dlist?.length) {
|
|
1916
2042
|
pkgList = pkgList.concat(dlist);
|
|
1917
|
-
|
|
1918
|
-
parentDependsOn.add(d["bom-ref"]);
|
|
1919
|
-
}
|
|
2043
|
+
recordRoots(projectDir, dlist);
|
|
1920
2044
|
}
|
|
1921
2045
|
}
|
|
1922
2046
|
}
|
|
@@ -1939,27 +2063,26 @@ export async function createCsharpBom(path, options) {
|
|
|
1939
2063
|
}
|
|
1940
2064
|
}
|
|
1941
2065
|
}
|
|
1942
|
-
const
|
|
2066
|
+
const projectRefByDir = {};
|
|
1943
2067
|
let services = [];
|
|
1944
2068
|
if (csProjFiles.length) {
|
|
1945
2069
|
manifestFiles = manifestFiles.concat(csProjFiles);
|
|
1946
|
-
// Parsing csproj is quite error-prone. Some project files may not have
|
|
1947
|
-
//
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
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);
|
|
1953
2075
|
let msbuildInstalled = false;
|
|
1954
|
-
|
|
1955
|
-
const result = safeSpawnSync(DOTNET_CMD, ["msbuild --version"], {
|
|
2076
|
+
const result = safeSpawnSync(DOTNET_CMD, ["msbuild", "--version"], {
|
|
1956
2077
|
shell: isWin,
|
|
1957
2078
|
});
|
|
1958
|
-
if (result.status === 0 && !result.error && result.stdout
|
|
2079
|
+
if (result.status === 0 && !result.error && result.stdout?.trim()) {
|
|
1959
2080
|
msbuildInstalled = true;
|
|
1960
|
-
} else {
|
|
1961
|
-
pkgVersionLabelCandidates = getPropertyGroupTextNodes(propsFiles);
|
|
1962
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);
|
|
1963
2086
|
// Directory.Packages.props can sit above the directory being scanned, so it is
|
|
1964
2087
|
// resolved per project file by walking up rather than from the props glob below.
|
|
1965
2088
|
// The cache keeps a repository with many projects from re-parsing the same file.
|
|
@@ -1970,15 +2093,19 @@ export async function createCsharpBom(path, options) {
|
|
|
1970
2093
|
console.log(`Parsing ${f}`);
|
|
1971
2094
|
}
|
|
1972
2095
|
const csProjData = readFileSync(f, { encoding: "utf-8" });
|
|
2096
|
+
const projectDir = dotnetManifestProjectDir(f);
|
|
1973
2097
|
const retMap = parseCsProjData(
|
|
1974
2098
|
csProjData,
|
|
1975
2099
|
f,
|
|
1976
|
-
|
|
2100
|
+
projectVersions[projectDir] || {},
|
|
1977
2101
|
msbuildInstalled,
|
|
1978
|
-
|
|
2102
|
+
projectPropertyCandidates(f, propsIndex),
|
|
1979
2103
|
getCentralPackageVersions(f, centralVersionsCache),
|
|
2104
|
+
agreedVersions,
|
|
2105
|
+
projectNames[projectDir] || {},
|
|
1980
2106
|
);
|
|
1981
2107
|
if (retMap?.parentComponent?.purl) {
|
|
2108
|
+
projectRefByDir[projectDir] ??= retMap.parentComponent["bom-ref"];
|
|
1982
2109
|
// If there are multiple project files, track the parent components using nested components
|
|
1983
2110
|
if (csProjFiles.length > 1) {
|
|
1984
2111
|
if (!parentComponent.components) {
|
|
@@ -2002,46 +2129,103 @@ export async function createCsharpBom(path, options) {
|
|
|
2002
2129
|
}
|
|
2003
2130
|
}
|
|
2004
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
|
+
}
|
|
2005
2151
|
if (pkgList.length) {
|
|
2006
2152
|
pkgList = trimComponents(pkgList);
|
|
2007
2153
|
// Perform deep analysis using dosai
|
|
2008
2154
|
if (options.deep) {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
//
|
|
2015
|
-
if
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
);
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
if (!
|
|
2155
|
+
// resolve (not join) so a fully-qualified --deps-slices-file is used
|
|
2156
|
+
// as-is: join would append it to the scan path, which is never a valid
|
|
2157
|
+
// path on Windows and silently loses the .NET evidence elsewhere
|
|
2158
|
+
// (issue 4394). A relative value still resolves against the scan path.
|
|
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) {
|
|
2025
2193
|
console.log(
|
|
2026
|
-
|
|
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.`,
|
|
2027
2195
|
);
|
|
2028
2196
|
}
|
|
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
|
+
}
|
|
2029
2212
|
}
|
|
2030
|
-
pkgList = addEvidenceForDotnet(pkgList, slicesFile);
|
|
2031
|
-
const methodsSlice = readDosaiJsonFile(slicesFile);
|
|
2032
|
-
// Provider-backed Services[] first so bom-refs, trust zones, and data
|
|
2033
|
-
// classifications survive; ApiEndpoints derivation fills in the rest.
|
|
2034
|
-
const servicesMap = collectDosaiServiceComponents(methodsSlice, {});
|
|
2035
|
-
collectDosaiServicesFromMethods(methodsSlice, servicesMap);
|
|
2036
|
-
services = normalizeDosaiServiceMap(servicesMap);
|
|
2037
2213
|
}
|
|
2038
2214
|
}
|
|
2039
|
-
// Parent dependency tree
|
|
2215
|
+
// Parent dependency tree. A single project file already contributed an
|
|
2216
|
+
// entry for the same parent, so the lock file roots are merged into it
|
|
2217
|
+
// rather than added as a second entry for the same ref.
|
|
2040
2218
|
if (parentDependsOn.size && parentComponent?.["bom-ref"]) {
|
|
2041
|
-
dependencies
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2219
|
+
dependencies = mergeDependencies(
|
|
2220
|
+
[
|
|
2221
|
+
{
|
|
2222
|
+
ref: parentComponent["bom-ref"],
|
|
2223
|
+
dependsOn: Array.from(parentDependsOn).sort(),
|
|
2224
|
+
},
|
|
2225
|
+
],
|
|
2226
|
+
dependencies,
|
|
2227
|
+
parentComponent,
|
|
2228
|
+
);
|
|
2045
2229
|
}
|
|
2046
2230
|
if (shouldFetchLicense()) {
|
|
2047
2231
|
const retMap = await getNugetMetadata(pkgList, dependencies);
|
|
@@ -2271,34 +2455,41 @@ export function createPerlBom(path, options) {
|
|
|
2271
2455
|
/**
|
|
2272
2456
|
* Function to create bom string for Terraform and OpenTofu projects.
|
|
2273
2457
|
*
|
|
2274
|
-
* The
|
|
2275
|
-
*
|
|
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.
|
|
2276
2463
|
*
|
|
2277
2464
|
* @param {string} path to the project
|
|
2278
2465
|
* @param {Object} options Parse options from the cli
|
|
2279
|
-
* @returns {Object} BOM object
|
|
2466
|
+
* @returns {Promise<Object>} BOM object
|
|
2280
2467
|
*/
|
|
2281
|
-
export function createTerraformBom(path, options) {
|
|
2282
|
-
|
|
2283
|
-
const lockFiles = getAllFiles(
|
|
2468
|
+
export async function createTerraformBom(path, options) {
|
|
2469
|
+
const parentComponent = createDefaultParentComponent(
|
|
2284
2470
|
path,
|
|
2285
|
-
|
|
2471
|
+
"generic",
|
|
2286
2472
|
options,
|
|
2287
2473
|
);
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
}
|
|
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 {};
|
|
2296
2481
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2482
|
+
parentComponent.properties = [
|
|
2483
|
+
...(parentComponent.properties || []),
|
|
2484
|
+
...parentProperties,
|
|
2485
|
+
];
|
|
2486
|
+
if (pkgList.length && shouldFetchPackageMetadata()) {
|
|
2487
|
+
await getTerraformRegistryMetadata(pkgList);
|
|
2302
2488
|
}
|
|
2303
|
-
return {
|
|
2489
|
+
return buildBomNSData(options, pkgList, "terraform", {
|
|
2490
|
+
src: path,
|
|
2491
|
+
filename: srcFiles.join(", "),
|
|
2492
|
+
dependencies,
|
|
2493
|
+
parentComponent,
|
|
2494
|
+
});
|
|
2304
2495
|
}
|