@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
|
@@ -234,3 +234,70 @@
|
|
|
234
234
|
"registry": $prop($, 'cdx:pypi:registry'),
|
|
235
235
|
"resolvedFrom": $prop($, 'cdx:pypi:resolved_from')
|
|
236
236
|
}
|
|
237
|
+
- id: PKG-012
|
|
238
|
+
name: "Terraform module from mutable VCS reference"
|
|
239
|
+
description: "Terraform modules sourced from git or mercurial without a commit or tag pin are not locked, so the next init can fetch different code"
|
|
240
|
+
severity: high
|
|
241
|
+
category: dependency-source
|
|
242
|
+
dry-run-support: full
|
|
243
|
+
condition: |
|
|
244
|
+
components[
|
|
245
|
+
$hasProp($, 'cdx:tf:kind') and $prop($, 'cdx:tf:kind') = 'module'
|
|
246
|
+
and $hasProp($, 'cdx:tf:module:sourceType')
|
|
247
|
+
and ($prop($, 'cdx:tf:module:sourceType') = 'git' or $prop($, 'cdx:tf:module:sourceType') = 'hg')
|
|
248
|
+
and ($prop($, 'cdx:tf:module:pinning') = 'branch' or $prop($, 'cdx:tf:module:pinning') = 'none')
|
|
249
|
+
]
|
|
250
|
+
location: |
|
|
251
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
252
|
+
message: "Terraform module '{{ name }}@{{ version }}' tracks VCS source '{{ $prop($, 'cdx:tf:module:source') }}' at a mutable reference"
|
|
253
|
+
mitigation: "Pin the module source with `?ref=<commit sha>` or a reviewed, signed tag; the dependency lock file does not cover modules"
|
|
254
|
+
evidence: |
|
|
255
|
+
{
|
|
256
|
+
"source": $prop($, 'cdx:tf:module:source'),
|
|
257
|
+
"sourceType": $prop($, 'cdx:tf:module:sourceType'),
|
|
258
|
+
"ref": $prop($, 'cdx:tf:module:ref'),
|
|
259
|
+
"pinning": $prop($, 'cdx:tf:module:pinning')
|
|
260
|
+
}
|
|
261
|
+
- id: PKG-013
|
|
262
|
+
name: "Terraform registry module not pinned to an exact version"
|
|
263
|
+
description: "Registry modules whose version argument is a range or absent resolve to whatever the registry serves at the next init"
|
|
264
|
+
severity: low
|
|
265
|
+
category: dependency-source
|
|
266
|
+
dry-run-support: full
|
|
267
|
+
condition: |
|
|
268
|
+
components[
|
|
269
|
+
$hasProp($, 'cdx:tf:kind') and $prop($, 'cdx:tf:kind') = 'module'
|
|
270
|
+
and $hasProp($, 'cdx:tf:module:sourceType') and $prop($, 'cdx:tf:module:sourceType') = 'registry'
|
|
271
|
+
and ($prop($, 'cdx:tf:module:pinning') = 'range' or $prop($, 'cdx:tf:module:pinning') = 'none')
|
|
272
|
+
]
|
|
273
|
+
location: |
|
|
274
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
275
|
+
message: "Terraform registry module '{{ $prop($, 'cdx:tf:address') }}' is not pinned to an exact version (pinning: {{ $prop($, 'cdx:tf:module:pinning') }})"
|
|
276
|
+
mitigation: "Set the module's `version` argument to a single exact version and review upgrades deliberately"
|
|
277
|
+
evidence: |
|
|
278
|
+
{
|
|
279
|
+
"constraints": $prop($, 'cdx:tf:constraints'),
|
|
280
|
+
"pinning": $prop($, 'cdx:tf:module:pinning'),
|
|
281
|
+
"address": $prop($, 'cdx:tf:address')
|
|
282
|
+
}
|
|
283
|
+
- id: PKG-014
|
|
284
|
+
name: "Terraform module source embeds credentials"
|
|
285
|
+
description: "A module source string carries a credential such as URL userinfo, a git sshkey parameter or S3 keys, exposing the secret to every reader of the configuration"
|
|
286
|
+
severity: high
|
|
287
|
+
category: dependency-source
|
|
288
|
+
dry-run-support: full
|
|
289
|
+
condition: |
|
|
290
|
+
components[
|
|
291
|
+
$hasProp($, 'cdx:tf:module:credentialInSource')
|
|
292
|
+
and $prop($, 'cdx:tf:module:credentialInSource') = 'true'
|
|
293
|
+
]
|
|
294
|
+
location: |
|
|
295
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
296
|
+
message: "Terraform module '{{ name }}@{{ version }}' resolves from a source that embeds credentials"
|
|
297
|
+
mitigation: "Rotate the exposed credential, move authentication to git credential helpers or an SSH agent, and keep the source URL credential-free"
|
|
298
|
+
evidence: |
|
|
299
|
+
{
|
|
300
|
+
"source": $prop($, 'cdx:tf:module:source'),
|
|
301
|
+
"sourceType": $prop($, 'cdx:tf:module:sourceType'),
|
|
302
|
+
"credentialInSource": $prop($, 'cdx:tf:module:credentialInSource')
|
|
303
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Scala Evinse and scalasem Rules
|
|
2
|
+
# Categories: scala-security, scala-compliance
|
|
3
|
+
# Evaluates cdx:scalasem:* properties and the crypto assets emitted by
|
|
4
|
+
# evinse -l scala and cdxgen --evidence for Scala projects.
|
|
5
|
+
|
|
6
|
+
- id: SCALA-SEC-001
|
|
7
|
+
name: "Scala source uses a weak cryptographic algorithm in runtime code"
|
|
8
|
+
description: "scalasem flagged a weak algorithm, mode or key size at a call site in main or generated sources (MD5, SHA-1, DES, ECB, or a key below the modern floor)"
|
|
9
|
+
severity: medium
|
|
10
|
+
category: scala-security
|
|
11
|
+
dry-run-support: full
|
|
12
|
+
condition: |
|
|
13
|
+
components[
|
|
14
|
+
type = 'cryptographic-asset'
|
|
15
|
+
and $prop($, 'cdx:scalasem:crypto:weak') = 'true'
|
|
16
|
+
and (
|
|
17
|
+
'main' in properties[name = 'cdx:scalasem:usageScopes'].value
|
|
18
|
+
or 'generated' in properties[name = 'cdx:scalasem:usageScopes'].value
|
|
19
|
+
)
|
|
20
|
+
]
|
|
21
|
+
location: |
|
|
22
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
23
|
+
message: "Weak cryptographic algorithm '{{ name }}' used in Scala source"
|
|
24
|
+
mitigation: "Move to the modern equivalent of this algorithm, mode and key size, and confirm no compatibility constraint forces the weak one. The locations name the exact call sites"
|
|
25
|
+
evidence: |
|
|
26
|
+
{
|
|
27
|
+
"algorithm": name,
|
|
28
|
+
"oid": cryptoProperties.oid,
|
|
29
|
+
"api": $prop($, 'cdx:scalasem:crypto:api'),
|
|
30
|
+
"locations": evidence.occurrences.(location & '#' & line)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
- id: SCALA-SEC-002
|
|
34
|
+
name: "Scala source calls an outbound service over plaintext transport"
|
|
35
|
+
description: "scalasem found an outbound HTTP or websocket client whose target URL starts with http:// or ws:// and is not a loopback address, so traffic and credentials cross the network unencrypted"
|
|
36
|
+
severity: medium
|
|
37
|
+
category: scala-security
|
|
38
|
+
dry-run-support: full
|
|
39
|
+
condition: |
|
|
40
|
+
services[
|
|
41
|
+
$exists($filter(endpoints, function($e) {
|
|
42
|
+
($contains($e, 'http://') or $contains($e, 'ws://'))
|
|
43
|
+
and $not($contains($e, /:\/\/(localhost|127\.[0-9.]+|\[::1\])(:|\/|$)/))
|
|
44
|
+
}))
|
|
45
|
+
and $prop($, 'cdx:scalasem:service:kind') != null
|
|
46
|
+
]
|
|
47
|
+
location: |
|
|
48
|
+
{ "name": name, "endpoints": endpoints }
|
|
49
|
+
message: "Outbound service '{{ name }}' is called over plaintext"
|
|
50
|
+
mitigation: "Switch the target to https:// or wss://, or a local socket if the peer is on the same host. The call sites are named by the service's cdx:scalasem:service:location properties"
|
|
51
|
+
evidence: |
|
|
52
|
+
{
|
|
53
|
+
"kind": $prop($, 'cdx:scalasem:service:kind'),
|
|
54
|
+
"client": $prop($, 'cdx:scalasem:service:client'),
|
|
55
|
+
"locations": $filter(properties, function($p) { $p.name = 'cdx:scalasem:service:location' }).value
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- id: SCALA-SEC-003
|
|
59
|
+
name: "Admin-like Scala endpoint is not marked as authenticated"
|
|
60
|
+
description: "scalasem found an inbound route with an admin, internal, manage or debug path segment that the report does not mark as authenticated. The source analysis does not see middleware or gateways, so this is a path-based prompt for review"
|
|
61
|
+
severity: low
|
|
62
|
+
category: scala-security
|
|
63
|
+
dry-run-support: full
|
|
64
|
+
condition: |
|
|
65
|
+
services[
|
|
66
|
+
$exists($filter(endpoints, function($e) { $contains($e, /(^|\/)(admin|internal|manage|debug)(\/|$)/) }))
|
|
67
|
+
and $prop($, 'cdx:scalasem:endpoint:framework') != null
|
|
68
|
+
and $not(authenticated = true)
|
|
69
|
+
]
|
|
70
|
+
location: |
|
|
71
|
+
{ "name": name, "endpoints": endpoints }
|
|
72
|
+
message: "Endpoint '{{ name }}' looks administrative and is not marked as authenticated"
|
|
73
|
+
mitigation: "Confirm the route is protected by middleware or a gateway the source does not show, and otherwise require authentication on it. The declaration site is named by the service's cdx:scalasem:service:location property"
|
|
74
|
+
evidence: |
|
|
75
|
+
{
|
|
76
|
+
"framework": $prop($, 'cdx:scalasem:endpoint:framework'),
|
|
77
|
+
"handler": $prop($, 'cdx:scalasem:endpoint:handler'),
|
|
78
|
+
"locations": $filter(properties, function($p) { $p.name = 'cdx:scalasem:service:location' }).value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
- id: SCALA-SEC-004
|
|
82
|
+
name: "Scala source uses a weak algorithm that has no registry OID"
|
|
83
|
+
description: "scalasem flagged a weak algorithm that the OID registry cannot name, such as an MD5 or SHA-1 based signature or an RC4 password scheme, so it is recorded as a property instead of a crypto asset"
|
|
84
|
+
severity: medium
|
|
85
|
+
category: scala-security
|
|
86
|
+
dry-run-support: full
|
|
87
|
+
condition: |
|
|
88
|
+
$append(components, metadata.component)[
|
|
89
|
+
$exists(properties[name = 'cdx:scalasem:crypto:weakFinding'])
|
|
90
|
+
]
|
|
91
|
+
location: |
|
|
92
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
93
|
+
message: "Weak cryptographic algorithm used in Scala source, recorded on '{{ name }}'"
|
|
94
|
+
mitigation: "Move to a modern algorithm. Each finding names the algorithm and its call site as name@file#line"
|
|
95
|
+
evidence: |
|
|
96
|
+
{
|
|
97
|
+
"findings": properties[name = 'cdx:scalasem:crypto:weakFinding'].value
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- id: SCALA-COMP-001
|
|
101
|
+
name: "Scala semantic evidence is incomplete"
|
|
102
|
+
description: "The scalasem run behind this BOM was degraded, for example by class files no compiler could read, a missing classpath, a timeout or a failed analyzer, so the BOM's Scala evidence may be missing usages, crypto findings or services"
|
|
103
|
+
severity: low
|
|
104
|
+
category: scala-compliance
|
|
105
|
+
dry-run-support: full
|
|
106
|
+
condition: |
|
|
107
|
+
metadata.component[$prop($, 'cdx:scalasem:degraded') = 'true']
|
|
108
|
+
location: |
|
|
109
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
110
|
+
message: "The Scala semantic evidence for '{{ name }}' is incomplete"
|
|
111
|
+
mitigation: "Read the cdx:scalasem:diagnostic:* metadata properties for the cause, and build the project before scanning when the diagnostics name missing class files or a missing classpath"
|
|
112
|
+
evidence: |
|
|
113
|
+
{
|
|
114
|
+
"diagnostics": $filter(properties, function($p) { $contains($p.name, 'cdx:scalasem:diagnostic:') }).(name & '=' & value)
|
|
115
|
+
}
|
package/lib/audit/index.js
CHANGED
|
@@ -390,6 +390,27 @@ function targetSlug(target) {
|
|
|
390
390
|
return `${target.type}-${normalized || "package"}-${version}-${digest}`;
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Build a deterministic directory name for a repository checkout, keyed by
|
|
395
|
+
* the repository URL and the ref it is cloned at.
|
|
396
|
+
*
|
|
397
|
+
* Keying by the coordinates rather than the package means the packages of
|
|
398
|
+
* one monorepo share a single checkout instead of each cloning the whole
|
|
399
|
+
* repository, while a different ref, which is a different snapshot of the
|
|
400
|
+
* tree, never shares one.
|
|
401
|
+
*
|
|
402
|
+
* @param {string} repoUrl Repository URL
|
|
403
|
+
* @param {string | undefined} gitRef Ref the checkout is cloned at
|
|
404
|
+
* @returns {string} slug string
|
|
405
|
+
*/
|
|
406
|
+
function repositoryCheckoutSlug(repoUrl, gitRef) {
|
|
407
|
+
const digest = createHash("sha256")
|
|
408
|
+
.update(`${repoUrl}\n${gitRef || ""}`)
|
|
409
|
+
.digest("hex")
|
|
410
|
+
.slice(0, 16);
|
|
411
|
+
return `repo-${digest}`;
|
|
412
|
+
}
|
|
413
|
+
|
|
393
414
|
/**
|
|
394
415
|
* Ensure a parent directory exists before writing a file.
|
|
395
416
|
*
|
|
@@ -1378,7 +1399,12 @@ async function ensureCheckout(target, resolution, workspaceDir, gitRef) {
|
|
|
1378
1399
|
if (!safeExistsSync(resolvedWorkspaceDir)) {
|
|
1379
1400
|
safeMkdirSync(resolvedWorkspaceDir, { recursive: true });
|
|
1380
1401
|
}
|
|
1381
|
-
|
|
1402
|
+
// One checkout per repository and ref, so packages of the same monorepo
|
|
1403
|
+
// share it; each package still keeps its own cache and report directory.
|
|
1404
|
+
const cloneDir = join(
|
|
1405
|
+
resolvedWorkspaceDir,
|
|
1406
|
+
repositoryCheckoutSlug(resolution.repoUrl, gitRef),
|
|
1407
|
+
);
|
|
1382
1408
|
if (safeExistsSync(join(cloneDir, ".git"))) {
|
|
1383
1409
|
return {
|
|
1384
1410
|
cleanup: false,
|
|
@@ -2635,11 +2661,17 @@ export async function runAuditFromBoms(inputBoms, options) {
|
|
|
2635
2661
|
total: extractedTargets.targets.length,
|
|
2636
2662
|
type: "run:start",
|
|
2637
2663
|
});
|
|
2638
|
-
// Every target's repository lookup is known here, before the first
|
|
2639
|
-
// so they are resolved in one round rather than one per iteration
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2664
|
+
// Every target's repository lookup is known here, before the first
|
|
2665
|
+
// clone, so they are resolved in one round rather than one per iteration
|
|
2666
|
+
// below. A target whose cached child BOM already answers it is resolved
|
|
2667
|
+
// from the workspace alone, so only the targets that will be cloned take
|
|
2668
|
+
// part in the round.
|
|
2669
|
+
const purlsToResolve = extractedTargets.targets
|
|
2670
|
+
.filter((target) => !loadCachedChildBom(options.workspaceDir, target))
|
|
2671
|
+
.map((target) => target.purl);
|
|
2672
|
+
if (purlsToResolve.length) {
|
|
2673
|
+
await prefetchGitUrlSources(purlsToResolve);
|
|
2674
|
+
}
|
|
2643
2675
|
}
|
|
2644
2676
|
for (const [index, target] of extractedTargets.targets.entries()) {
|
|
2645
2677
|
const targetIndex = index + 1;
|
package/lib/cli/index.js
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
parseOpenapiSpecData,
|
|
45
45
|
parsePrivadoFile,
|
|
46
46
|
} from "../ecosystems/parsers-misc.js";
|
|
47
|
+
import { hasTerraformConfiguration } from "../ecosystems/parsers-terraform.js";
|
|
47
48
|
import { isPyLockFile } from "../ecosystems/pylockutils.js";
|
|
48
49
|
import {
|
|
49
50
|
buildDependencyTrackBomRequest,
|
|
@@ -65,6 +66,7 @@ import {
|
|
|
65
66
|
hasHbomProjectType,
|
|
66
67
|
} from "../inventory/hbom.js";
|
|
67
68
|
import { mergeHostInventoryBoms } from "../inventory/hostTopology.js";
|
|
69
|
+
import { orderParentCandidates } from "../inventory/parentSelection.js";
|
|
68
70
|
// Imported directly rather than through the utils.js barrel: purl validity is
|
|
69
71
|
// checked in a hot path and the barrel is already the root of several import
|
|
70
72
|
// cycles.
|
|
@@ -877,6 +879,19 @@ export async function createMultiXBom(pathList, options) {
|
|
|
877
879
|
let components = [];
|
|
878
880
|
let dependencies = [];
|
|
879
881
|
let parentSubComponents = [];
|
|
882
|
+
// Each builder detects the project of its own ecosystem. A builder that sets
|
|
883
|
+
// options.parentComponent to force its project into its own BOM must not
|
|
884
|
+
// make that project the next builder's too, or the next ecosystem's project
|
|
885
|
+
// is lost: every builder starts from the parent the caller gave.
|
|
886
|
+
const callerParent = options.parentComponent;
|
|
887
|
+
const withOwnParent = async (build) => {
|
|
888
|
+
options.parentComponent = callerParent;
|
|
889
|
+
try {
|
|
890
|
+
return await build();
|
|
891
|
+
} finally {
|
|
892
|
+
options.parentComponent = callerParent;
|
|
893
|
+
}
|
|
894
|
+
};
|
|
880
895
|
setActivityContext({
|
|
881
896
|
projectType: Array.isArray(options.projectType)
|
|
882
897
|
? options.projectType.join(",")
|
|
@@ -923,7 +938,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
923
938
|
}
|
|
924
939
|
}
|
|
925
940
|
setProjectTypeActivityContext(exactAiInventoryType || "js", path);
|
|
926
|
-
bomData = await createNodejsBom(path, options);
|
|
941
|
+
bomData = await withOwnParent(() => createNodejsBom(path, options));
|
|
927
942
|
if (bomData?.bomJson?.components?.length) {
|
|
928
943
|
if (exactAiInventoryType) {
|
|
929
944
|
thoughtLog(
|
|
@@ -973,7 +988,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
973
988
|
);
|
|
974
989
|
}
|
|
975
990
|
setProjectTypeActivityContext("java", path);
|
|
976
|
-
bomData = await createJavaBom(path, options);
|
|
991
|
+
bomData = await withOwnParent(() => createJavaBom(path, options));
|
|
977
992
|
if (bomData?.bomJson?.components?.length) {
|
|
978
993
|
thoughtLog(
|
|
979
994
|
`I found ${bomData.bomJson.components.length} java packages.`,
|
|
@@ -1038,7 +1053,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1038
1053
|
);
|
|
1039
1054
|
}
|
|
1040
1055
|
setProjectTypeActivityContext("py", path);
|
|
1041
|
-
bomData = await createPythonBom(path, options);
|
|
1056
|
+
bomData = await withOwnParent(() => createPythonBom(path, options));
|
|
1042
1057
|
if (bomData?.bomJson?.components?.length) {
|
|
1043
1058
|
thoughtLog(
|
|
1044
1059
|
`I found ${bomData.bomJson.components.length} python packages.`,
|
|
@@ -1059,6 +1074,17 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1059
1074
|
) {
|
|
1060
1075
|
parentSubComponents.push(bomData.parentComponent);
|
|
1061
1076
|
}
|
|
1077
|
+
// Retain metadata.component.components. uv workspace members are
|
|
1078
|
+
// nested under the python project, so hoisting them keeps them
|
|
1079
|
+
// siblings of the other sub-components instead of riding one level
|
|
1080
|
+
// deeper (or being dropped by the same-name collapse) when another
|
|
1081
|
+
// ecosystem's project becomes the parent.
|
|
1082
|
+
if (bomData.parentComponent?.components?.length) {
|
|
1083
|
+
parentSubComponents = parentSubComponents.concat(
|
|
1084
|
+
bomData.parentComponent.components,
|
|
1085
|
+
);
|
|
1086
|
+
delete bomData.parentComponent.components;
|
|
1087
|
+
}
|
|
1062
1088
|
}
|
|
1063
1089
|
if (bomData?.formulationList?.length) {
|
|
1064
1090
|
formulationList = formulationList.concat(bomData.formulationList);
|
|
@@ -1071,7 +1097,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1071
1097
|
);
|
|
1072
1098
|
}
|
|
1073
1099
|
setProjectTypeActivityContext("go", path);
|
|
1074
|
-
bomData = await createGoBom(path, options);
|
|
1100
|
+
bomData = await withOwnParent(() => createGoBom(path, options));
|
|
1075
1101
|
if (bomData?.bomJson?.components?.length) {
|
|
1076
1102
|
thoughtLog(`I found ${bomData.bomJson.components.length} go packages.`);
|
|
1077
1103
|
if (DEBUG_MODE) {
|
|
@@ -1099,7 +1125,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1099
1125
|
);
|
|
1100
1126
|
}
|
|
1101
1127
|
setProjectTypeActivityContext("rust", path);
|
|
1102
|
-
bomData = await createRustBom(path, options);
|
|
1128
|
+
bomData = await withOwnParent(() => createRustBom(path, options));
|
|
1103
1129
|
if (bomData?.bomJson?.components?.length) {
|
|
1104
1130
|
thoughtLog(
|
|
1105
1131
|
`I found ${bomData.bomJson.components.length} rust packages.`,
|
|
@@ -1136,7 +1162,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1136
1162
|
);
|
|
1137
1163
|
}
|
|
1138
1164
|
setProjectTypeActivityContext("php", path);
|
|
1139
|
-
bomData = createPHPBom(path, options);
|
|
1165
|
+
bomData = await withOwnParent(() => createPHPBom(path, options));
|
|
1140
1166
|
if (bomData?.bomJson?.components?.length) {
|
|
1141
1167
|
thoughtLog(
|
|
1142
1168
|
`I found ${bomData.bomJson.components.length} php packages.`,
|
|
@@ -1173,7 +1199,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1173
1199
|
);
|
|
1174
1200
|
}
|
|
1175
1201
|
setProjectTypeActivityContext("ruby", path);
|
|
1176
|
-
bomData = await createRubyBom(path, options);
|
|
1202
|
+
bomData = await withOwnParent(() => createRubyBom(path, options));
|
|
1177
1203
|
if (bomData?.bomJson?.components?.length) {
|
|
1178
1204
|
thoughtLog(
|
|
1179
1205
|
`We got ${bomData.bomJson.components.length} ruby packages.`,
|
|
@@ -1209,7 +1235,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1209
1235
|
thoughtLog("**CSHARP**: What about csharp and fsharp projects?");
|
|
1210
1236
|
}
|
|
1211
1237
|
setProjectTypeActivityContext("csharp", path);
|
|
1212
|
-
bomData = await createCsharpBom(path, options);
|
|
1238
|
+
bomData = await withOwnParent(() => createCsharpBom(path, options));
|
|
1213
1239
|
if (bomData?.bomJson?.components?.length) {
|
|
1214
1240
|
thoughtLog(
|
|
1215
1241
|
`There are ${bomData.bomJson.components.length} csharp packages.`,
|
|
@@ -1246,7 +1272,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1246
1272
|
);
|
|
1247
1273
|
}
|
|
1248
1274
|
setProjectTypeActivityContext("dart", path);
|
|
1249
|
-
bomData = await createDartBom(path, options);
|
|
1275
|
+
bomData = await withOwnParent(() => createDartBom(path, options));
|
|
1250
1276
|
if (bomData?.bomJson?.components?.length) {
|
|
1251
1277
|
thoughtLog(
|
|
1252
1278
|
`I found ${bomData.bomJson.components.length} pub packages.`,
|
|
@@ -1276,7 +1302,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1276
1302
|
);
|
|
1277
1303
|
}
|
|
1278
1304
|
setProjectTypeActivityContext("haskell", path);
|
|
1279
|
-
bomData = createHaskellBom(path, options);
|
|
1305
|
+
bomData = await withOwnParent(() => createHaskellBom(path, options));
|
|
1280
1306
|
if (bomData?.bomJson?.components?.length) {
|
|
1281
1307
|
thoughtLog(
|
|
1282
1308
|
`I found ${bomData.bomJson.components.length} hackage packages.`,
|
|
@@ -1306,7 +1332,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1306
1332
|
);
|
|
1307
1333
|
}
|
|
1308
1334
|
setProjectTypeActivityContext("elixir", path);
|
|
1309
|
-
bomData = createElixirBom(path, options);
|
|
1335
|
+
bomData = await withOwnParent(() => createElixirBom(path, options));
|
|
1310
1336
|
if (bomData?.bomJson?.components?.length) {
|
|
1311
1337
|
thoughtLog(
|
|
1312
1338
|
`I found ${bomData.bomJson.components.length} mix packages.`,
|
|
@@ -1336,7 +1362,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1336
1362
|
);
|
|
1337
1363
|
}
|
|
1338
1364
|
setProjectTypeActivityContext("c", path);
|
|
1339
|
-
bomData = createCppBom(path, options);
|
|
1365
|
+
bomData = await withOwnParent(() => createCppBom(path, options));
|
|
1340
1366
|
if (bomData?.bomJson?.components?.length) {
|
|
1341
1367
|
thoughtLog(
|
|
1342
1368
|
`I found ${bomData.bomJson.components.length} cpp packages.`,
|
|
@@ -1358,6 +1384,10 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1358
1384
|
parentSubComponents.push(bomData.parentComponent);
|
|
1359
1385
|
}
|
|
1360
1386
|
}
|
|
1387
|
+
// presets and compilers describe the build, whatever it depends on
|
|
1388
|
+
if (bomData?.formulationList?.length) {
|
|
1389
|
+
formulationList = formulationList.concat(bomData.formulationList);
|
|
1390
|
+
}
|
|
1361
1391
|
}
|
|
1362
1392
|
if (hasAnyProjectType(["oci", "clojure"], options)) {
|
|
1363
1393
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -1366,7 +1396,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1366
1396
|
);
|
|
1367
1397
|
}
|
|
1368
1398
|
setProjectTypeActivityContext("clojure", path);
|
|
1369
|
-
bomData = createClojureBom(path, options);
|
|
1399
|
+
bomData = await withOwnParent(() => createClojureBom(path, options));
|
|
1370
1400
|
if (bomData?.bomJson?.components?.length) {
|
|
1371
1401
|
thoughtLog(
|
|
1372
1402
|
`I found ${bomData.bomJson.components.length} clojure packages.`,
|
|
@@ -1396,7 +1426,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1396
1426
|
);
|
|
1397
1427
|
}
|
|
1398
1428
|
setProjectTypeActivityContext("github", path);
|
|
1399
|
-
bomData = createGitHubBom(path, options);
|
|
1429
|
+
bomData = await withOwnParent(() => createGitHubBom(path, options));
|
|
1400
1430
|
if (bomData?.bomJson?.components?.length) {
|
|
1401
1431
|
thoughtLog(
|
|
1402
1432
|
`I found ${bomData.bomJson.components.length} github action packages as well. Should I convert these to formulation instead 🤔`,
|
|
@@ -1426,7 +1456,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1426
1456
|
);
|
|
1427
1457
|
}
|
|
1428
1458
|
setProjectTypeActivityContext("cloudbuild", path);
|
|
1429
|
-
bomData = createCloudBuildBom(path, options);
|
|
1459
|
+
bomData = await withOwnParent(() => createCloudBuildBom(path, options));
|
|
1430
1460
|
if (bomData?.bomJson?.components?.length) {
|
|
1431
1461
|
thoughtLog(
|
|
1432
1462
|
`I found ${bomData.bomJson.components.length} cloudbuild packages.`,
|
|
@@ -1456,7 +1486,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1456
1486
|
);
|
|
1457
1487
|
}
|
|
1458
1488
|
setProjectTypeActivityContext("swift", path);
|
|
1459
|
-
bomData = await createSwiftBom(path, options);
|
|
1489
|
+
bomData = await withOwnParent(() => createSwiftBom(path, options));
|
|
1460
1490
|
if (bomData?.bomJson?.components?.length) {
|
|
1461
1491
|
thoughtLog(
|
|
1462
1492
|
`I found ${bomData.bomJson.components.length} swift packages here.`,
|
|
@@ -1494,7 +1524,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1494
1524
|
detail:
|
|
1495
1525
|
"Components are being inferred by scanning jar/war/ear files from disk.",
|
|
1496
1526
|
});
|
|
1497
|
-
bomData = await createJarBom(path, options);
|
|
1527
|
+
bomData = await withOwnParent(() => createJarBom(path, options));
|
|
1498
1528
|
if (bomData?.bomJson?.components?.length) {
|
|
1499
1529
|
thoughtLog(
|
|
1500
1530
|
`I found ${bomData.bomJson.components.length} jar packages as well.`,
|
|
@@ -1519,7 +1549,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1519
1549
|
}
|
|
1520
1550
|
if (hasAnyProjectType(["oci", "cocoa"], options)) {
|
|
1521
1551
|
setProjectTypeActivityContext("cocoa", path);
|
|
1522
|
-
bomData = await createCocoaBom(path, options);
|
|
1552
|
+
bomData = await withOwnParent(() => createCocoaBom(path, options));
|
|
1523
1553
|
if (bomData?.bomJson?.components?.length) {
|
|
1524
1554
|
if (DEBUG_MODE) {
|
|
1525
1555
|
console.log(
|
|
@@ -1538,7 +1568,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1538
1568
|
}
|
|
1539
1569
|
if (hasAnyProjectType(["oci", "nix"], options)) {
|
|
1540
1570
|
setProjectTypeActivityContext("nix", path);
|
|
1541
|
-
bomData = await createNixBom(path, options);
|
|
1571
|
+
bomData = await withOwnParent(() => createNixBom(path, options));
|
|
1542
1572
|
if (bomData?.bomJson?.components?.length) {
|
|
1543
1573
|
if (DEBUG_MODE) {
|
|
1544
1574
|
console.log(
|
|
@@ -1560,7 +1590,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1560
1590
|
}
|
|
1561
1591
|
if (hasAnyProjectType(["caxa"], options, false)) {
|
|
1562
1592
|
setProjectTypeActivityContext("caxa", path);
|
|
1563
|
-
bomData = await createCaxaBom(path, options);
|
|
1593
|
+
bomData = await withOwnParent(() => createCaxaBom(path, options));
|
|
1564
1594
|
if (bomData?.bomJson?.components?.length) {
|
|
1565
1595
|
if (DEBUG_MODE) {
|
|
1566
1596
|
console.log(
|
|
@@ -1582,7 +1612,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1582
1612
|
}
|
|
1583
1613
|
if (hasAnyProjectType(["asar"], options, false)) {
|
|
1584
1614
|
setProjectTypeActivityContext("asar", path);
|
|
1585
|
-
bomData = await createAsarBom(path, options);
|
|
1615
|
+
bomData = await withOwnParent(() => createAsarBom(path, options));
|
|
1586
1616
|
if (bomData?.bomJson?.components?.length) {
|
|
1587
1617
|
if (DEBUG_MODE) {
|
|
1588
1618
|
console.log(
|
|
@@ -1604,7 +1634,9 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1604
1634
|
}
|
|
1605
1635
|
if (hasAnyProjectType(["vscode-extension"], options, false)) {
|
|
1606
1636
|
setProjectTypeActivityContext("vscode-extension", path);
|
|
1607
|
-
bomData = await
|
|
1637
|
+
bomData = await withOwnParent(() =>
|
|
1638
|
+
createVscodeExtensionBom(path, options),
|
|
1639
|
+
);
|
|
1608
1640
|
if (bomData?.bomJson?.components?.length) {
|
|
1609
1641
|
if (DEBUG_MODE) {
|
|
1610
1642
|
console.log(
|
|
@@ -1647,7 +1679,9 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1647
1679
|
sharedOptions.__didScanChromeExtensions = true;
|
|
1648
1680
|
}
|
|
1649
1681
|
setProjectTypeActivityContext("chrome-extension", path);
|
|
1650
|
-
bomData = await
|
|
1682
|
+
bomData = await withOwnParent(() =>
|
|
1683
|
+
createChromeExtensionBom(path, options),
|
|
1684
|
+
);
|
|
1651
1685
|
if (bomData?.bomJson?.components?.length) {
|
|
1652
1686
|
if (DEBUG_MODE) {
|
|
1653
1687
|
console.log(
|
|
@@ -1668,6 +1702,34 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1668
1702
|
}
|
|
1669
1703
|
}
|
|
1670
1704
|
}
|
|
1705
|
+
if (hasAnyProjectType(["terraform"], options)) {
|
|
1706
|
+
thoughtLog(
|
|
1707
|
+
"**TERRAFORM**: Looking for Terraform and OpenTofu modules and providers. The registry metadata fetch is opt-in, so I will rely on the lock file, the module manifest and the configuration files.",
|
|
1708
|
+
);
|
|
1709
|
+
setProjectTypeActivityContext("terraform", path);
|
|
1710
|
+
bomData = await withOwnParent(() => createTerraformBom(path, options));
|
|
1711
|
+
if (bomData?.bomJson?.components?.length) {
|
|
1712
|
+
thoughtLog(
|
|
1713
|
+
`I found ${bomData.bomJson.components.length} terraform components. Should I also resolve their licenses from the registry when FETCH_LICENSE is enabled?`,
|
|
1714
|
+
);
|
|
1715
|
+
if (DEBUG_MODE) {
|
|
1716
|
+
console.log(
|
|
1717
|
+
`Found ${bomData.bomJson.components.length} terraform components at ${path}`,
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
components = components.concat(bomData.bomJson.components);
|
|
1721
|
+
dependencies = mergeDependencies(
|
|
1722
|
+
dependencies,
|
|
1723
|
+
bomData.bomJson.dependencies,
|
|
1724
|
+
);
|
|
1725
|
+
if (
|
|
1726
|
+
bomData.parentComponent &&
|
|
1727
|
+
Object.keys(bomData.parentComponent).length
|
|
1728
|
+
) {
|
|
1729
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1671
1733
|
// Collect any crypto keys
|
|
1672
1734
|
if (
|
|
1673
1735
|
options.specVersion >= 1.6 &&
|
|
@@ -1680,7 +1742,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1680
1742
|
);
|
|
1681
1743
|
}
|
|
1682
1744
|
setProjectTypeActivityContext("cbom", path);
|
|
1683
|
-
bomData = await createCryptoCertsBom(path, options);
|
|
1745
|
+
bomData = await withOwnParent(() => createCryptoCertsBom(path, options));
|
|
1684
1746
|
if (bomData?.bomJson?.components?.length) {
|
|
1685
1747
|
thoughtLog(
|
|
1686
1748
|
`I found ${bomData.bomJson.components.length} crypto assets.`,
|
|
@@ -1771,7 +1833,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1771
1833
|
!options.lastWorkingDir.includes("/home/")
|
|
1772
1834
|
) {
|
|
1773
1835
|
setProjectTypeActivityContext("jar", options.lastWorkingDir);
|
|
1774
|
-
bomData = createJarBom(options.lastWorkingDir, options);
|
|
1836
|
+
bomData = await createJarBom(options.lastWorkingDir, options);
|
|
1775
1837
|
if (bomData?.bomJson?.components?.length) {
|
|
1776
1838
|
if (DEBUG_MODE) {
|
|
1777
1839
|
console.log(
|
|
@@ -1797,14 +1859,24 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1797
1859
|
thoughtLog("**METADATA**: Tweaking the parent component hierarchy.");
|
|
1798
1860
|
}
|
|
1799
1861
|
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
1800
|
-
|
|
1862
|
+
// the project whose manifest sits at the scan root describes the
|
|
1863
|
+
// repository; ties keep the order the ecosystems were scanned in
|
|
1864
|
+
parentComponent = orderParentCandidates(
|
|
1865
|
+
parentSubComponents,
|
|
1866
|
+
pathList,
|
|
1867
|
+
options,
|
|
1868
|
+
)[0];
|
|
1801
1869
|
}
|
|
1802
1870
|
// Our naive approach to appending to sub-components could result in same parent being included as a child
|
|
1803
1871
|
// This is filtered out here
|
|
1804
1872
|
parentSubComponents = parentSubComponents.filter(
|
|
1805
1873
|
(c) => c["bom-ref"] !== parentComponent["bom-ref"],
|
|
1806
1874
|
);
|
|
1807
|
-
|
|
1875
|
+
// keep the parent's own sub-components (a CMake project's sub-projects)
|
|
1876
|
+
parentComponent.components = trimComponents([
|
|
1877
|
+
...(parentComponent.components || []),
|
|
1878
|
+
...parentSubComponents,
|
|
1879
|
+
]);
|
|
1808
1880
|
if (
|
|
1809
1881
|
parentComponent.components.length === 1 &&
|
|
1810
1882
|
parentComponent.components[0].name === parentComponent.name &&
|
|
@@ -1814,24 +1886,15 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1814
1886
|
delete parentComponent.components;
|
|
1815
1887
|
}
|
|
1816
1888
|
// Add references between the multiple sub-boms
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
if (!parentDependencies["dependsOn"]) {
|
|
1827
|
-
parentDependencies["dependsOn"] = [];
|
|
1828
|
-
}
|
|
1829
|
-
for (const parentSub of parentSubComponents) {
|
|
1830
|
-
// Issue: 1622. We might have already captured this parent component dependency
|
|
1831
|
-
if (!parentDependencies["dependsOn"].includes(parentSub["bom-ref"])) {
|
|
1832
|
-
parentDependencies["dependsOn"].push(parentSub["bom-ref"]);
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1889
|
+
// mergeDependencies returns fresh objects and unions dependsOn per ref,
|
|
1890
|
+
// so the parent edges are folded in through a single merge
|
|
1891
|
+
const parentRef = parentComponent["bom-ref"];
|
|
1892
|
+
const parentSubRefs = parentSubComponents
|
|
1893
|
+
.map((c) => c["bom-ref"])
|
|
1894
|
+
.filter((ref) => ref && ref !== parentRef);
|
|
1895
|
+
dependencies = mergeDependencies(dependencies, [
|
|
1896
|
+
{ ref: parentRef, dependsOn: parentSubRefs },
|
|
1897
|
+
]);
|
|
1835
1898
|
}
|
|
1836
1899
|
// some cleanup, but not complete
|
|
1837
1900
|
for (const path of pathList) {
|
|
@@ -2464,14 +2527,10 @@ export async function createXBom(path, options) {
|
|
|
2464
2527
|
return createPerlBom(path, options);
|
|
2465
2528
|
}
|
|
2466
2529
|
|
|
2467
|
-
// Terraform and OpenTofu providers
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
options,
|
|
2472
|
-
);
|
|
2473
|
-
if (tfLockFiles.length) {
|
|
2474
|
-
return createTerraformBom(path, options);
|
|
2530
|
+
// Terraform and OpenTofu providers and modules, discovered with the same
|
|
2531
|
+
// patterns and exclusions the workspace assembly uses.
|
|
2532
|
+
if (hasTerraformConfiguration(path, options)) {
|
|
2533
|
+
return await createTerraformBom(path, options);
|
|
2475
2534
|
}
|
|
2476
2535
|
}
|
|
2477
2536
|
|
|
@@ -2861,7 +2920,7 @@ async function createBomForPath(path, options) {
|
|
|
2861
2920
|
return createPerlBom(path, options);
|
|
2862
2921
|
}
|
|
2863
2922
|
if (PROJECT_TYPE_ALIASES["terraform"].includes(projectType[0])) {
|
|
2864
|
-
return createTerraformBom(path, options);
|
|
2923
|
+
return await createTerraformBom(path, options);
|
|
2865
2924
|
}
|
|
2866
2925
|
if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
|
|
2867
2926
|
return await createCaxaBom(path, options);
|