@cdxgen/cdxgen 13.3.0 → 13.5.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 (372) hide show
  1. package/README.md +12 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +115 -31
  4. package/bin/evinse.js +23 -1
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +115 -0
  7. package/bin/sign.js +45 -15
  8. package/bin/tracebom.js +5 -1
  9. package/bin/validate.js +84 -18
  10. package/bin/verify.js +155 -86
  11. package/data/cbomosdb-queries.json +13 -1
  12. package/data/component-tags.json +8 -0
  13. package/data/helpers/init.gradle +46 -21
  14. package/data/queries-darwin.json +25 -1
  15. package/data/queries-win.json +19 -1
  16. package/data/queries.json +41 -0
  17. package/data/remediations.json +344 -1
  18. package/data/rules/cbom-security.yaml +235 -4
  19. package/data/rules/dependency-sources.yaml +67 -0
  20. package/data/rules/go-binaries.yaml +46 -0
  21. package/data/rules/golem-go.yaml +80 -0
  22. package/data/rules/hbom-compliance.yaml +40 -22
  23. package/data/rules/hbom-performance.yaml +7 -1
  24. package/data/rules/host-topology.yaml +1 -1
  25. package/data/rules/obom-runtime.yaml +31 -2
  26. package/data/rules/php-runtime.yaml +143 -0
  27. package/data/rules/scala-evidence.yaml +115 -0
  28. package/lib/audit/index.js +52 -6
  29. package/lib/cli/bomAssembly.js +28 -3
  30. package/lib/cli/cliOptions.js +110 -3
  31. package/lib/cli/index.js +186 -58
  32. package/lib/cli/jsBom.js +50 -29
  33. package/lib/cli/jvmBom.js +779 -146
  34. package/lib/cli/managedBom.js +1021 -261
  35. package/lib/cli/nativeBom.js +364 -58
  36. package/lib/core/activity.js +12 -0
  37. package/lib/core/env.js +47 -17
  38. package/lib/core/fs.js +31 -21
  39. package/lib/core/httpClient.js +233 -69
  40. package/lib/core/pluginRun.js +184 -0
  41. package/lib/core/processTree.js +100 -0
  42. package/lib/core/projectConfig.js +1 -0
  43. package/lib/core/projectTooling.js +97 -0
  44. package/lib/core/propertySanitizer.js +72 -0
  45. package/lib/core/runState.js +39 -0
  46. package/lib/core/toml.js +72 -0
  47. package/lib/ecosystems/asarutils.js +4 -2
  48. package/lib/ecosystems/bunutils.js +5 -7
  49. package/lib/ecosystems/cargoCache.js +231 -8
  50. package/lib/ecosystems/cmakeResolver.js +10 -21
  51. package/lib/ecosystems/cppBuildContext.js +631 -0
  52. package/lib/ecosystems/cppEvidence.js +337 -22
  53. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  54. package/lib/ecosystems/denoutils.js +93 -66
  55. package/lib/ecosystems/ecosystems.js +1967 -788
  56. package/lib/ecosystems/golem.js +578 -18
  57. package/lib/ecosystems/gradleutils.js +340 -46
  58. package/lib/ecosystems/jsEvidence.js +85 -24
  59. package/lib/ecosystems/kosi.js +21 -4
  60. package/lib/ecosystems/npmutils.js +64 -0
  61. package/lib/ecosystems/parsers-dotnet.js +668 -153
  62. package/lib/ecosystems/parsers-elm.js +46 -28
  63. package/lib/ecosystems/parsers-gleam.js +3 -4
  64. package/lib/ecosystems/parsers-go.js +1283 -297
  65. package/lib/ecosystems/parsers-js.js +92 -26
  66. package/lib/ecosystems/parsers-julia.js +2 -3
  67. package/lib/ecosystems/parsers-jvm.js +463 -29
  68. package/lib/ecosystems/parsers-misc.js +405 -93
  69. package/lib/ecosystems/parsers-python.js +152 -20
  70. package/lib/ecosystems/parsers-rust.js +4 -4
  71. package/lib/ecosystems/parsers-terraform.js +1573 -19
  72. package/lib/ecosystems/pythonutils.js +102 -6
  73. package/lib/ecosystems/remote/huggingface.js +3 -0
  74. package/lib/ecosystems/rubyutils.js +73 -20
  75. package/lib/ecosystems/rusi.js +32 -9
  76. package/lib/ecosystems/sbtutils.js +243 -187
  77. package/lib/ecosystems/scalacliutils.js +343 -0
  78. package/lib/ecosystems/scalasem.js +2620 -0
  79. package/lib/ecosystems/terraformRegistry.js +288 -0
  80. package/lib/ecosystems/utils.js +2 -0
  81. package/lib/ecosystems/vendoredCode.js +156 -0
  82. package/lib/evinser/evinser.js +695 -78
  83. package/lib/evinser/swiftsem.js +68 -5
  84. package/lib/helpers/agenticToolutils.js +2 -2
  85. package/lib/helpers/bomSigner.js +576 -128
  86. package/lib/inventory/agentSkillSpec.js +17 -0
  87. package/lib/inventory/aiCollector.js +4 -1
  88. package/lib/inventory/analyzer.js +110 -127
  89. package/lib/inventory/apkInstalledDb.js +243 -0
  90. package/lib/inventory/atomUtils.js +204 -48
  91. package/lib/inventory/auditCategories.js +10 -3
  92. package/lib/inventory/buildToolRateLimit.js +144 -0
  93. package/lib/inventory/cacheCommands.js +2 -1
  94. package/lib/inventory/cacheDir.js +52 -9
  95. package/lib/inventory/certificates.js +2371 -0
  96. package/lib/inventory/cmakeBuildDirs.js +254 -0
  97. package/lib/inventory/communityAiConfigParser.js +4 -9
  98. package/lib/inventory/deps.js +507 -128
  99. package/lib/inventory/display.js +200 -3
  100. package/lib/inventory/dosai.js +747 -101
  101. package/lib/inventory/dosaiParsers.js +310 -15
  102. package/lib/inventory/dynamic.js +96 -8
  103. package/lib/inventory/envcontext.js +168 -5
  104. package/lib/inventory/evidenceUtils.js +590 -132
  105. package/lib/inventory/fetchBatch.js +574 -38
  106. package/lib/inventory/fetchDiskCache.js +597 -0
  107. package/lib/inventory/forgeEnricher.js +14 -5
  108. package/lib/inventory/formulationParsers.js +3 -2
  109. package/lib/inventory/goBuildInfo.js +140 -0
  110. package/lib/inventory/hbom.js +67 -7
  111. package/lib/inventory/hbomAnalysis.js +16 -0
  112. package/lib/inventory/hostTopology.js +21 -0
  113. package/lib/inventory/jvmLocalRepos.js +917 -0
  114. package/lib/inventory/mcpConfigParser.js +2 -3
  115. package/lib/inventory/osPackagePurl.js +244 -0
  116. package/lib/inventory/osPackageResolver.js +89 -87
  117. package/lib/inventory/osinfo.js +79 -31
  118. package/lib/inventory/osqueryTransform.js +114 -5
  119. package/lib/inventory/parentSelection.js +146 -0
  120. package/lib/inventory/phpBuildInfo.js +487 -0
  121. package/lib/inventory/phpHostProbe.js +641 -0
  122. package/lib/inventory/rootfsCerts.js +426 -0
  123. package/lib/inventory/rustFormulationParser.js +3 -4
  124. package/lib/inventory/scalaCoords.js +153 -0
  125. package/lib/inventory/source.js +215 -11
  126. package/lib/inventory/spdx.js +245 -0
  127. package/lib/inventory/traceRunner.js +93 -8
  128. package/lib/inventory/tracedCrypto.js +423 -0
  129. package/lib/managers/binary.js +529 -183
  130. package/lib/managers/docker.js +199 -44
  131. package/lib/managers/oci.js +24 -2
  132. package/lib/parsers/cmakeCache.js +69 -0
  133. package/lib/parsers/cmakeLists.js +16 -2
  134. package/lib/parsers/cmakePresets.js +400 -0
  135. package/lib/parsers/compileCommands.js +557 -0
  136. package/lib/parsers/email.js +277 -0
  137. package/lib/parsers/largeJson.js +34 -2
  138. package/lib/parsers/strictJson.js +322 -0
  139. package/lib/parsers/terraformConfig.js +1073 -0
  140. package/lib/parsers/terraformSource.js +652 -0
  141. package/lib/server/server.js +209 -1
  142. package/lib/stages/postgen/annotator.js +17 -1
  143. package/lib/stages/postgen/introspection/markers.js +1 -1
  144. package/lib/stages/postgen/introspection/reflect.js +4 -1
  145. package/lib/stages/postgen/postgen.js +20 -6
  146. package/lib/stages/postgen/ruleEngine.js +58 -1
  147. package/lib/stages/postgen/specVersionCompat.js +62 -0
  148. package/lib/stages/pregen/pregen.js +117 -5
  149. package/lib/validator/bomValidator.js +26 -5
  150. package/lib/validator/generated/runtime.mjs +13 -0
  151. package/lib/validator/generated/validate-1.6.mjs +26 -7
  152. package/lib/validator/generated/validate-1.7.mjs +31 -12
  153. package/lib/validator/generated/validate-2.0.mjs +30 -11
  154. package/lib/validator/index.js +25 -10
  155. package/lib/validator/reporters/json.js +1 -0
  156. package/package.json +23 -23
  157. package/types/bin/cdxgen-secure.d.ts +3 -0
  158. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  159. package/types/bin/repl.d.ts.map +1 -1
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +9 -2
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +43 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts.map +1 -1
  167. package/types/lib/cli/jvmBom.d.ts +44 -1
  168. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  169. package/types/lib/cli/managedBom.d.ts +7 -4
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts +3 -1
  172. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  173. package/types/lib/core/activity.d.ts.map +1 -1
  174. package/types/lib/core/env.d.ts +27 -2
  175. package/types/lib/core/env.d.ts.map +1 -1
  176. package/types/lib/core/fs.d.ts.map +1 -1
  177. package/types/lib/core/httpClient.d.ts +13 -8
  178. package/types/lib/core/httpClient.d.ts.map +1 -1
  179. package/types/lib/core/pluginRun.d.ts +97 -0
  180. package/types/lib/core/pluginRun.d.ts.map +1 -0
  181. package/types/lib/core/processTree.d.ts +36 -0
  182. package/types/lib/core/processTree.d.ts.map +1 -0
  183. package/types/lib/core/projectConfig.d.ts.map +1 -1
  184. package/types/lib/core/projectTooling.d.ts +41 -0
  185. package/types/lib/core/projectTooling.d.ts.map +1 -0
  186. package/types/lib/core/propertySanitizer.d.ts +26 -0
  187. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  188. package/types/lib/core/runState.d.ts +29 -0
  189. package/types/lib/core/runState.d.ts.map +1 -0
  190. package/types/lib/core/toml.d.ts +20 -0
  191. package/types/lib/core/toml.d.ts.map +1 -0
  192. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  193. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  194. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  195. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  197. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  199. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  200. package/types/lib/ecosystems/cppEvidence.d.ts +19 -3
  201. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  202. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  203. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  204. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  205. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  207. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  208. package/types/lib/ecosystems/golem.d.ts +81 -1
  209. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  210. package/types/lib/ecosystems/gradleutils.d.ts +59 -7
  211. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  212. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  213. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  214. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  215. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
  217. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  218. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  219. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  220. package/types/lib/ecosystems/parsers-go.d.ts +119 -5
  221. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  222. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  223. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  224. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  225. package/types/lib/ecosystems/parsers-jvm.d.ts +74 -3
  226. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  227. package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
  228. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  229. package/types/lib/ecosystems/parsers-python.d.ts +57 -6
  230. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  231. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  232. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  233. package/types/lib/ecosystems/pythonutils.d.ts +26 -1
  234. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  235. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  236. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  237. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  238. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  239. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  240. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  241. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  242. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  243. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  244. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  245. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  246. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  247. package/types/lib/ecosystems/utils.d.ts +2 -2
  248. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  249. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  250. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  251. package/types/lib/evinser/evinser.d.ts +100 -8
  252. package/types/lib/evinser/evinser.d.ts.map +1 -1
  253. package/types/lib/evinser/swiftsem.d.ts +10 -0
  254. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  255. package/types/lib/helpers/bomSigner.d.ts +80 -4
  256. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  257. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  258. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  259. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  260. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  261. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  262. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  263. package/types/lib/inventory/atomUtils.d.ts +96 -10
  264. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  265. package/types/lib/inventory/auditCategories.d.ts +3 -0
  266. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  267. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  268. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  269. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  270. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  271. package/types/lib/inventory/cacheDir.d.ts +12 -4
  272. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  273. package/types/lib/inventory/certificates.d.ts +205 -0
  274. package/types/lib/inventory/certificates.d.ts.map +1 -0
  275. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  276. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  277. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  278. package/types/lib/inventory/deps.d.ts +61 -2
  279. package/types/lib/inventory/deps.d.ts.map +1 -1
  280. package/types/lib/inventory/display.d.ts +18 -0
  281. package/types/lib/inventory/display.d.ts.map +1 -1
  282. package/types/lib/inventory/dosai.d.ts +202 -14
  283. package/types/lib/inventory/dosai.d.ts.map +1 -1
  284. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  285. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  286. package/types/lib/inventory/dynamic.d.ts +16 -1
  287. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  288. package/types/lib/inventory/envcontext.d.ts +40 -2
  289. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  290. package/types/lib/inventory/evidenceUtils.d.ts +48 -14
  291. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  292. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  293. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  294. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  295. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  296. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  297. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  298. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  299. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  300. package/types/lib/inventory/hbom.d.ts.map +1 -1
  301. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  302. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  303. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  304. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  305. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  306. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  307. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  308. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  309. package/types/lib/inventory/osPackageResolver.d.ts +6 -2
  310. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  311. package/types/lib/inventory/osinfo.d.ts +31 -6
  312. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  313. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  314. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  315. package/types/lib/inventory/parentSelection.d.ts +34 -0
  316. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  317. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  318. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  319. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  320. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  321. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  322. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  323. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  324. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  325. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  326. package/types/lib/inventory/source.d.ts +28 -0
  327. package/types/lib/inventory/source.d.ts.map +1 -1
  328. package/types/lib/inventory/spdx.d.ts +24 -0
  329. package/types/lib/inventory/spdx.d.ts.map +1 -1
  330. package/types/lib/inventory/traceRunner.d.ts +16 -1
  331. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  332. package/types/lib/inventory/tracedCrypto.d.ts +64 -0
  333. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  334. package/types/lib/managers/binary.d.ts +10 -0
  335. package/types/lib/managers/binary.d.ts.map +1 -1
  336. package/types/lib/managers/docker.d.ts +12 -0
  337. package/types/lib/managers/docker.d.ts.map +1 -1
  338. package/types/lib/managers/oci.d.ts +3 -1
  339. package/types/lib/managers/oci.d.ts.map +1 -1
  340. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  341. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  342. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  343. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  344. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  345. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  346. package/types/lib/parsers/compileCommands.d.ts +115 -0
  347. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  348. package/types/lib/parsers/email.d.ts +19 -0
  349. package/types/lib/parsers/email.d.ts.map +1 -0
  350. package/types/lib/parsers/largeJson.d.ts +13 -0
  351. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  352. package/types/lib/parsers/strictJson.d.ts +46 -0
  353. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  354. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  355. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  356. package/types/lib/parsers/terraformSource.d.ts +66 -0
  357. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  358. package/types/lib/server/server.d.ts +8 -0
  359. package/types/lib/server/server.d.ts.map +1 -1
  360. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  361. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  362. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  363. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  364. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  365. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  366. package/types/lib/stages/pregen/pregen.d.ts +14 -1
  367. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  368. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  369. package/types/lib/validator/index.d.ts +8 -0
  370. package/types/lib/validator/index.d.ts.map +1 -1
  371. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  372. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -4,6 +4,7 @@ import {
4
4
  sep as _sep,
5
5
  basename,
6
6
  dirname,
7
+ isAbsolute,
7
8
  join,
8
9
  relative,
9
10
  resolve,
@@ -13,14 +14,15 @@ import { build, Purl } from "@cdxgen/cdx-purl";
13
14
  import { parse as _load } from "yaml";
14
15
 
15
16
  import {
16
- cdxgenAgent,
17
17
  DEBUG_MODE,
18
+ hasDangerousUnicode,
18
19
  readEnvironmentVariable,
19
20
  } from "../core/activity.js";
20
21
  import { recordDegradation } from "../core/buildLedger.js";
21
22
  import { deferFailOnError } from "../core/deferredExit.js";
22
23
  import { shouldFetchLicense } from "../core/env.js";
23
24
  import {
25
+ getTmpDir,
24
26
  safeExistsSync,
25
27
  safeSpawnSync,
26
28
  safeWriteSync,
@@ -28,10 +30,16 @@ import {
28
30
  } from "../core/fs.js";
29
31
  import { thoughtLog } from "../core/logger.js";
30
32
  import { isWin } from "../core/paths.js";
33
+ import {
34
+ isProjectToolingAllowed,
35
+ reportProjectToolingSkipped,
36
+ } from "../core/projectTooling.js";
37
+ import { registerRunReset } from "../core/runState.js";
31
38
  import { mesonWrapDB } from "../core/state.js";
32
39
  import { isGitHubUrl } from "../core/urls.js";
33
40
  import { parseWorkflowFile } from "../inventory/ciParsers/githubActions.js";
34
41
  import {
42
+ gatedGet,
35
43
  prefetchEnabled,
36
44
  prefetchedResponse,
37
45
  prefetchJson,
@@ -45,6 +53,7 @@ import {
45
53
  } from "../inventory/purl.js";
46
54
  import { getLicenses, spdxLicenses } from "../inventory/spdx.js";
47
55
  import { cmakeFetchDependencies } from "../parsers/cmakeLists.js";
56
+ import { isValidIdnEmail } from "../parsers/email.js";
48
57
  import { submodulePurlCoordinates } from "../parsers/gitmodules.js";
49
58
  import { getDartMetadata } from "./ecosystems.js";
50
59
  import { parsePkgJson } from "./parsers-js.js";
@@ -59,6 +68,22 @@ import { parsePkgJson } from "./parsers-js.js";
59
68
  */
60
69
  const MAX_FLAKE_INPUTS_BLOCK_LENGTH = 16 * 1024;
61
70
 
71
+ /**
72
+ * Whether a package registry can hold a pubspec.lock entry.
73
+ *
74
+ * The entry's `source` names where the package comes from. `sdk` packages ship
75
+ * with the Dart or Flutter SDK, `path` packages live in the working tree, and
76
+ * `git` packages come from a repository. None is published on a registry, and
77
+ * asking about them leaks their names and imports a look-alike's metadata.
78
+ *
79
+ * @param {Object} packageData One pubspec.lock `packages` entry.
80
+ * @returns {boolean}
81
+ */
82
+ function canPubRegistryServe(packageData) {
83
+ const source = packageData?.source;
84
+ return source === undefined || source === "hosted";
85
+ }
86
+
62
87
  /**
63
88
  * Method to parse pubspec.lock files.
64
89
  *
@@ -71,10 +96,11 @@ export async function parsePubLockData(pubLockData, lockFile) {
71
96
  if (!pubLockData) {
72
97
  return [];
73
98
  }
74
- let pkgList = [];
99
+ const pkgList = [];
75
100
  const rootList = [];
76
101
  const data = _load(pubLockData);
77
102
  const packages = data.packages;
103
+ const lookupList = [];
78
104
  for (const [packageName, packageData] of Object.entries(packages)) {
79
105
  const pkg = {
80
106
  name: packageName,
@@ -131,9 +157,15 @@ export async function parsePubLockData(pubLockData, lockFile) {
131
157
  };
132
158
  }
133
159
  pkgList.push(pkg);
160
+ if (canPubRegistryServe(packageData)) {
161
+ lookupList.push(pkg);
162
+ }
134
163
  }
135
164
  if (shouldFetchLicense()) {
136
- pkgList = await getDartMetadata(pkgList);
165
+ // getDartMetadata enriches in place, so the sdk, path and git packages
166
+ // stay in pkgList with the lockfile's answers and are simply not handed
167
+ // to it.
168
+ await getDartMetadata(lookupList);
137
169
  }
138
170
  return { rootList, pkgList };
139
171
  }
@@ -1533,11 +1565,146 @@ export function parseComposerJson(composerJsonFile) {
1533
1565
  return { rootRequires, moduleParent };
1534
1566
  }
1535
1567
 
1568
+ // Composer's platform packages stand for what the runtime provides, and none
1569
+ // is installable, so none has a purl or an edge in the dependency graph. These
1570
+ // are the ones that describe PHP: PHP and its build flavours, HHVM, and the
1571
+ // extensions and system libraries PHP was built with. composer,
1572
+ // composer-plugin-api and composer-runtime-api are platform packages too, but
1573
+ // they describe Composer, so they are left out.
1574
+ // https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L39
1575
+ const COMPOSER_PLATFORM_NAMES = new Set([
1576
+ "php",
1577
+ "php-64bit",
1578
+ "php-ipv6",
1579
+ "php-zts",
1580
+ "php-debug",
1581
+ "hhvm",
1582
+ ]);
1583
+
1584
+ function isComposerPlatformRequirement(name) {
1585
+ return (
1586
+ COMPOSER_PLATFORM_NAMES.has(name) ||
1587
+ name.startsWith("ext-") ||
1588
+ name.startsWith("lib-")
1589
+ );
1590
+ }
1591
+
1592
+ // What the lock records about the platform is a constraint, or the version a
1593
+ // project told Composer to assume. It is a fact about the project's
1594
+ // requirements, never the version of anything that runs, so it is kept as a
1595
+ // property and never becomes a component.
1596
+ const COMPOSER_PLATFORM_SECTIONS = [
1597
+ ["platform", "cdx:composer:platform:"],
1598
+ ["platform-dev", "cdx:composer:platformDev:"],
1599
+ ["platform-overrides", "cdx:composer:platformOverride:"],
1600
+ ];
1601
+ const COMPOSER_REQUIRE_PREFIX = "cdx:composer:require:";
1602
+ const MAX_COMPOSER_PLATFORM_VALUE_LENGTH = 256;
1603
+ // A real lock names a few dozen platform packages per map at most; the cap
1604
+ // keeps a crafted one from turning each map into thousands of properties.
1605
+ const MAX_COMPOSER_PLATFORM_ENTRIES = 128;
1606
+
1607
+ function isComposerPlatformKey(key) {
1608
+ return /^[A-Za-z0-9._-]{1,100}$/.test(key);
1609
+ }
1610
+
1611
+ /**
1612
+ * The value a platform entry carries as a property: a constraint or version
1613
+ * string, or `false`, which `config.platform` uses to disable a package:
1614
+ * https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L125
1615
+ *
1616
+ * @param {unknown} value value from the lock
1617
+ * @returns {string|undefined} property value, or undefined when it is not one
1618
+ */
1619
+ function composerPlatformValue(value) {
1620
+ if (value === false) {
1621
+ return "false";
1622
+ }
1623
+ if (typeof value !== "string") {
1624
+ return undefined;
1625
+ }
1626
+ const trimmed = value.trim();
1627
+ if (
1628
+ !trimmed ||
1629
+ trimmed.length > MAX_COMPOSER_PLATFORM_VALUE_LENGTH ||
1630
+ hasDangerousUnicode(trimmed) ||
1631
+ /[\t\n\r]/.test(trimmed)
1632
+ ) {
1633
+ return undefined;
1634
+ }
1635
+ return trimmed;
1636
+ }
1637
+
1638
+ /**
1639
+ * Turn a map of platform entries into properties named `<prefix><name>`, in
1640
+ * name order, at most MAX_COMPOSER_PLATFORM_ENTRIES of them. Over the cap the
1641
+ * PHP and HHVM entries are kept first, since `ext-*` sorts before `php`. An
1642
+ * array (Composer writes an empty section as `[]`) or any other shape holds no
1643
+ * entries.
1644
+ *
1645
+ * @param {unknown} entries map of requirement name to constraint
1646
+ * @param {string} prefix property name prefix
1647
+ * @param {function(string): boolean} [keep] which names to keep
1648
+ * @returns {{name: string, value: string}[]} properties
1649
+ */
1650
+ function composerPlatformProperties(entries, prefix, keep = () => true) {
1651
+ if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
1652
+ return [];
1653
+ }
1654
+ const kept = [];
1655
+ for (const key of Object.keys(entries).sort()) {
1656
+ const value = composerPlatformValue(entries[key]);
1657
+ if (value !== undefined && isComposerPlatformKey(key) && keep(key)) {
1658
+ kept.push({ key, value });
1659
+ }
1660
+ }
1661
+ let selected = kept;
1662
+ if (kept.length > MAX_COMPOSER_PLATFORM_ENTRIES) {
1663
+ const runtimes = kept.filter(({ key }) => COMPOSER_PLATFORM_NAMES.has(key));
1664
+ const others = kept
1665
+ .filter(({ key }) => !COMPOSER_PLATFORM_NAMES.has(key))
1666
+ .slice(0, MAX_COMPOSER_PLATFORM_ENTRIES - runtimes.length);
1667
+ const chosen = new Set([...runtimes, ...others]);
1668
+ selected = kept.filter((entry) => chosen.has(entry));
1669
+ }
1670
+ return selected.map(({ key, value }) => ({ name: `${prefix}${key}`, value }));
1671
+ }
1672
+
1673
+ /**
1674
+ * A CycloneDX `organizationalContact` from an author record in package
1675
+ * metadata. The schema asserts `idn-email` on the address, so one that is not
1676
+ * a valid address is dropped rather than failing BOM validation; an author with
1677
+ * neither a name nor a valid address yields nothing.
1678
+ *
1679
+ * @param {{name?: string, email?: unknown}} author Author as the manifest has it
1680
+ * @returns {Array<{name?: string, email?: string}>} Zero or one contact
1681
+ */
1682
+ function contactFromAuthor(author) {
1683
+ const contact = {};
1684
+ if (typeof author?.name === "string" && author.name.trim()) {
1685
+ contact.name = author.name;
1686
+ }
1687
+ const email = typeof author?.email === "string" ? author.email.trim() : "";
1688
+ if (isValidIdnEmail(email)) {
1689
+ contact.email = email;
1690
+ }
1691
+ return Object.keys(contact).length ? [contact] : [];
1692
+ }
1693
+
1536
1694
  /**
1537
1695
  * Parse composer lock file
1538
1696
  *
1539
1697
  * @param {string} pkgLockFile composer.lock file
1540
1698
  * @param {array} rootRequires require section from composer.json
1699
+ * @returns {{pkgList: Object[], dependenciesList: Object[], rootList: Object[], platformProperties: {name: string, value: string}[]}|Array}
1700
+ * The locked packages, the dependency edges between them, the packages the
1701
+ * root requires, and the lock's `platform`, `platform-dev` and
1702
+ * `platform-overrides` entries as `cdx:composer:*` properties. The platform
1703
+ * entries are constraints and assumed versions, not the PHP that runs; the
1704
+ * caller puts them on the module component. Every locked package carries its
1705
+ * own `php`, `hhvm`, `ext-*` and `lib-*` requirements as
1706
+ * `cdx:composer:require:<name>` properties. A lock that is not valid JSON
1707
+ * gives an empty array.
1541
1708
  */
1542
1709
  export function parseComposerLock(pkgLockFile, rootRequires) {
1543
1710
  const pkgList = [];
@@ -1546,16 +1713,12 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
1546
1713
  const pkgNamePurlMap = {};
1547
1714
  const rootList = [];
1548
1715
  const rootRequiresMap = {};
1716
+ let platformProperties = [];
1549
1717
  if (rootRequires) {
1550
1718
  for (const rr of Object.keys(rootRequires)) {
1551
1719
  // Skip platform requirements (php, hhvm, ext-*, lib-*) — they are never
1552
1720
  // Composer package names and must not be used to identify root packages.
1553
- if (
1554
- rr === "php" ||
1555
- rr === "hhvm" ||
1556
- rr.startsWith("ext-") ||
1557
- rr.startsWith("lib-")
1558
- ) {
1721
+ if (isComposerPlatformRequirement(rr)) {
1559
1722
  continue;
1560
1723
  }
1561
1724
  rootRequiresMap[rr] = true;
@@ -1570,6 +1733,11 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
1570
1733
  return [];
1571
1734
  }
1572
1735
  if (lockData) {
1736
+ for (const [section, prefix] of COMPOSER_PLATFORM_SECTIONS) {
1737
+ platformProperties = platformProperties.concat(
1738
+ composerPlatformProperties(lockData[section], prefix),
1739
+ );
1740
+ }
1573
1741
  const packages = {};
1574
1742
  if (lockData["packages"]) {
1575
1743
  packages["required"] = lockData["packages"];
@@ -1642,9 +1810,10 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
1642
1810
  apkg.distribution = { url: distUrl };
1643
1811
  }
1644
1812
  if (pkg?.authors?.length) {
1645
- apkg.authors = pkg.authors.map((a) => {
1646
- return { name: a.name, email: a.email };
1647
- });
1813
+ const authors = pkg.authors.flatMap(contactFromAuthor);
1814
+ if (authors.length) {
1815
+ apkg.authors = authors;
1816
+ }
1648
1817
  }
1649
1818
  if (pkg?.keywords?.length) {
1650
1819
  apkg.tags = pkg.keywords;
@@ -1665,6 +1834,13 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
1665
1834
  });
1666
1835
  }
1667
1836
  }
1837
+ apkg.properties.push(
1838
+ ...composerPlatformProperties(
1839
+ pkg.require,
1840
+ COMPOSER_REQUIRE_PREFIX,
1841
+ isComposerPlatformRequirement,
1842
+ ),
1843
+ );
1668
1844
  pkgList.push(apkg);
1669
1845
  dependenciesMap[purl] = new Set();
1670
1846
  pkgNamePurlMap[pkg.name] = purl;
@@ -1704,6 +1880,7 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
1704
1880
  pkgList,
1705
1881
  dependenciesList,
1706
1882
  rootList,
1883
+ platformProperties,
1707
1884
  };
1708
1885
  }
1709
1886
 
@@ -3002,14 +3179,67 @@ export function parseMakeDFile(dfile) {
3002
3179
  return pkgFilesMap;
3003
3180
  }
3004
3181
 
3182
+ /**
3183
+ * Resolve a path a lockfile names against the lockfile's directory, refusing
3184
+ * absolute paths and any that leave the scanned directory. The value comes
3185
+ * from the scanned project, so it must not steer the scan at files outside
3186
+ * it. The bound is the scan root, not the lockfile's directory: a React
3187
+ * Native `ios/Podfile` names its pods as `../node_modules/...`.
3188
+ *
3189
+ * @param {String} projectPath Directory of the Podfile.lock
3190
+ * @param {String} value Path as the lockfile writes it
3191
+ * @param {String} [scanRoot] Directory the scan was asked for; the lockfile's
3192
+ * directory when not given
3193
+ * @returns {String|undefined} The resolved path, or undefined when refused
3194
+ */
3195
+ function resolveInsideScan(projectPath, value, scanRoot = projectPath) {
3196
+ if (typeof value !== "string" || !value.length) {
3197
+ return undefined;
3198
+ }
3199
+ if (
3200
+ isAbsolute(value) ||
3201
+ /^[a-zA-Z]:[\\/]/.test(value) ||
3202
+ value.startsWith("\\\\")
3203
+ ) {
3204
+ return undefined;
3205
+ }
3206
+ const resolved = resolve(projectPath, value);
3207
+ const back = relative(resolve(scanRoot), resolved);
3208
+ if (back.startsWith("..") || isAbsolute(back)) {
3209
+ return undefined;
3210
+ }
3211
+ return resolved;
3212
+ }
3213
+
3214
+ /**
3215
+ * Whether a pod name can be used as a file name. Pod names come from the
3216
+ * lockfile and are joined onto directories.
3217
+ *
3218
+ * @param {String} podName Root pod name
3219
+ * @returns {Boolean}
3220
+ */
3221
+ function isSafePodFileName(podName) {
3222
+ return (
3223
+ typeof podName === "string" &&
3224
+ podName.length > 0 &&
3225
+ !podName.includes("/") &&
3226
+ !podName.includes("\\") &&
3227
+ !podName.includes("\0") &&
3228
+ podName !== "." &&
3229
+ podName !== ".."
3230
+ );
3231
+ }
3232
+
3005
3233
  /**
3006
3234
  * Parse the contents of a 'Podfile.lock'
3007
3235
  *
3008
3236
  * @param {Object} podfileLock The content of the podfile.lock as an Object
3009
3237
  * @param {String} projectPath The path to the project root
3238
+ * @param {String} [scanRoot] Directory the scan was asked for. Local `:path`
3239
+ * and `:podspec` sources outside it are not read. Defaults to projectPath.
3010
3240
  * @returns {Map} Map of all dependencies with their direct dependencies
3011
3241
  */
3012
- export async function parsePodfileLock(podfileLock, projectPath) {
3242
+ export async function parsePodfileLock(podfileLock, projectPath, scanRoot) {
3013
3243
  const dependencies = new Map();
3014
3244
  for (const pod of podfileLock["PODS"]) {
3015
3245
  const dependency = {};
@@ -3031,58 +3261,103 @@ export async function parsePodfileLock(podfileLock, projectPath) {
3031
3261
  dependency.metadata.name.indexOf("/"),
3032
3262
  )
3033
3263
  : dependency.metadata.name;
3034
- if (podfileLock["EXTERNAL SOURCES"]?.[podName]) {
3264
+ if (
3265
+ podfileLock["EXTERNAL SOURCES"]?.[podName] &&
3266
+ isSafePodFileName(podName)
3267
+ ) {
3035
3268
  const externalPod = podfileLock["EXTERNAL SOURCES"][podName];
3036
3269
  if (externalPod[":git"]) {
3037
- let projectRepo = externalPod[":git"];
3038
- if (isGitHubUrl(projectRepo)) {
3039
- projectRepo = projectRepo.replace(
3040
- "github.com",
3041
- "raw.githubusercontent.com",
3042
- );
3043
- }
3044
- if (projectRepo.endsWith(".git")) {
3045
- projectRepo = projectRepo.substring(0, projectRepo.length - 4);
3270
+ // The podspec of a checked out pod, which `pod install` writes
3271
+ // alongside the sources it downloaded. Reading it needs no request,
3272
+ // so it answers before any raw repository URL.
3273
+ const localPodspec = join(
3274
+ projectPath,
3275
+ "Pods",
3276
+ "Local Podspecs",
3277
+ `${podName}.podspec.json`,
3278
+ );
3279
+ if (safeExistsSync(localPodspec)) {
3280
+ dependency.metadata.properties = [
3281
+ {
3282
+ name: "cdx:pods:podspecLocation",
3283
+ value: localPodspec,
3284
+ },
3285
+ ];
3286
+ } else {
3287
+ let projectRepo = externalPod[":git"];
3288
+ if (isGitHubUrl(projectRepo)) {
3289
+ projectRepo = projectRepo.replace(
3290
+ "github.com",
3291
+ "raw.githubusercontent.com",
3292
+ );
3293
+ }
3294
+ if (projectRepo.endsWith(".git")) {
3295
+ projectRepo = projectRepo.substring(0, projectRepo.length - 4);
3296
+ }
3297
+ // The commit the lockfile recorded in CHECKOUT OPTIONS names the
3298
+ // exact revision CocoaPods checked out, so its podspec is asked
3299
+ // for directly instead of probing the default branch names. It
3300
+ // wins over a branch, whose head may have moved since.
3301
+ const checkoutCommit =
3302
+ podfileLock["CHECKOUT OPTIONS"]?.[podName]?.[":commit"];
3303
+ const projectRepoBranchOrTag =
3304
+ typeof checkoutCommit === "string" &&
3305
+ /^[0-9a-f]{7,64}$/i.test(checkoutCommit)
3306
+ ? checkoutCommit
3307
+ : externalPod[":tag"]
3308
+ ? `refs/tags/${externalPod[":tag"]}`
3309
+ : externalPod[":branch"]
3310
+ ? `refs/heads/${externalPod[":branch"]}`
3311
+ : "refs/heads/<DEFAULT>";
3312
+ dependency.metadata.properties = [
3313
+ {
3314
+ name: "cdx:pods:podspecLocation",
3315
+ value: `${projectRepo}/${projectRepoBranchOrTag}/${podName}.podspec`,
3316
+ },
3317
+ ];
3046
3318
  }
3047
- const projectRepoBranchOrTag = externalPod[":tag"]
3048
- ? `tags/${externalPod[":tag"]}`
3049
- : `heads/${externalPod[":branch"] ? externalPod[":branch"] : "<DEFAULT>"}`;
3050
- dependency.metadata.properties = [
3051
- {
3052
- name: "cdx:pods:podspecLocation",
3053
- value: `${projectRepo}/refs/${projectRepoBranchOrTag}/${podName}.podspec`,
3054
- },
3055
- ];
3056
3319
  } else if (externalPod[":path"]) {
3057
- const projectLocation = resolve(projectPath, externalPod[":path"]);
3058
- dependency.metadata.properties = [
3059
- {
3060
- name: "cdx:pods:projectDir",
3061
- value: projectLocation,
3062
- },
3063
- ];
3064
- let podspec = join(projectLocation, `${podName}.podspec`);
3065
- if (!safeExistsSync(podspec)) {
3066
- podspec = `${podspec}.json`;
3067
- }
3068
- if (safeExistsSync(podspec)) {
3069
- dependency.metadata.properties.push({
3070
- name: "cdx:pods:podspecLocation",
3071
- value: podspec,
3072
- });
3320
+ const projectLocation = resolveInsideScan(
3321
+ projectPath,
3322
+ externalPod[":path"],
3323
+ scanRoot,
3324
+ );
3325
+ if (projectLocation) {
3326
+ dependency.metadata.properties = [
3327
+ {
3328
+ name: "cdx:pods:projectDir",
3329
+ value: projectLocation,
3330
+ },
3331
+ ];
3332
+ let podspec = join(projectLocation, `${podName}.podspec`);
3333
+ if (!safeExistsSync(podspec)) {
3334
+ podspec = `${podspec}.json`;
3335
+ }
3336
+ if (safeExistsSync(podspec)) {
3337
+ dependency.metadata.properties.push({
3338
+ name: "cdx:pods:podspecLocation",
3339
+ value: podspec,
3340
+ });
3341
+ }
3073
3342
  }
3074
3343
  } else if (externalPod[":podspec"]) {
3075
- const podspecLocation = resolve(projectPath, externalPod[":podspec"]);
3076
- dependency.metadata.properties = [
3077
- {
3078
- name: "cdx:pods:projectDir",
3079
- value: dirname(podspecLocation),
3080
- },
3081
- {
3082
- name: "cdx:pods:podspecLocation",
3083
- value: podspecLocation,
3084
- },
3085
- ];
3344
+ const podspecLocation = resolveInsideScan(
3345
+ projectPath,
3346
+ externalPod[":podspec"],
3347
+ scanRoot,
3348
+ );
3349
+ if (podspecLocation) {
3350
+ dependency.metadata.properties = [
3351
+ {
3352
+ name: "cdx:pods:projectDir",
3353
+ value: dirname(podspecLocation),
3354
+ },
3355
+ {
3356
+ name: "cdx:pods:podspecLocation",
3357
+ value: podspecLocation,
3358
+ },
3359
+ ];
3360
+ }
3086
3361
  }
3087
3362
  }
3088
3363
  dependencies.set(dependency.metadata.name, dependency);
@@ -3408,6 +3683,8 @@ export function resetCocoaPodspecPrefetch() {
3408
3683
  cocoaPodspecPrefetch = new Map();
3409
3684
  }
3410
3685
 
3686
+ registerRunReset("cocoapods:podspecs", resetCocoaPodspecPrefetch);
3687
+
3411
3688
  /**
3412
3689
  * Prefetch the remote podspecs a full CocoaPods scan is about to read.
3413
3690
  *
@@ -3486,6 +3763,12 @@ async function fullScanCocoaPod(dependency, component, options) {
3486
3763
  value: podspecLocation,
3487
3764
  });
3488
3765
  }
3766
+ // A Ruby podspec is a program that only `pod` can read, and it comes from
3767
+ // the scanned project or a repository its lockfile names. Without
3768
+ // permission to run the project's tooling, only JSON podspecs are read,
3769
+ // directly and without `pod`.
3770
+ const toolingAllowed = isProjectToolingAllowed(options);
3771
+ const isRemote = podspecLocation.startsWith("http");
3489
3772
  let replacements = [];
3490
3773
  if (
3491
3774
  podspecLocation.endsWith(".podspec") &&
@@ -3502,29 +3785,50 @@ async function fullScanCocoaPod(dependency, component, options) {
3502
3785
  "COCOA_PODSPEC_JSON_REPLACEMENTS",
3503
3786
  ).split(";");
3504
3787
  }
3505
- if (replacements || podspecLocation.startsWith("http")) {
3506
- let podspecContent;
3507
- if (podspecLocation.startsWith("http")) {
3508
- let httpResult;
3509
- for (const candidate of podspecCandidates(podspecLocation)) {
3510
- try {
3511
- httpResult =
3512
- prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
3513
- (await cdxgenAgent.get(candidate));
3514
- podspecLocation = candidate;
3515
- } catch (_err) {
3516
- continue;
3517
- }
3518
- component.properties.find(
3519
- ({ name }) => name === "cdx:pods:podspecLocation",
3520
- ).value = podspecLocation;
3521
- podspecLocation = `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`;
3522
- podspecContent = httpResult.body;
3523
- break;
3788
+ let podspecContent;
3789
+ if (isRemote) {
3790
+ let httpResult;
3791
+ for (const candidate of podspecCandidates(podspecLocation)) {
3792
+ if (!toolingAllowed && !candidate.endsWith(".json")) {
3793
+ continue;
3524
3794
  }
3525
- } else {
3526
- podspecContent = readFileSync(podspecLocation, "utf-8");
3795
+ try {
3796
+ httpResult =
3797
+ prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
3798
+ (await gatedGet(candidate, { responseType: "text" }));
3799
+ podspecLocation = candidate;
3800
+ } catch (_err) {
3801
+ continue;
3802
+ }
3803
+ component.properties.find(
3804
+ ({ name }) => name === "cdx:pods:podspecLocation",
3805
+ ).value = podspecLocation;
3806
+ podspecContent = httpResult.body;
3807
+ break;
3527
3808
  }
3809
+ // A location none of the candidates served contributes nothing: the
3810
+ // component keeps what the lockfile already said about the pod.
3811
+ if (podspecContent === undefined) {
3812
+ return;
3813
+ }
3814
+ } else if (!toolingAllowed && !podspecLocation.endsWith(".json")) {
3815
+ reportProjectToolingSkipped(
3816
+ options,
3817
+ "cocoa.podspec.tooling-disabled",
3818
+ "Skipping 'pod ipc spec' for Ruby podspecs since dependency installation is disabled. Their pods keep the details from Podfile.lock.",
3819
+ {
3820
+ ecosystem: "cocoa",
3821
+ tool: "pod",
3822
+ impact: "licenses",
3823
+ detail:
3824
+ "Dependency installation is disabled, so Ruby podspecs were not evaluated and their pods lack license and author details.",
3825
+ },
3826
+ );
3827
+ return;
3828
+ } else if (replacements.length || !toolingAllowed) {
3829
+ podspecContent = readFileSync(podspecLocation, "utf-8");
3830
+ }
3831
+ if (podspecContent !== undefined) {
3528
3832
  for (const replacement of replacements) {
3529
3833
  const replacementPair = replacement.split("=");
3530
3834
  let match = replacementPair[0].replaceAll("<NEWLINE>", "\n");
@@ -3534,18 +3838,28 @@ async function fullScanCocoaPod(dependency, component, options) {
3534
3838
  const repl = replacementPair[1].replaceAll("<NEWLINE>", "\n");
3535
3839
  podspecContent = podspecContent.replaceAll(match, repl);
3536
3840
  }
3537
- podspecLocation = join(
3538
- dirname(podspecLocation),
3539
- `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
3841
+ }
3842
+ if (!toolingAllowed) {
3843
+ // Only JSON podspecs reach this point, and they need no `pod`
3844
+ result = { stdout: podspecContent };
3845
+ } else {
3846
+ if (podspecContent !== undefined) {
3847
+ // A downloaded podspec goes to the temporary directory; a local one
3848
+ // with replacements stays beside the original, whose relative paths
3849
+ // its Ruby may read.
3850
+ podspecLocation = join(
3851
+ isRemote ? getTmpDir() : dirname(podspecLocation),
3852
+ `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
3853
+ );
3854
+ safeWriteSync(podspecLocation, podspecContent);
3855
+ temporaryFiles.add(podspecLocation);
3856
+ }
3857
+ result = executePodCommand(
3858
+ ["ipc", "spec", "--silent", podspecLocation],
3859
+ undefined,
3860
+ options,
3540
3861
  );
3541
- safeWriteSync(podspecLocation, podspecContent);
3542
- temporaryFiles.add(podspecLocation);
3543
3862
  }
3544
- result = executePodCommand(
3545
- ["ipc", "spec", "--silent", podspecLocation],
3546
- undefined,
3547
- options,
3548
- );
3549
3863
  } else {
3550
3864
  let dependencyName = dependency.name;
3551
3865
  if (dependencyName.includes("/")) {
@@ -3610,9 +3924,7 @@ async function fullScanCocoaPod(dependency, component, options) {
3610
3924
  component.authors = [];
3611
3925
  if (podspec.authors.constructor === Object) {
3612
3926
  Object.entries(podspec.authors).forEach(([name, email]) => {
3613
- email.includes("@")
3614
- ? component.authors.push({ name, email })
3615
- : component.authors.push({ name });
3927
+ component.authors.push(...contactFromAuthor({ name, email }));
3616
3928
  });
3617
3929
  } else if (podspec.authors.constructor === Array) {
3618
3930
  podspec.authors.forEach((name) => {