@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.
Files changed (311) hide show
  1. package/README.md +33 -14
  2. package/bin/cdxgen.js +310 -138
  3. package/bin/evinse.js +22 -0
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +119 -15
  6. package/bin/tracebom.js +69 -5
  7. package/bin/validate.js +107 -19
  8. package/bin/verify.js +172 -63
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +85 -0
  11. package/data/rules/dependency-sources.yaml +67 -0
  12. package/data/rules/scala-evidence.yaml +115 -0
  13. package/lib/audit/index.js +38 -6
  14. package/lib/cli/bomAssembly.js +105 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +156 -58
  17. package/lib/cli/jsBom.js +99 -53
  18. package/lib/cli/jvmBom.js +708 -147
  19. package/lib/cli/managedBom.js +415 -224
  20. package/lib/cli/nativeBom.js +264 -44
  21. package/lib/core/activity.js +12 -0
  22. package/lib/core/env.js +22 -15
  23. package/lib/core/fs.js +176 -7
  24. package/lib/core/httpClient.js +159 -24
  25. package/lib/core/metadataProperties.js +97 -0
  26. package/lib/core/pluginRun.js +184 -0
  27. package/lib/core/processTree.js +100 -0
  28. package/lib/core/projectConfig.js +31 -4
  29. package/lib/core/runState.js +39 -0
  30. package/lib/core/toml.js +72 -0
  31. package/lib/ecosystems/asarutils.js +4 -2
  32. package/lib/ecosystems/bunutils.js +274 -108
  33. package/lib/ecosystems/cargoCache.js +231 -8
  34. package/lib/ecosystems/caxa.js +434 -2
  35. package/lib/ecosystems/cmakeResolver.js +76 -21
  36. package/lib/ecosystems/cppBuildContext.js +631 -0
  37. package/lib/ecosystems/cppEvidence.js +399 -51
  38. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  39. package/lib/ecosystems/denoutils.js +93 -66
  40. package/lib/ecosystems/ecosystems.js +1983 -793
  41. package/lib/ecosystems/golem.js +28 -12
  42. package/lib/ecosystems/gradleutils.js +356 -37
  43. package/lib/ecosystems/jsEvidence.js +104 -44
  44. package/lib/ecosystems/kosi.js +31 -7
  45. package/lib/ecosystems/npmutils.js +174 -9
  46. package/lib/ecosystems/parsers-dotnet.js +616 -159
  47. package/lib/ecosystems/parsers-elm.js +46 -28
  48. package/lib/ecosystems/parsers-gleam.js +3 -4
  49. package/lib/ecosystems/parsers-go.js +371 -70
  50. package/lib/ecosystems/parsers-js.js +263 -76
  51. package/lib/ecosystems/parsers-julia.js +2 -3
  52. package/lib/ecosystems/parsers-jvm.js +204 -12
  53. package/lib/ecosystems/parsers-misc.js +929 -216
  54. package/lib/ecosystems/parsers-python.js +167 -24
  55. package/lib/ecosystems/parsers-rust.js +28 -22
  56. package/lib/ecosystems/parsers-terraform.js +1573 -19
  57. package/lib/ecosystems/pythonutils.js +102 -6
  58. package/lib/ecosystems/remote/huggingface.js +3 -0
  59. package/lib/ecosystems/rubyutils.js +1492 -182
  60. package/lib/ecosystems/rusi.js +54 -10
  61. package/lib/ecosystems/sbtutils.js +243 -187
  62. package/lib/ecosystems/scalacliutils.js +342 -0
  63. package/lib/ecosystems/scalasem.js +2618 -0
  64. package/lib/ecosystems/terraformRegistry.js +288 -0
  65. package/lib/ecosystems/utils.js +2 -0
  66. package/lib/ecosystems/vendoredCode.js +156 -0
  67. package/lib/evinser/evinser.js +1008 -191
  68. package/lib/evinser/swiftsem.js +1914 -302
  69. package/lib/helpers/agenticToolutils.js +2 -2
  70. package/lib/helpers/bomSigner.js +1101 -222
  71. package/lib/inventory/aiCollector.js +4 -1
  72. package/lib/inventory/analyzer.js +110 -127
  73. package/lib/inventory/atomUtils.js +703 -43
  74. package/lib/inventory/auditCategories.js +7 -0
  75. package/lib/inventory/buildToolRateLimit.js +144 -0
  76. package/lib/inventory/cacheCommands.js +2 -1
  77. package/lib/inventory/cdxrs.js +103 -12
  78. package/lib/inventory/cmakeBuildDirs.js +254 -0
  79. package/lib/inventory/deps.js +501 -124
  80. package/lib/inventory/depsUtils.js +183 -11
  81. package/lib/inventory/display.js +4 -5
  82. package/lib/inventory/dosai.js +798 -57
  83. package/lib/inventory/dosaiParsers.js +310 -15
  84. package/lib/inventory/dynamic.js +90 -6
  85. package/lib/inventory/evidenceUtils.js +166 -113
  86. package/lib/inventory/fetchBatch.js +690 -55
  87. package/lib/inventory/fetchDiskCache.js +597 -0
  88. package/lib/inventory/forgeEnricher.js +14 -5
  89. package/lib/inventory/jvmLocalRepos.js +917 -0
  90. package/lib/inventory/mcpConfigParser.js +2 -3
  91. package/lib/inventory/parentSelection.js +146 -0
  92. package/lib/inventory/protobom.js +8 -1
  93. package/lib/inventory/purl.js +152 -3
  94. package/lib/inventory/rustFormulationParser.js +3 -4
  95. package/lib/inventory/sbomDocument.js +2 -70
  96. package/lib/inventory/scalaCoords.js +153 -0
  97. package/lib/inventory/source.js +45 -27
  98. package/lib/inventory/spdx.js +101 -0
  99. package/lib/inventory/traceRunner.js +295 -25
  100. package/lib/inventory/tracedCrypto.js +294 -0
  101. package/lib/managers/binary.js +115 -24
  102. package/lib/managers/docker.js +467 -46
  103. package/lib/managers/oci.js +63 -3
  104. package/lib/parsers/cmakeCache.js +69 -0
  105. package/lib/parsers/cmakeLists.js +466 -0
  106. package/lib/parsers/cmakePresets.js +400 -0
  107. package/lib/parsers/compileCommands.js +557 -0
  108. package/lib/parsers/largeJson.js +454 -0
  109. package/lib/parsers/npmrc.js +19 -1
  110. package/lib/parsers/strictJson.js +322 -0
  111. package/lib/parsers/terraformConfig.js +1073 -0
  112. package/lib/parsers/terraformSource.js +652 -0
  113. package/lib/server/server.js +23 -0
  114. package/lib/stages/postgen/introspection/markers.js +1 -1
  115. package/lib/stages/postgen/postgen.js +83 -16
  116. package/lib/stages/postgen/specVersionCompat.js +32 -0
  117. package/lib/validator/bomValidator.js +6 -16
  118. package/lib/validator/index.js +64 -11
  119. package/lib/validator/reporters/console.js +3 -0
  120. package/lib/validator/reporters/json.js +2 -0
  121. package/lib/validator/reporters/sarif.js +7 -0
  122. package/package.json +23 -23
  123. package/types/lib/audit/index.d.ts.map +1 -1
  124. package/types/lib/cli/bomAssembly.d.ts +2 -1
  125. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  126. package/types/lib/cli/cliOptions.d.ts +33 -1
  127. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  128. package/types/lib/cli/index.d.ts.map +1 -1
  129. package/types/lib/cli/jsBom.d.ts.map +1 -1
  130. package/types/lib/cli/jvmBom.d.ts +24 -1
  131. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  132. package/types/lib/cli/managedBom.d.ts +7 -4
  133. package/types/lib/cli/managedBom.d.ts.map +1 -1
  134. package/types/lib/cli/nativeBom.d.ts +3 -1
  135. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  136. package/types/lib/core/activity.d.ts.map +1 -1
  137. package/types/lib/core/env.d.ts +9 -0
  138. package/types/lib/core/env.d.ts.map +1 -1
  139. package/types/lib/core/fs.d.ts +55 -0
  140. package/types/lib/core/fs.d.ts.map +1 -1
  141. package/types/lib/core/httpClient.d.ts +13 -8
  142. package/types/lib/core/httpClient.d.ts.map +1 -1
  143. package/types/lib/core/metadataProperties.d.ts +39 -0
  144. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  145. package/types/lib/core/pluginRun.d.ts +97 -0
  146. package/types/lib/core/pluginRun.d.ts.map +1 -0
  147. package/types/lib/core/processTree.d.ts +36 -0
  148. package/types/lib/core/processTree.d.ts.map +1 -0
  149. package/types/lib/core/projectConfig.d.ts +6 -2
  150. package/types/lib/core/projectConfig.d.ts.map +1 -1
  151. package/types/lib/core/runState.d.ts +29 -0
  152. package/types/lib/core/runState.d.ts.map +1 -0
  153. package/types/lib/core/toml.d.ts +20 -0
  154. package/types/lib/core/toml.d.ts.map +1 -0
  155. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  156. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  157. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  158. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  159. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  160. package/types/lib/ecosystems/caxa.d.ts +14 -0
  161. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  162. package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
  163. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  164. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  165. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  166. package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
  167. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  168. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  169. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  170. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  171. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  172. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  173. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  174. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  175. package/types/lib/ecosystems/gradleutils.d.ts +70 -4
  176. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  177. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  178. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  179. package/types/lib/ecosystems/kosi.d.ts +13 -0
  180. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  181. package/types/lib/ecosystems/npmutils.d.ts +57 -0
  182. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  183. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  184. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  187. package/types/lib/ecosystems/parsers-go.d.ts +54 -2
  188. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  189. package/types/lib/ecosystems/parsers-js.d.ts +16 -5
  190. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  191. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  199. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  200. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  201. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  202. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  203. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  204. package/types/lib/ecosystems/rubyutils.d.ts +164 -3
  205. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/rusi.d.ts +13 -0
  207. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  208. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  209. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  210. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  211. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  212. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  213. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  214. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  215. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  216. package/types/lib/ecosystems/utils.d.ts +2 -2
  217. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  218. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  219. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  220. package/types/lib/evinser/evinser.d.ts +132 -15
  221. package/types/lib/evinser/evinser.d.ts.map +1 -1
  222. package/types/lib/evinser/swiftsem.d.ts +280 -15
  223. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  224. package/types/lib/helpers/bomSigner.d.ts +182 -13
  225. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  226. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  227. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  228. package/types/lib/inventory/atomUtils.d.ts +219 -5
  229. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  230. package/types/lib/inventory/auditCategories.d.ts +3 -0
  231. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  232. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  233. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  234. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  235. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  236. package/types/lib/inventory/cdxrs.d.ts +8 -3
  237. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  238. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  239. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  240. package/types/lib/inventory/deps.d.ts +59 -1
  241. package/types/lib/inventory/deps.d.ts.map +1 -1
  242. package/types/lib/inventory/depsUtils.d.ts +15 -0
  243. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  244. package/types/lib/inventory/display.d.ts.map +1 -1
  245. package/types/lib/inventory/dosai.d.ts +121 -9
  246. package/types/lib/inventory/dosai.d.ts.map +1 -1
  247. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  248. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  249. package/types/lib/inventory/dynamic.d.ts +10 -0
  250. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  251. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  252. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  253. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  254. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  255. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  256. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  257. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  258. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  259. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  260. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/parentSelection.d.ts +34 -0
  262. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  263. package/types/lib/inventory/plugins.d.ts.map +1 -1
  264. package/types/lib/inventory/protobom.d.ts.map +1 -1
  265. package/types/lib/inventory/purl.d.ts +71 -2
  266. package/types/lib/inventory/purl.d.ts.map +1 -1
  267. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  268. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  269. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  270. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  271. package/types/lib/inventory/source.d.ts.map +1 -1
  272. package/types/lib/inventory/spdx.d.ts +9 -0
  273. package/types/lib/inventory/spdx.d.ts.map +1 -1
  274. package/types/lib/inventory/traceRunner.d.ts +59 -1
  275. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  276. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  277. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  278. package/types/lib/managers/binary.d.ts +30 -1
  279. package/types/lib/managers/binary.d.ts.map +1 -1
  280. package/types/lib/managers/docker.d.ts +29 -0
  281. package/types/lib/managers/docker.d.ts.map +1 -1
  282. package/types/lib/managers/oci.d.ts +17 -1
  283. package/types/lib/managers/oci.d.ts.map +1 -1
  284. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  285. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  286. package/types/lib/parsers/cmakeLists.d.ts +77 -0
  287. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  288. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  289. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  290. package/types/lib/parsers/compileCommands.d.ts +115 -0
  291. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  292. package/types/lib/parsers/largeJson.d.ts +52 -0
  293. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  294. package/types/lib/parsers/npmrc.d.ts +5 -0
  295. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  296. package/types/lib/parsers/strictJson.d.ts +46 -0
  297. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  298. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  299. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  300. package/types/lib/parsers/terraformSource.d.ts +66 -0
  301. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  302. package/types/lib/server/server.d.ts.map +1 -1
  303. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  304. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  306. package/types/lib/validator/index.d.ts +18 -1
  307. package/types/lib/validator/index.d.ts.map +1 -1
  308. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  309. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  310. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
  311. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -1,7 +1,7 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { readFileSync, statSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
- import { basename, dirname, join, relative, resolve } from "node:path";
4
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
5
  import process from "node:process";
6
6
 
7
7
  import { build } from "@cdxgen/cdx-purl";
@@ -28,12 +28,19 @@ import {
28
28
  } from "../core/fs.js";
29
29
  import { thoughtLog } from "../core/logger.js";
30
30
  import { dirNameStr, isMac } from "../core/paths.js";
31
+ import { readCrateArchiveValues } from "../ecosystems/cargoCache.js";
31
32
  import {
32
33
  buildDependentPurl,
33
34
  collapseCmakeVersions,
35
+ preferFetchedCmakeDependencies,
34
36
  resolveCmakeContext,
37
+ withoutScrapedFetchDependency,
35
38
  } from "../ecosystems/cmakeResolver.js";
36
- import { getCppModules } from "../ecosystems/cppEvidence.js";
39
+ import {
40
+ emptyCppBuildContext,
41
+ resolveCppBuildContext,
42
+ } from "../ecosystems/cppBuildContext.js";
43
+ import { getCppModules, hasCppSources } from "../ecosystems/cppEvidence.js";
37
44
  import {
38
45
  getCratesMetadata,
39
46
  getSwiftPackageMetadata,
@@ -48,6 +55,7 @@ import {
48
55
  } from "../ecosystems/parsers-elm.js";
49
56
  import { parseGleamProject } from "../ecosystems/parsers-gleam.js";
50
57
  import {
58
+ isGoModWhyNotNeeded,
51
59
  parseGoListDep,
52
60
  parseGoModData,
53
61
  parseGoModGraph,
@@ -55,6 +63,7 @@ import {
55
63
  parseGoModWhy,
56
64
  parseGopkgData,
57
65
  parseGosumData,
66
+ parseGosumHashes,
58
67
  } from "../ecosystems/parsers-go.js";
59
68
  import { parseJuliaProject } from "../ecosystems/parsers-julia.js";
60
69
  import {
@@ -69,6 +78,7 @@ import {
69
78
  } from "../ecosystems/parsers-lua.js";
70
79
  import {
71
80
  buildObjectForCocoaPod,
81
+ classifySwiftDependencyScopes,
72
82
  executeAlpmList,
73
83
  executeApkList,
74
84
  executeDpkgList,
@@ -111,7 +121,9 @@ import {
111
121
  } from "../ecosystems/parsers-rust.js";
112
122
  import { parseSpackLock } from "../ecosystems/parsers-spack.js";
113
123
  import { parseStackProject } from "../ecosystems/parsers-stack.js";
124
+ import { findVendoredCode } from "../ecosystems/vendoredCode.js";
114
125
  import { resolveZigGraph } from "../ecosystems/zigResolver.js";
126
+ import { cmakeBuildDirCandidates } from "../inventory/cmakeBuildDirs.js";
115
127
  import { mergeDependencies, trimComponents } from "../inventory/depsUtils.js";
116
128
  import { convertOSQueryResults } from "../inventory/evidenceUtils.js";
117
129
  import { applyPurl, nixBomRef, tryBuildPurl } from "../inventory/purl.js";
@@ -196,7 +208,6 @@ export function getCargoCacheDir() {
196
208
  export async function createGoBom(path, options) {
197
209
  let pkgList = [];
198
210
  let dependencies = [];
199
- const allImports = {};
200
211
  let parentComponent = createDefaultParentComponent(path, "golang", options);
201
212
  // Is this a binary file
202
213
  let maybeBinary;
@@ -236,6 +247,8 @@ export async function createGoBom(path, options) {
236
247
  }
237
248
  }
238
249
  const doneList = {};
250
+ const requiredModules = new Set();
251
+ const unneededModules = new Set();
239
252
  let circuitBreak = false;
240
253
  if (DEBUG_MODE) {
241
254
  console.log(
@@ -249,12 +262,12 @@ export async function createGoBom(path, options) {
249
262
  }
250
263
  const pkgFullName = `${apkg.name}`;
251
264
  if (apkg.scope === "required") {
252
- allImports[pkgFullName] = true;
265
+ requiredModules.add(pkgFullName);
253
266
  continue;
254
267
  }
255
268
  if (
256
269
  apkg.scope === "optional" ||
257
- allImports[pkgFullName] ||
270
+ requiredModules.has(pkgFullName) ||
258
271
  doneList[pkgFullName]
259
272
  ) {
260
273
  continue;
@@ -283,19 +296,40 @@ export async function createGoBom(path, options) {
283
296
  const mstdout = mresult.stdout;
284
297
  if (mstdout) {
285
298
  const cmdOutput = Buffer.from(mstdout).toString();
286
- const whyPkg = parseGoModWhy(cmdOutput);
287
- // whyPkg would include this package string
288
- // github.com/golang/protobuf/proto github.com/golang/protobuf
289
- // golang.org/x/tools/cmd/goimports golang.org/x/tools
290
- if (whyPkg?.includes(pkgFullName)) {
291
- allImports[pkgFullName] = true;
299
+ if (isGoModWhyNotNeeded(cmdOutput)) {
300
+ unneededModules.add(pkgFullName);
301
+ } else {
302
+ // The last line of the import chain is a package of the module:
303
+ // github.com/golang/protobuf/proto for github.com/golang/protobuf
304
+ const whyPkg = parseGoModWhy(cmdOutput);
305
+ if (
306
+ whyPkg === pkgFullName ||
307
+ whyPkg?.startsWith(`${pkgFullName}/`)
308
+ ) {
309
+ requiredModules.add(pkgFullName);
310
+ }
292
311
  }
293
312
  doneList[pkgFullName] = true;
294
313
  }
295
314
  }
296
315
  }
316
+ // A module go reports as unneeded is optional, but only when every module
317
+ // was checked: once go mod why fails, the answers are incomplete, and a
318
+ // tool failure is not evidence that a module is unused.
319
+ for (const apkg of pkgList) {
320
+ if (apkg.scope) {
321
+ continue;
322
+ }
323
+ if (requiredModules.has(apkg.name)) {
324
+ apkg.scope = "required";
325
+ } else if (!circuitBreak && unneededModules.has(apkg.name)) {
326
+ apkg.scope = "optional";
327
+ }
328
+ }
297
329
  if (DEBUG_MODE) {
298
- console.log(`Required packages: ${Object.keys(allImports).length}`);
330
+ console.log(
331
+ `Required packages: ${requiredModules.size}, not needed: ${circuitBreak ? "unknown" : unneededModules.size}`,
332
+ );
299
333
  }
300
334
  return buildBomNSData(options, pkgList, "golang", {
301
335
  src: path,
@@ -313,12 +347,9 @@ export async function createGoBom(path, options) {
313
347
  console.log(`Parsing ${f}`);
314
348
  }
315
349
  const gosumData = readFileSync(f, { encoding: "utf-8" });
316
- const dlist = await parseGosumData(gosumData);
317
- if (dlist?.length) {
318
- dlist.forEach((pkg) => {
319
- gosumMap[`${pkg.name}@${pkg.version}`] = pkg._integrity;
320
- });
321
- }
350
+ // Only the hashes are needed here; reading them costs no registry
351
+ // round trip. The components come from go.mod and the go list output.
352
+ Object.assign(gosumMap, parseGosumHashes(gosumData));
322
353
  }
323
354
  }
324
355
 
@@ -560,7 +591,6 @@ export async function createGoBom(path, options) {
560
591
  }
561
592
  if (pkgList.length && !shouldManuallyParse) {
562
593
  return buildBomNSData(options, pkgList, "golang", {
563
- allImports,
564
594
  dependencies,
565
595
  parentComponent,
566
596
  src: path,
@@ -1138,7 +1168,9 @@ export function buildCargoCacheComponent(crateFile) {
1138
1168
 
1139
1169
  /**
1140
1170
  * Enriches a cargo cache component with a SHA-256 hash and filename-based
1141
- * identity evidence (confidence 0.5). Hash computation failures are ignored.
1171
+ * identity evidence (confidence 0.5), and with what the archive's own manifest
1172
+ * says: its licence, description and repository, read from the archive without
1173
+ * extracting it anywhere. Hash computation failures are ignored.
1142
1174
  *
1143
1175
  * @param {string} crateFile Absolute path to the `.crate` archive
1144
1176
  * @param {object} [component] Component produced by `buildCargoCacheComponent`
@@ -1158,6 +1190,33 @@ export async function enrichCargoCacheComponent(crateFile, component) {
1158
1190
  } catch {
1159
1191
  // continue without hashes
1160
1192
  }
1193
+ const archive = await readCrateArchiveValues(
1194
+ crateFile,
1195
+ component.name,
1196
+ component.version,
1197
+ );
1198
+ if (archive) {
1199
+ if (archive.values.license && !component.license) {
1200
+ component.license = archive.values.license;
1201
+ }
1202
+ if (archive.values.licenses && !component.licenses) {
1203
+ component.licenses = archive.values.licenses;
1204
+ }
1205
+ if (archive.values.description && !component.description) {
1206
+ component.description = archive.values.description;
1207
+ }
1208
+ if (archive.values.repository && !component.repository) {
1209
+ component.repository = { url: archive.values.repository };
1210
+ }
1211
+ if (archive.values.homepage && !component.homepage) {
1212
+ component.homepage = { url: archive.values.homepage };
1213
+ }
1214
+ component.properties = component.properties || [];
1215
+ component.properties.push({
1216
+ name: "cdx:cargo:metadataSource",
1217
+ value: "local-registry",
1218
+ });
1219
+ }
1161
1220
  component.evidence = {
1162
1221
  identity: {
1163
1222
  field: "purl",
@@ -1340,6 +1399,7 @@ export function createCppBom(path, options) {
1340
1399
  }
1341
1400
  let pkgList = [];
1342
1401
  let parentComponentDependencies = [];
1402
+ const subProjects = [];
1343
1403
  if (conanLockFiles.length) {
1344
1404
  for (const f of conanLockFiles) {
1345
1405
  if (DEBUG_MODE) {
@@ -1415,22 +1475,31 @@ export function createCppBom(path, options) {
1415
1475
  }
1416
1476
  if (cmakeLikeFiles.length) {
1417
1477
  const beforeCmake = pkgList.length;
1478
+ // The files at the scan root name the project, so they are read before
1479
+ // the files of any sub-project (the sort is stable otherwise). The scan
1480
+ // root may be given as a relative path while the files come back absolute.
1481
+ const scanRoot = resolve(path);
1482
+ const atScanRoot = (f) => resolve(dirname(f)) === scanRoot;
1483
+ cmakeLikeFiles.sort(
1484
+ (a, b) => (atScanRoot(a) ? 0 : 1) - (atScanRoot(b) ? 0 : 1),
1485
+ );
1418
1486
  for (const f of cmakeLikeFiles) {
1419
1487
  if (DEBUG_MODE) {
1420
1488
  console.log(`Parsing ${f}`);
1421
1489
  }
1422
- const basePath = dirname(f);
1423
1490
  const retMap = parseCmakeLikeFile(f, "generic");
1424
1491
  if (retMap.pkgList?.length) {
1425
1492
  pkgList = pkgList.concat(retMap.pkgList);
1426
1493
  }
1427
1494
  if (
1428
- basePath === path &&
1495
+ atScanRoot(f) &&
1429
1496
  retMap.parentComponent &&
1430
1497
  Object.keys(retMap.parentComponent).length
1431
1498
  ) {
1432
1499
  if (!parentComponent) {
1433
1500
  parentComponent = retMap.parentComponent;
1501
+ // a sub-project reusing the root project's name is the root itself
1502
+ addedParentComponentsMap[retMap.parentComponent.name] = true;
1434
1503
  } else {
1435
1504
  parentComponent.components = parentComponent.components || [];
1436
1505
  if (!addedParentComponentsMap[retMap.parentComponent.name]) {
@@ -1443,8 +1512,18 @@ export function createCppBom(path, options) {
1443
1512
  Object.keys(retMap.parentComponent).length &&
1444
1513
  !addedParentComponentsMap[retMap.parentComponent.name]
1445
1514
  ) {
1515
+ // a project() below the scan root is a part of this project, not a
1516
+ // dependency of it
1446
1517
  retMap.parentComponent.type = "library";
1447
- pkgList.push(retMap.parentComponent);
1518
+ const subprojectDir = relative(scanRoot, resolve(dirname(f)))
1519
+ .split(sep)
1520
+ .join("/");
1521
+ retMap.parentComponent.properties = [
1522
+ ...(retMap.parentComponent.properties || []),
1523
+ { name: "cdx:cmake:subprojectDir", value: subprojectDir || "." },
1524
+ ];
1525
+ subProjects.push(retMap.parentComponent);
1526
+ addedParentComponentsMap[retMap.parentComponent.name] = true;
1448
1527
  }
1449
1528
  // Retain the dependency tree from cmake
1450
1529
  if (retMap.dependenciesList) {
@@ -1464,7 +1543,11 @@ export function createCppBom(path, options) {
1464
1543
  // resolved versions and are left as they are.
1465
1544
  pkgList = pkgList
1466
1545
  .slice(0, beforeCmake)
1467
- .concat(collapseCmakeVersions(pkgList.slice(beforeCmake)));
1546
+ .concat(
1547
+ collapseCmakeVersions(
1548
+ preferFetchedCmakeDependencies(pkgList.slice(beforeCmake)),
1549
+ ),
1550
+ );
1468
1551
  }
1469
1552
  // CMake describes how a project is built from source. An image or an OS
1470
1553
  // installation carries build output, not a build tree, so resolving CMake
@@ -1483,6 +1566,29 @@ export function createCppBom(path, options) {
1483
1566
  boundaries: new Map(),
1484
1567
  }
1485
1568
  : resolveCmakeContext(path, options);
1569
+ // How the project is built: its presets and compilers (formulation), its
1570
+ // hardening options (project properties) and its own include directories.
1571
+ const isImageScan = hasAnyProjectType(
1572
+ ["docker", "oci", "container", "os"],
1573
+ options,
1574
+ false,
1575
+ );
1576
+ // Code carried in the tree under a license of its own, outside the sources
1577
+ // of the dependencies CMake already knows and the build trees
1578
+ const vendored = isImageScan
1579
+ ? { components: [], dirs: [] }
1580
+ : findVendoredCode(path, options, [
1581
+ ...[...(cmakeContext.boundaries || new Map())]
1582
+ .filter(([, e]) => e?.kind === "fetch" || e?.kind === "submodule")
1583
+ .map(([dir]) => resolve(path, dir)),
1584
+ ...cmakeBuildDirCandidates(path, options),
1585
+ ]);
1586
+ if (vendored.components.length) {
1587
+ pkgList = pkgList.concat(vendored.components);
1588
+ }
1589
+ const buildContext = isImageScan
1590
+ ? emptyCppBuildContext()
1591
+ : resolveCppBuildContext(path, options, cmakeContext, vendored.dirs);
1486
1592
  if (cmakeContext.rootProject?.version && parentComponent) {
1487
1593
  const resolvedVersion = cmakeContext.rootProject.version;
1488
1594
  const scrapedVersion = parentComponent.version;
@@ -1554,6 +1660,7 @@ export function createCppBom(path, options) {
1554
1660
  { name: "cdx:cmake:resolvedVia", value: "gitclone-script" },
1555
1661
  ],
1556
1662
  };
1663
+ pkgList = withoutScrapedFetchDependency(pkgList, dep.name);
1557
1664
  if (dep.sourceDir) {
1558
1665
  depComponent.properties.push({
1559
1666
  name: "cdx:cmake:sourceDir",
@@ -1611,30 +1718,44 @@ export function createCppBom(path, options) {
1611
1718
  // We collect OS packages with the word dev in the name using osquery here
1612
1719
  // rpm, deb and ebuild are supported
1613
1720
  // TODO: For archlinux and alpine users we need a different mechanism to collect this information
1614
- for (const queryCategory of Object.keys(cosDbQueries)) {
1615
- const queryObj = cosDbQueries[queryCategory];
1616
- const results = executeOsQuery(queryObj.query);
1617
- const dlist = convertOSQueryResults(
1618
- queryCategory,
1619
- queryObj,
1620
- results,
1621
- true,
1622
- {
1623
- deb: executeDpkgList,
1624
- rpm: executeRpmList,
1625
- apk: executeApkList,
1626
- ebuild: executeEqueryList,
1627
- alpm: executeAlpmList,
1628
- },
1629
- );
1630
- if (dlist?.length) {
1631
- osPkgsList = osPkgsList.concat(dlist);
1721
+ // The packages only resolve the headers named by the usages slice, so
1722
+ // they are not queried when there is neither a slice to reuse nor a C or
1723
+ // C++ source for atom to read.
1724
+ if (
1725
+ (options.usagesSlicesFile && safeExistsSync(options.usagesSlicesFile)) ||
1726
+ hasCppSources(path, options)
1727
+ ) {
1728
+ for (const queryCategory of Object.keys(cosDbQueries)) {
1729
+ const queryObj = cosDbQueries[queryCategory];
1730
+ const results = executeOsQuery(queryObj.query);
1731
+ const dlist = convertOSQueryResults(
1732
+ queryCategory,
1733
+ queryObj,
1734
+ results,
1735
+ true,
1736
+ {
1737
+ deb: executeDpkgList,
1738
+ rpm: executeRpmList,
1739
+ apk: executeApkList,
1740
+ ebuild: executeEqueryList,
1741
+ alpm: executeAlpmList,
1742
+ },
1743
+ );
1744
+ if (dlist?.length) {
1745
+ osPkgsList = osPkgsList.concat(dlist);
1746
+ }
1632
1747
  }
1633
1748
  }
1634
1749
  // Now we check with atom and attempt to detect all external modules via usages
1635
1750
  // We pass the current list of packages so that we enhance the current list and replace
1636
1751
  // components inadvertently. For example, we might resolved a name, version and url information already via cmake
1637
- const retMap = getCppModules(path, options, osPkgsList, pkgList);
1752
+ const retMap = getCppModules(
1753
+ path,
1754
+ options,
1755
+ osPkgsList,
1756
+ pkgList,
1757
+ buildContext.isFirstPartyHeader,
1758
+ );
1638
1759
  if (retMap.pkgList?.length) {
1639
1760
  pkgList = pkgList.concat(retMap.pkgList);
1640
1761
  }
@@ -1678,6 +1799,33 @@ export function createCppBom(path, options) {
1678
1799
  ]);
1679
1800
  }
1680
1801
 
1802
+ if (subProjects.length) {
1803
+ if (parentComponent) {
1804
+ parentComponent.components = parentComponent.components || [];
1805
+ for (const sub of subProjects) {
1806
+ if (
1807
+ !parentComponent.components.some(
1808
+ (c) => c["bom-ref"] === sub["bom-ref"],
1809
+ ) &&
1810
+ sub["bom-ref"] !== parentComponent["bom-ref"]
1811
+ ) {
1812
+ parentComponent.components.push(sub);
1813
+ }
1814
+ }
1815
+ } else {
1816
+ // with no project at the scan root there is nothing to nest them under
1817
+ pkgList = pkgList.concat(subProjects);
1818
+ }
1819
+ }
1820
+ if (parentComponent && buildContext.parentProperties.length) {
1821
+ parentComponent.properties = (parentComponent.properties || []).filter(
1822
+ (p) =>
1823
+ !buildContext.parentProperties.some(
1824
+ (bp) => bp.name === p.name && bp.value === p.value,
1825
+ ),
1826
+ );
1827
+ parentComponent.properties.push(...buildContext.parentProperties);
1828
+ }
1681
1829
  if (parentComponent) {
1682
1830
  sanitizeUnresolvedPurl(parentComponent);
1683
1831
  if (parentComponent.components) {
@@ -1694,6 +1842,7 @@ export function createCppBom(path, options) {
1694
1842
  src: path,
1695
1843
  parentComponent,
1696
1844
  dependencies,
1845
+ formulationList: buildContext.formulationComponents,
1697
1846
  });
1698
1847
  }
1699
1848
 
@@ -2024,6 +2173,50 @@ export async function createSwiftBom(path, options) {
2024
2173
  let parentComponent = {};
2025
2174
  const completedPath = [];
2026
2175
  let packageArgsMessageShown = false;
2176
+ // Packages that only the root package's tests use are scoped optional so
2177
+ // that `--required-only` can narrow the BOM. A package some project (or
2178
+ // some production path) needs stays required.
2179
+ const swiftScopes = {
2180
+ optionalRefs: new Set(),
2181
+ requiredRefs: new Set(),
2182
+ optionalNames: new Set(),
2183
+ requiredNames: new Set(),
2184
+ };
2185
+ const classifiedPaths = new Set();
2186
+ const classifyScopes = (basePath, swiftCommand, treeData) => {
2187
+ if (classifiedPaths.has(basePath)) {
2188
+ return;
2189
+ }
2190
+ classifiedPaths.add(basePath);
2191
+ const dumpArgs =
2192
+ swiftCommand === "xcrun"
2193
+ ? ["swift", "package", "dump-package"]
2194
+ : ["package", "dump-package"];
2195
+ const dumpResult = safeSpawnSync(swiftCommand, dumpArgs, {
2196
+ cwd: basePath,
2197
+ });
2198
+ if (!dumpResult?.stdout) {
2199
+ return;
2200
+ }
2201
+ let dumpJson;
2202
+ let treeJson;
2203
+ try {
2204
+ dumpJson = JSON.parse(Buffer.from(dumpResult.stdout).toString());
2205
+ } catch (_e) {
2206
+ return;
2207
+ }
2208
+ try {
2209
+ treeJson = treeData ? JSON.parse(treeData) : undefined;
2210
+ } catch (_e) {
2211
+ treeJson = undefined;
2212
+ }
2213
+ const scopes = classifySwiftDependencyScopes(dumpJson, treeJson);
2214
+ for (const key of Object.keys(swiftScopes)) {
2215
+ for (const value of scopes[key]) {
2216
+ swiftScopes[key].add(value);
2217
+ }
2218
+ }
2219
+ };
2027
2220
  if (pkgResolvedFiles.length) {
2028
2221
  for (const f of pkgResolvedFiles) {
2029
2222
  if (!parentComponent || !Object.keys(parentComponent).length) {
@@ -2147,6 +2340,33 @@ export async function createSwiftBom(path, options) {
2147
2340
  detail: "the swift package command failed",
2148
2341
  });
2149
2342
  }
2343
+ // Scope classification also works without the dependency graph
2344
+ classifyScopes(basePath, swiftCommand, treeData);
2345
+ }
2346
+ }
2347
+ // Scope packages that only tests use as optional. A package some project or
2348
+ // production path needs keeps its default (required) scope; evidence-based
2349
+ // filtering after generation can still promote an optional package when
2350
+ // the sources are found to use it.
2351
+ for (const apkg of pkgList) {
2352
+ if (apkg?.scope || !apkg?.name) {
2353
+ continue;
2354
+ }
2355
+ const refs = [apkg["bom-ref"], apkg.purl].filter(Boolean);
2356
+ const names = [String(apkg.name).toLowerCase()];
2357
+ for (const aproperty of apkg.properties || []) {
2358
+ if (aproperty?.name === "cdx:swift:packageName" && aproperty.value) {
2359
+ names.push(String(aproperty.value).toLowerCase());
2360
+ }
2361
+ }
2362
+ const isOptional =
2363
+ refs.some((r) => swiftScopes.optionalRefs.has(r)) ||
2364
+ names.some((n) => swiftScopes.optionalNames.has(n));
2365
+ const isRequired =
2366
+ refs.some((r) => swiftScopes.requiredRefs.has(r)) ||
2367
+ names.some((n) => swiftScopes.requiredNames.has(n));
2368
+ if (isOptional && !isRequired) {
2369
+ apkg.scope = "optional";
2150
2370
  }
2151
2371
  }
2152
2372
  if (shouldFetchLicense()) {
@@ -2217,7 +2437,7 @@ export async function createCocoaBom(path, options) {
2217
2437
  "application",
2218
2438
  );
2219
2439
  const podfileLock = loadYaml(readFileSync(lockFile, "utf-8"));
2220
- const pods = await parsePodfileLock(podfileLock, projectPath);
2440
+ const pods = await parsePodfileLock(podfileLock, projectPath, path);
2221
2441
  const allObjects = new Map();
2222
2442
  if (options) {
2223
2443
  // Every remote podspec the scan below reads is known from the lockfile,
@@ -285,6 +285,18 @@ const MANIFEST_ACTIVITY_HINTS = new Map([
285
285
  ],
286
286
  ]);
287
287
  const SENSITIVE_CONFIG_ACTIVITY_HINTS = [
288
+ {
289
+ matcher: (_lowerPath, baseName) =>
290
+ baseName === ".terraformrc" ||
291
+ baseName === "terraform.rc" ||
292
+ baseName === "credentials.tfrc.json",
293
+ metadata: {
294
+ classification: "credential",
295
+ ecosystem: "terraform",
296
+ label: "Terraform CLI configuration",
297
+ sensitive: true,
298
+ },
299
+ },
288
300
  {
289
301
  matcher: (lowerPath, _baseName) =>
290
302
  lowerPath.includes("/.cargo/config.toml") ||
package/lib/core/env.js CHANGED
@@ -111,19 +111,27 @@ export function shouldFetchLicense() {
111
111
  );
112
112
  }
113
113
 
114
+ /**
115
+ * Determines whether registry provenance, such as publish times, publishers
116
+ * and trusted-publishing signals, is wanted. `--bom-audit` turns it on through
117
+ * CDXGEN_FETCH_PKG_METADATA. Local data cannot answer these questions, so a
118
+ * package whose licence and description are known locally is still looked up.
119
+ *
120
+ * @returns {boolean} True if CDXGEN_FETCH_PKG_METADATA is "true" or "1"
121
+ */
122
+ export function shouldFetchRegistryProvenance() {
123
+ return ["true", "1"].includes(
124
+ readEnvironmentVariable("CDXGEN_FETCH_PKG_METADATA"),
125
+ );
126
+ }
127
+
114
128
  /**
115
129
  * Determines whether remote package metadata should be fetched for enrichment.
116
130
  *
117
131
  * @returns {boolean} True when registry metadata enrichment is enabled.
118
132
  */
119
133
  export function shouldFetchPackageMetadata() {
120
- return (
121
- shouldFetchLicense() ||
122
- (readEnvironmentVariable("CDXGEN_FETCH_PKG_METADATA") &&
123
- ["true", "1"].includes(
124
- readEnvironmentVariable("CDXGEN_FETCH_PKG_METADATA"),
125
- ))
126
- );
134
+ return shouldFetchLicense() || shouldFetchRegistryProvenance();
127
135
  }
128
136
 
129
137
  /**
@@ -457,7 +465,7 @@ export const PROJECT_TYPE_ALIASES = {
457
465
  perl: ["perl", "cpan", "carton", "cpanfile"],
458
466
  spack: ["spack"],
459
467
  terraform: ["terraform", "opentofu", "tf"],
460
- c: ["c", "cpp", "c++", "conan", "collider"],
468
+ c: ["c", "cpp", "c++", "conan", "collider", "cmake", "meson", "vcpkg"],
461
469
  clojure: ["clojure", "edn", "clj", "leiningen"],
462
470
  github: ["github", "actions"],
463
471
  hbom: ["hbom", "hardware"],
@@ -491,9 +499,9 @@ export const PROJECT_TYPE_ALIASES = {
491
499
  "visionos",
492
500
  ],
493
501
  binary: ["binary", "blint"],
494
- oci: ["docker", "oci", "container", "podman", "rootfs", "oci-dir"],
502
+ oci: ["docker", "image", "oci", "container", "podman", "rootfs", "oci-dir"],
495
503
  cocoa: ["cocoa", "cocoapods", "objective-c", "swift", "ios"],
496
- scala: ["scala", "scala3", "sbt", "mill"],
504
+ scala: ["scala", "scala3", "sbt", "mill", "scala-cli"],
497
505
  nix: ["nix", "nixos", "flake"],
498
506
  zig: ["zig", "zon"],
499
507
  gleam: ["gleam"],
@@ -643,11 +651,10 @@ export function hasAnyProjectType(projectTypes, options, defaultStatus = true) {
643
651
  const allProjectTypes = [...projectTypes];
644
652
  // Convert the project types into base types
645
653
  const baseProjectTypes = [];
646
- // Support for arbitrary versioned ruby type
647
- if (
648
- options.projectType?.length &&
649
- projectTypes.filter((p) => p.startsWith("ruby")).length
650
- ) {
654
+ // Support for arbitrary versioned ruby type: a requested ruby3.4.1 is a
655
+ // ruby project. The test is on the requested types; testing the queried
656
+ // ones made every scan a ruby scan whenever "ruby" was asked about.
657
+ if (options.projectType?.some((p) => p.startsWith("ruby"))) {
651
658
  baseProjectTypes.push("ruby");
652
659
  }
653
660
  // Versioned JVM build tool types such as maven3.9.9 or gradle8.14 are java