@cdxgen/cdxgen 13.5.0 → 13.7.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 (380) hide show
  1. package/README.md +17 -10
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +260 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +28 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +24 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/license-aliases.json +24 -2
  15. package/data/pqc-migration.json +239 -0
  16. package/data/remediations.json +176 -0
  17. package/data/rules/ai-bom.yaml +1 -1
  18. package/data/rules/brotli-decoder.yaml +107 -0
  19. package/data/rules/cbom-security.yaml +4 -3
  20. package/data/rules/package-integrity.yaml +28 -0
  21. package/data/rules/vscode-extensions.yaml +75 -0
  22. package/data/spdx-license-list.json +121 -7
  23. package/data/spdx-licenses.json +69 -0
  24. package/data/spdx.schema.json +845 -2
  25. package/lib/audit/index.js +25 -3
  26. package/lib/cli/bomAssembly.js +172 -4
  27. package/lib/cli/cliOptions.js +121 -17
  28. package/lib/cli/index.js +720 -902
  29. package/lib/cli/jsBom.js +156 -64
  30. package/lib/cli/jvmBom.js +602 -86
  31. package/lib/cli/managedBom.js +161 -19
  32. package/lib/cli/nativeBom.js +52 -5
  33. package/lib/core/boundedFile.js +344 -0
  34. package/lib/core/brotliProbe.js +182 -0
  35. package/lib/core/env.js +34 -0
  36. package/lib/core/fs.js +138 -3
  37. package/lib/core/globPatterns.js +218 -0
  38. package/lib/core/httpClient.js +98 -1
  39. package/lib/core/lockPaths.js +38 -0
  40. package/lib/core/paths.js +24 -1
  41. package/lib/core/processTree.js +100 -8
  42. package/lib/core/projectConfig.js +332 -25
  43. package/lib/core/toolRegistry.js +184 -0
  44. package/lib/ecosystems/cppEvidence.js +263 -106
  45. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  46. package/lib/ecosystems/denoutils.js +2 -2
  47. package/lib/ecosystems/ecosystems.js +25 -10
  48. package/lib/ecosystems/gemHostProbe.js +301 -0
  49. package/lib/ecosystems/golem.js +3 -8
  50. package/lib/ecosystems/gradleutils.js +1909 -229
  51. package/lib/ecosystems/jsEvidence.js +90 -38
  52. package/lib/ecosystems/kosi.js +3 -5
  53. package/lib/ecosystems/npmVersionResolution.js +376 -0
  54. package/lib/ecosystems/parsers-bazel.js +107 -6
  55. package/lib/ecosystems/parsers-js.js +505 -22
  56. package/lib/ecosystems/parsers-misc.js +209 -20
  57. package/lib/ecosystems/parsers-python.js +178 -30
  58. package/lib/ecosystems/parsers-rust.js +117 -15
  59. package/lib/ecosystems/pythonutils.js +128 -2
  60. package/lib/ecosystems/remote/huggingface.js +22 -0
  61. package/lib/ecosystems/rubyutils.js +538 -31
  62. package/lib/ecosystems/rusi.js +2 -4
  63. package/lib/ecosystems/scalacliutils.js +2 -0
  64. package/lib/ecosystems/scalasem.js +798 -152
  65. package/lib/ecosystems/vendoredCode.js +237 -3
  66. package/lib/ecosystems/venvProbe.js +360 -0
  67. package/lib/evinser/evinser.js +536 -71
  68. package/lib/evinser/scalasem.js +5 -1
  69. package/lib/helpers/agenticToolutils.js +525 -94
  70. package/lib/helpers/bomSigner.js +43 -9
  71. package/lib/helpers/jssSigner.js +1276 -0
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentBlueprint.js +193 -0
  74. package/lib/inventory/agentToolNames.js +145 -0
  75. package/lib/inventory/aiCollector.js +153 -7
  76. package/lib/inventory/aiInventory.js +44 -8
  77. package/lib/inventory/aiProviders.js +324 -0
  78. package/lib/inventory/atomUtils.js +77 -0
  79. package/lib/inventory/bomUtils.js +8 -1
  80. package/lib/inventory/brotliDecoder.js +142 -0
  81. package/lib/inventory/buildToolRateLimit.js +1 -1
  82. package/lib/inventory/cbomutils.js +56 -7
  83. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  84. package/lib/inventory/citations.js +31 -2
  85. package/lib/inventory/communityAiConfigParser.js +978 -80
  86. package/lib/inventory/cyclonedx20.js +152 -0
  87. package/lib/inventory/cyclonedx20Common.js +286 -0
  88. package/lib/inventory/cyclonedx20Derived.js +159 -0
  89. package/lib/inventory/cyclonedx20Parties.js +552 -0
  90. package/lib/inventory/cyclonedx20Read.js +768 -0
  91. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  92. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  93. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  94. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  95. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  96. package/lib/inventory/cyclonedx20Render.js +766 -0
  97. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  98. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  99. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  100. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  101. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  102. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  103. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  104. package/lib/inventory/cyclonedx20Tables.js +594 -0
  105. package/lib/inventory/deps.js +141 -13
  106. package/lib/inventory/dosai.js +375 -6
  107. package/lib/inventory/dosaiParsers.js +3 -6
  108. package/lib/inventory/dotnetRuntime.js +367 -0
  109. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  110. package/lib/inventory/envcontext.js +436 -48
  111. package/lib/inventory/evidenceUtils.js +13 -1
  112. package/lib/inventory/fetchBatch.js +1 -1
  113. package/lib/inventory/formulationParsers.js +124 -14
  114. package/lib/inventory/gtfobins.js +13 -0
  115. package/lib/inventory/hbom.js +45 -6
  116. package/lib/inventory/hbomLoader.js +166 -0
  117. package/lib/inventory/hostRuntimeProbe.js +686 -0
  118. package/lib/inventory/jvmLocalRepos.js +9 -2
  119. package/lib/inventory/jvmToolEnv.js +29 -0
  120. package/lib/inventory/licenseEnhancer.js +12 -0
  121. package/lib/inventory/mavenCentralMirror.js +534 -0
  122. package/lib/inventory/mcpConfigParser.js +5 -0
  123. package/lib/inventory/nodeBuildInfo.js +218 -0
  124. package/lib/inventory/nodeHostProbe.js +202 -0
  125. package/lib/inventory/occurrenceKey.js +90 -0
  126. package/lib/inventory/osPackageResolver.js +182 -0
  127. package/lib/inventory/phpBuildInfo.js +10 -59
  128. package/lib/inventory/phpHostProbe.js +32 -488
  129. package/lib/inventory/pqcMigration.js +426 -0
  130. package/lib/inventory/rubyBuildInfo.js +972 -0
  131. package/lib/inventory/rubyHostProbe.js +218 -0
  132. package/lib/inventory/rustFormulationParser.js +21 -10
  133. package/lib/inventory/scalaCoords.js +4 -3
  134. package/lib/inventory/scalaDedupe.js +440 -0
  135. package/lib/inventory/source.js +114 -8
  136. package/lib/managers/binary.js +337 -3
  137. package/lib/managers/docker.js +79 -0
  138. package/lib/parsers/huggingfaceManifest.js +9 -1
  139. package/lib/parsers/huggingfaceUtils.js +18 -0
  140. package/lib/parsers/kiroPermissions.js +166 -0
  141. package/lib/parsers/largeJson.js +28 -11
  142. package/lib/parsers/vscodeExtensionId.js +183 -0
  143. package/lib/server/server.js +48 -35
  144. package/lib/stages/postgen/postgen.js +41 -1
  145. package/lib/stages/postgen/ruleEngine.js +13 -5
  146. package/lib/stages/postgen/specVersionCompat.js +76 -0
  147. package/lib/stages/pregen/envAudit.js +1 -0
  148. package/lib/stages/pregen/pregen.js +114 -57
  149. package/lib/validator/bomValidator.js +14 -2
  150. package/lib/validator/complianceEngine.js +30 -3
  151. package/lib/validator/complianceRules.js +1416 -4
  152. package/lib/validator/generated/validate-1.6.mjs +1 -1
  153. package/lib/validator/generated/validate-1.7.mjs +1 -1
  154. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  155. package/lib/validator/index.js +49 -8
  156. package/lib/validator/reporters/json.js +6 -0
  157. package/package.json +11 -10
  158. package/types/bin/repl.d.ts.map +1 -1
  159. package/types/lib/audit/index.d.ts +6 -2
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +29 -1
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +6 -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 +3 -18
  167. package/types/lib/cli/jsBom.d.ts.map +1 -1
  168. package/types/lib/cli/jvmBom.d.ts +15 -0
  169. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  172. package/types/lib/core/boundedFile.d.ts +100 -0
  173. package/types/lib/core/boundedFile.d.ts.map +1 -0
  174. package/types/lib/core/brotliProbe.d.ts +37 -0
  175. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  176. package/types/lib/core/env.d.ts +14 -0
  177. package/types/lib/core/env.d.ts.map +1 -1
  178. package/types/lib/core/fs.d.ts +4 -0
  179. package/types/lib/core/fs.d.ts.map +1 -1
  180. package/types/lib/core/globPatterns.d.ts +70 -0
  181. package/types/lib/core/globPatterns.d.ts.map +1 -0
  182. package/types/lib/core/httpClient.d.ts +24 -0
  183. package/types/lib/core/httpClient.d.ts.map +1 -1
  184. package/types/lib/core/lockPaths.d.ts +19 -0
  185. package/types/lib/core/lockPaths.d.ts.map +1 -0
  186. package/types/lib/core/paths.d.ts +9 -0
  187. package/types/lib/core/paths.d.ts.map +1 -1
  188. package/types/lib/core/processTree.d.ts +17 -4
  189. package/types/lib/core/processTree.d.ts.map +1 -1
  190. package/types/lib/core/projectConfig.d.ts +47 -8
  191. package/types/lib/core/projectConfig.d.ts.map +1 -1
  192. package/types/lib/core/toolRegistry.d.ts +36 -0
  193. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  194. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  195. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  197. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  199. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  200. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  201. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  202. package/types/lib/ecosystems/gradleutils.d.ts +294 -20
  203. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  205. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  206. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  207. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  208. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  209. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  210. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  211. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  212. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  213. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  214. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  215. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  217. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  218. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  219. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  220. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  221. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  222. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  223. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  224. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  225. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  226. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  227. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  228. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  229. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  230. package/types/lib/evinser/evinser.d.ts +120 -13
  231. package/types/lib/evinser/evinser.d.ts.map +1 -1
  232. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  233. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  234. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  235. package/types/lib/helpers/bomSigner.d.ts +20 -3
  236. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  237. package/types/lib/helpers/jssSigner.d.ts +288 -0
  238. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  239. package/types/lib/helpers/vsixutils.d.ts +303 -19
  240. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  241. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  242. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  243. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  244. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  245. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  246. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  247. package/types/lib/inventory/aiProviders.d.ts +85 -0
  248. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  249. package/types/lib/inventory/atomUtils.d.ts +19 -0
  250. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  251. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  252. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  253. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  254. package/types/lib/inventory/cbomutils.d.ts +17 -0
  255. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  256. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  257. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  258. package/types/lib/inventory/citations.d.ts +5 -0
  259. package/types/lib/inventory/citations.d.ts.map +1 -1
  260. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  262. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  263. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  264. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  265. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  266. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  267. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  268. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  269. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  270. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  271. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  272. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  273. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  274. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  275. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  276. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  277. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  278. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  279. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  280. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  281. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  282. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  283. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  284. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  285. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  286. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  287. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  288. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  289. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  290. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  291. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  292. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  293. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  294. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  295. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  296. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  297. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  298. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  299. package/types/lib/inventory/deps.d.ts +35 -2
  300. package/types/lib/inventory/deps.d.ts.map +1 -1
  301. package/types/lib/inventory/dosai.d.ts +36 -1
  302. package/types/lib/inventory/dosai.d.ts.map +1 -1
  303. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  304. package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
  305. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  306. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  307. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  308. package/types/lib/inventory/envcontext.d.ts +74 -12
  309. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  310. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  311. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  312. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  313. package/types/lib/inventory/hbom.d.ts +11 -0
  314. package/types/lib/inventory/hbom.d.ts.map +1 -1
  315. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  316. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  317. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  318. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  319. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  320. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  321. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  322. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  323. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  324. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  325. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  326. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  327. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  328. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  329. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  330. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  331. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  332. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  333. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  334. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  335. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  336. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  337. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  338. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  339. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  340. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  341. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  342. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  343. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  344. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  345. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  346. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  347. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  348. package/types/lib/inventory/source.d.ts +25 -0
  349. package/types/lib/inventory/source.d.ts.map +1 -1
  350. package/types/lib/managers/binary.d.ts.map +1 -1
  351. package/types/lib/managers/docker.d.ts +12 -0
  352. package/types/lib/managers/docker.d.ts.map +1 -1
  353. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  354. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  355. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  356. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  357. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  358. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  359. package/types/lib/parsers/largeJson.d.ts +4 -3
  360. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  361. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  362. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  363. package/types/lib/server/server.d.ts.map +1 -1
  364. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  365. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  366. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  367. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  368. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  369. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  370. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  371. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  372. package/types/lib/validator/bomValidator.d.ts +5 -1
  373. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  374. package/types/lib/validator/complianceEngine.d.ts +4 -4
  375. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  376. package/types/lib/validator/complianceRules.d.ts +14 -2
  377. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  378. package/types/lib/validator/index.d.ts +10 -0
  379. package/types/lib/validator/index.d.ts.map +1 -1
  380. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -0,0 +1,979 @@
1
+ import { basename, dirname, isAbsolute, relative, resolve } from "node:path";
2
+
3
+ import { readRegularTextFile } from "../core/boundedFile.js";
4
+
5
+ // Build-input formulation parsers for the ecosystems whose build files cdxgen
6
+ // can read without running a build: make and CMake for C/C++, go.mod for Go,
7
+ // pip requirements and constraints files for Python, and project.assets.json
8
+ // plus .csproj for .NET. Rust already has its own parser
9
+ // (rustFormulationParser); Go binaries are read by the OBOM path through the
10
+ // bounded buildinfo reader, so a source scan records the module file here.
11
+ //
12
+ // Every parser takes `parse(files, options, context)`, where context.rootDir
13
+ // is the scanned project root. Files are read only when they are regular
14
+ // files under that root, and components record their path relative to it.
15
+
16
+ // A build file larger than this is machine generated (or not a build file at
17
+ // all), and reading it would only burn memory, so it is skipped unread.
18
+ export const MAX_READ_BYTES = 2 * 1024 * 1024;
19
+
20
+ // `dotnet restore` output grows with the dependency graph, and tens of
21
+ // megabytes are common in .NET monorepos, so it gets its own, larger bound.
22
+ export const MAX_ASSETS_READ_BYTES = 32 * 1024 * 1024;
23
+
24
+ // No build-file line worth reading is longer than this. Longer lines are
25
+ // skipped before any tokenising, so a crafted file cannot make a parser
26
+ // spend more than linear time on one line.
27
+ const MAX_LINE_LENGTH = 4096;
28
+
29
+ /**
30
+ * The path of a build input relative to the scanned project root, with
31
+ * forward slashes, as recorded in bom-refs and `internal:SrcFile`. A file
32
+ * outside the root, or a call without a root, records only its file name,
33
+ * so no path of the scanning machine reaches the BOM.
34
+ *
35
+ * @param {string} filePath file the component describes
36
+ * @param {string} [rootDir] scanned project root
37
+ * @returns {string} project-relative path, or the file name
38
+ */
39
+ export function formulationSourcePath(filePath, rootDir) {
40
+ if (!filePath) {
41
+ return filePath;
42
+ }
43
+ if (rootDir) {
44
+ const rel = relative(resolve(rootDir), resolve(filePath));
45
+ if (rel && !isAbsolute(rel) && rel.split(/[\\/]/)[0] !== "..") {
46
+ return rel.replaceAll("\\", "/");
47
+ }
48
+ }
49
+ return basename(filePath);
50
+ }
51
+
52
+ /**
53
+ * Read a build file when it is a regular file within the size bound. A
54
+ * symlink, a FIFO, a file that resolves outside rootDir and an oversize
55
+ * file are not read.
56
+ *
57
+ * @param {string} filePath file to read
58
+ * @param {string} [rootDir] root the file must stay under
59
+ * @param {number} [maxBytes] largest size read, in bytes
60
+ * @returns {string|undefined} file text, or undefined
61
+ */
62
+ function readBounded(filePath, rootDir, maxBytes = MAX_READ_BYTES) {
63
+ return readRegularTextFile(filePath, maxBytes, rootDir);
64
+ }
65
+
66
+ /**
67
+ * Split text into lines, dropping lines longer than MAX_LINE_LENGTH.
68
+ *
69
+ * @param {string} content file text
70
+ * @returns {string[]} lines of reasonable length
71
+ */
72
+ function boundedLines(content) {
73
+ return content
74
+ .split("\n")
75
+ .map((line) => (line.endsWith("\r") ? line.slice(0, -1) : line))
76
+ .filter((line) => line.length <= MAX_LINE_LENGTH);
77
+ }
78
+
79
+ /**
80
+ * Join lines that end in a backslash with the line after them, the way make
81
+ * and pip read their files. A line longer than MAX_LINE_LENGTH contributes no
82
+ * text but still continues when it ends in a backslash, so its neighbours
83
+ * stay where the tool would put them.
84
+ *
85
+ * @param {string} content file text
86
+ * @param {object} rules joining rules
87
+ * @param {boolean} rules.commentLinesContinue true for make, where a comment
88
+ * ending in a backslash continues; false for pip, where a comment line
89
+ * never continues and ends a continuation in progress
90
+ * @returns {string[]} logical lines
91
+ */
92
+ function joinContinuationLines(content, { commentLinesContinue }) {
93
+ const logicalLines = [];
94
+ let pending;
95
+ for (const rawLine of content.split("\n")) {
96
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
97
+ const overlong = line.length > MAX_LINE_LENGTH;
98
+ const text = overlong ? "" : line;
99
+ const isComment = !commentLinesContinue && text.trimStart().startsWith("#");
100
+ const continues = line.trimEnd().endsWith("\\") && !isComment;
101
+ let piece = text;
102
+ if (continues && !overlong) {
103
+ piece = text.trimEnd().slice(0, -1);
104
+ }
105
+ const joined =
106
+ pending === undefined ? piece : `${pending} ${piece.trimStart()}`;
107
+ if (continues) {
108
+ pending = joined;
109
+ continue;
110
+ }
111
+ logicalLines.push(joined);
112
+ pending = undefined;
113
+ }
114
+ if (pending !== undefined) {
115
+ logicalLines.push(pending);
116
+ }
117
+ return logicalLines;
118
+ }
119
+
120
+ /**
121
+ * Split a line on runs of whitespace.
122
+ *
123
+ * @param {string} line line text
124
+ * @returns {string[]} non-empty tokens
125
+ */
126
+ function tokens(line) {
127
+ return line.split(/\s+/).filter(Boolean);
128
+ }
129
+
130
+ function appendProperty(properties, name, value) {
131
+ if (!name || value === undefined || value === null || value === "") {
132
+ return;
133
+ }
134
+ properties.push({
135
+ name,
136
+ value: typeof value === "string" ? value : String(value),
137
+ });
138
+ }
139
+
140
+ function createBuildInputComponent(name, filePath, tool, rootDir) {
141
+ const sourcePath = formulationSourcePath(filePath, rootDir);
142
+ return {
143
+ type: "application",
144
+ name,
145
+ version: "config",
146
+ "bom-ref": `urn:cdxgen:formulation:${tool}:${sourcePath}`,
147
+ properties: [
148
+ { name: "internal:SrcFile", value: sourcePath },
149
+ { name: "cdx:build:tool", value: tool },
150
+ ],
151
+ };
152
+ }
153
+
154
+ const MAX_LISTED = 25;
155
+
156
+ const MAKE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
157
+ const MAKE_INCLUDE_DIRECTIVES = new Set(["include", "-include", "sinclude"]);
158
+ const MAKE_VARIABLE_MODIFIERS = new Set(["export", "override", "private"]);
159
+
160
+ /**
161
+ * Whether a path from a make file is safe to record: relative to the make
162
+ * file and not leaving the repository. Absolute POSIX, Windows drive and UNC
163
+ * paths, home-relative paths and paths with a `..` segment describe the
164
+ * machine or tree around the project, so they are dropped.
165
+ *
166
+ * @param {string} includePath include directive argument or target name
167
+ * @returns {boolean} true when the path can be recorded
168
+ */
169
+ function isRecordableIncludePath(includePath) {
170
+ if (
171
+ !includePath ||
172
+ includePath.startsWith("/") ||
173
+ includePath.startsWith("\\") ||
174
+ includePath.startsWith("~")
175
+ ) {
176
+ return false;
177
+ }
178
+ if (
179
+ includePath.length >= 2 &&
180
+ includePath[1] === ":" &&
181
+ /^[A-Za-z]$/.test(includePath[0])
182
+ ) {
183
+ return false;
184
+ }
185
+ return !includePath.split(/[\\/]/).includes("..");
186
+ }
187
+
188
+ /**
189
+ * Whether the text before a rule colon is a target name worth recording: one
190
+ * token with no whitespace or quotes, not an option-like word, not a special
191
+ * (`.PHONY`), variable-reference or pattern target, and a path that passes
192
+ * the same filter as include directives.
193
+ *
194
+ * @param {string} name text before the rule colon
195
+ * @returns {boolean} true when the target can be recorded
196
+ */
197
+ function isRecordableMakeTarget(name) {
198
+ if (!name || /[\s"'`]/.test(name)) {
199
+ return false;
200
+ }
201
+ if (".#$-".includes(name[0]) || name.includes("%")) {
202
+ return false;
203
+ }
204
+ return isRecordableIncludePath(name);
205
+ }
206
+
207
+ /**
208
+ * The variable name a `define` line opens, without a trailing assignment
209
+ * operator.
210
+ *
211
+ * @param {string[]} words tokens after the `define` keyword
212
+ * @returns {string|undefined} the variable name
213
+ */
214
+ function defineVariableName(words) {
215
+ let name = words[0] || "";
216
+ while (name && "?+!:=".includes(name[name.length - 1])) {
217
+ name = name.slice(0, -1);
218
+ }
219
+ return MAKE_IDENTIFIER.test(name) ? name : undefined;
220
+ }
221
+
222
+ /**
223
+ * Collect target names, include directives and variable names from a make
224
+ * file. Values are deliberately not recorded: flags can carry tokens or
225
+ * machine-specific paths, and a name is enough for audit triage. Continuation
226
+ * lines are joined first and `define ... endef` bodies are skipped, so text
227
+ * from inside a value is never read as a target. Lines are tokenised with
228
+ * indexOf, so the work stays linear in the line length.
229
+ *
230
+ * @param {string} content make file text
231
+ * @returns {{ targets: string[], includes: string[], variables: string[] }}
232
+ */
233
+ function parseMakeContent(content) {
234
+ const targets = [];
235
+ const includes = [];
236
+ const variables = [];
237
+ let defineDepth = 0;
238
+ for (const line of joinContinuationLines(content, {
239
+ commentLinesContinue: true,
240
+ })) {
241
+ const trimmed = line.trim();
242
+ if (!trimmed || trimmed.startsWith("#")) {
243
+ continue;
244
+ }
245
+ const words = tokens(trimmed);
246
+ let start = 0;
247
+ while (
248
+ start < words.length - 1 &&
249
+ MAKE_VARIABLE_MODIFIERS.has(words[start])
250
+ ) {
251
+ start += 1;
252
+ }
253
+ const directive = words[start];
254
+ if (defineDepth > 0) {
255
+ if (directive === "endef") {
256
+ defineDepth -= 1;
257
+ } else if (directive === "define") {
258
+ defineDepth += 1;
259
+ }
260
+ continue;
261
+ }
262
+ // Recipe lines cannot declare targets or variables.
263
+ if (line.startsWith("\t")) {
264
+ continue;
265
+ }
266
+ if (directive === "define") {
267
+ defineDepth = 1;
268
+ const name = defineVariableName(words.slice(start + 1));
269
+ if (name) {
270
+ variables.push(name);
271
+ }
272
+ continue;
273
+ }
274
+ if (directive === "endef") {
275
+ continue;
276
+ }
277
+ if (MAKE_INCLUDE_DIRECTIVES.has(words[0])) {
278
+ includes.push(...words.slice(1).filter(isRecordableIncludePath));
279
+ continue;
280
+ }
281
+ // export, override and private qualify the assignment that follows.
282
+ const body = start ? words.slice(start).join(" ") : trimmed;
283
+ const eq = body.indexOf("=");
284
+ const colon = body.indexOf(":");
285
+ // An assignment: `=`, `?=`, `+=`, `!=`, `:=`, `::=` or `:::=` before any
286
+ // rule colon.
287
+ const colonIsOperator =
288
+ colon !== -1 &&
289
+ eq > colon &&
290
+ body
291
+ .slice(colon, eq)
292
+ .split("")
293
+ .every((ch) => ch === ":");
294
+ if (eq !== -1 && (colon === -1 || eq < colon || colonIsOperator)) {
295
+ let nameEnd = colonIsOperator ? colon : eq;
296
+ while (nameEnd > 0 && "?+!".includes(body[nameEnd - 1])) {
297
+ nameEnd -= 1;
298
+ }
299
+ const name = body.slice(0, nameEnd).trim();
300
+ if (MAKE_IDENTIFIER.test(name)) {
301
+ variables.push(name);
302
+ }
303
+ continue;
304
+ }
305
+ if (start || colon <= 0) {
306
+ continue;
307
+ }
308
+ // A Windows drive path such as C:\out\app.exe names the machine.
309
+ if (
310
+ colon === 1 &&
311
+ /^[A-Za-z]$/.test(body[0]) &&
312
+ (body[2] === "\\" || body[2] === "/")
313
+ ) {
314
+ continue;
315
+ }
316
+ const name = body.slice(0, colon).trim();
317
+ if (isRecordableMakeTarget(name)) {
318
+ targets.push(name);
319
+ }
320
+ }
321
+ return { targets, includes, variables };
322
+ }
323
+
324
+ function parseMakeFile(filePath, rootDir) {
325
+ const content = readBounded(filePath, rootDir);
326
+ if (!content) {
327
+ return undefined;
328
+ }
329
+ const { targets, includes, variables } = parseMakeContent(content);
330
+ const component = createBuildInputComponent(
331
+ basename(dirname(filePath)) || basename(filePath),
332
+ filePath,
333
+ "make",
334
+ rootDir,
335
+ );
336
+ const properties = component.properties;
337
+ appendProperty(properties, "cdx:make:targetCount", targets.length);
338
+ appendProperty(
339
+ properties,
340
+ "cdx:make:targets",
341
+ targets.length ? targets.slice(0, MAX_LISTED).join(", ") : undefined,
342
+ );
343
+ appendProperty(
344
+ properties,
345
+ "cdx:make:includes",
346
+ includes.length ? includes.slice(0, MAX_LISTED).join(", ") : undefined,
347
+ );
348
+ appendProperty(
349
+ properties,
350
+ "cdx:make:variables",
351
+ variables.length ? variables.slice(0, MAX_LISTED).join(", ") : undefined,
352
+ );
353
+ appendProperty(
354
+ properties,
355
+ "cdx:make:usesDocker",
356
+ targets.some((t) => /docker/i.test(t)) ? "true" : undefined,
357
+ );
358
+ return component;
359
+ }
360
+
361
+ /**
362
+ * Remove CMake comments: `#` line comments and `#[[ ... ]]` bracket
363
+ * comments, outside quoted and bracket arguments. The text is scanned once
364
+ * with indexOf, so the work stays linear in the file size.
365
+ *
366
+ * @param {string} content CMakeLists.txt text
367
+ * @returns {string} the text without comments
368
+ */
369
+ function stripCMakeComments(content) {
370
+ const kept = [];
371
+ let segmentStart = 0;
372
+ let index = 0;
373
+ let inQuote = false;
374
+ // A bracket opener is `[`, any number of `=`, then `[`; it returns the
375
+ // matching closer, or undefined.
376
+ const bracketCloser = (at) => {
377
+ let cursor = at + 1;
378
+ while (content[cursor] === "=") {
379
+ cursor += 1;
380
+ }
381
+ return content[cursor] === "["
382
+ ? `]${"=".repeat(cursor - at - 1)}]`
383
+ : undefined;
384
+ };
385
+ while (index < content.length) {
386
+ const ch = content[index];
387
+ if (ch === "\\") {
388
+ index += 2;
389
+ continue;
390
+ }
391
+ if (inQuote) {
392
+ if (ch === '"') {
393
+ inQuote = false;
394
+ }
395
+ index += 1;
396
+ continue;
397
+ }
398
+ if (ch === '"') {
399
+ inQuote = true;
400
+ index += 1;
401
+ continue;
402
+ }
403
+ if (ch === "[") {
404
+ const closer = bracketCloser(index);
405
+ if (closer) {
406
+ const end = content.indexOf(closer, index);
407
+ index = end === -1 ? content.length : end + closer.length;
408
+ continue;
409
+ }
410
+ index += 1;
411
+ continue;
412
+ }
413
+ if (ch === "#") {
414
+ kept.push(content.slice(segmentStart, index));
415
+ const closer =
416
+ content[index + 1] === "[" ? bracketCloser(index + 1) : undefined;
417
+ let end;
418
+ if (closer) {
419
+ end = content.indexOf(closer, index);
420
+ end = end === -1 ? content.length : end + closer.length;
421
+ } else {
422
+ end = content.indexOf("\n", index);
423
+ end = end === -1 ? content.length : end;
424
+ }
425
+ index = end;
426
+ segmentStart = end;
427
+ continue;
428
+ }
429
+ index += 1;
430
+ }
431
+ kept.push(content.slice(segmentStart));
432
+ return kept.join("");
433
+ }
434
+
435
+ /**
436
+ * Blank the inside of CMake quoted arguments, keeping the text length, so a
437
+ * command name or parenthesis inside a string is not read as code. Indexes
438
+ * into the result point at the same characters of the input.
439
+ *
440
+ * @param {string} content CMakeLists.txt text without comments
441
+ * @returns {string} the text with quoted argument contents replaced by spaces
442
+ */
443
+ function maskCMakeStrings(content) {
444
+ const kept = [];
445
+ let segmentStart = 0;
446
+ let index = 0;
447
+ while (index < content.length) {
448
+ const ch = content[index];
449
+ if (ch === "\\") {
450
+ index += 2;
451
+ continue;
452
+ }
453
+ if (ch !== '"') {
454
+ index += 1;
455
+ continue;
456
+ }
457
+ let end = index + 1;
458
+ while (end < content.length && content[end] !== '"') {
459
+ end += content[end] === "\\" ? 2 : 1;
460
+ }
461
+ end = Math.min(end, content.length);
462
+ kept.push(content.slice(segmentStart, index + 1));
463
+ kept.push(" ".repeat(end - index - 1));
464
+ segmentStart = end;
465
+ index = end + 1;
466
+ }
467
+ kept.push(content.slice(segmentStart));
468
+ return kept.join("");
469
+ }
470
+
471
+ const CMAKE_PROJECT_KEYWORDS = new Set([
472
+ "DESCRIPTION",
473
+ "HOMEPAGE_URL",
474
+ "LANGUAGES",
475
+ "VERSION",
476
+ ]);
477
+ const CMAKE_LANGUAGES = new Set([
478
+ "ASM",
479
+ "ASM-ATT",
480
+ "ASM_MASM",
481
+ "ASM_NASM",
482
+ "C",
483
+ "CSharp",
484
+ "CUDA",
485
+ "CXX",
486
+ "Fortran",
487
+ "HIP",
488
+ "ISPC",
489
+ "Java",
490
+ "OBJC",
491
+ "OBJCXX",
492
+ "Swift",
493
+ ]);
494
+
495
+ /**
496
+ * Read the first `project(<name> ...)` command of a CMake file whose
497
+ * comments were removed. Command names are case-insensitive in CMake. The
498
+ * arguments are cut at the next `)` with indexOf, so a file full of
499
+ * unterminated `project(` calls is still read in linear time. Languages are
500
+ * the arguments after `LANGUAGES`, or, in the short form
501
+ * `project(name C CXX)`, the arguments before the first keyword.
502
+ *
503
+ * @param {string} content CMakeLists.txt text without comments
504
+ * @param {string} masked the same text with quoted contents blanked
505
+ * @returns {{ name: string, languages: string[] }|undefined}
506
+ */
507
+ function parseCMakeProject(content, masked) {
508
+ const projectCall = /\bproject\s*\(/i.exec(masked);
509
+ if (!projectCall) {
510
+ return undefined;
511
+ }
512
+ const argsStart = projectCall.index + projectCall[0].length;
513
+ const argsEnd = masked.indexOf(")", argsStart);
514
+ if (argsEnd === -1) {
515
+ return undefined;
516
+ }
517
+ const args = tokens(content.slice(argsStart, argsEnd)).map((arg) =>
518
+ arg.length >= 2 && arg.startsWith('"') && arg.endsWith('"')
519
+ ? arg.slice(1, -1)
520
+ : arg,
521
+ );
522
+ const name = args[0];
523
+ if (!name || !/^[A-Za-z0-9_.-]+$/.test(name)) {
524
+ return undefined;
525
+ }
526
+ const rest = args.slice(1);
527
+ const languagesAt = rest.indexOf("LANGUAGES");
528
+ const languageArgs = [];
529
+ for (const arg of rest.slice(languagesAt + 1)) {
530
+ if (CMAKE_PROJECT_KEYWORDS.has(arg)) {
531
+ break;
532
+ }
533
+ languageArgs.push(arg);
534
+ }
535
+ return {
536
+ name,
537
+ languages: [...new Set(languageArgs.filter((a) => CMAKE_LANGUAGES.has(a)))],
538
+ };
539
+ }
540
+
541
+ function parseCMakeLists(filePath, rootDir) {
542
+ const raw = readBounded(filePath, rootDir);
543
+ if (!raw) {
544
+ return undefined;
545
+ }
546
+ const content = stripCMakeComments(raw);
547
+ const masked = maskCMakeStrings(content);
548
+ const minimum = /\bcmake_minimum_required\s*\(\s*VERSION\s+([0-9.]+)/i.exec(
549
+ masked,
550
+ );
551
+ const project = parseCMakeProject(content, masked);
552
+ const executableCount = (masked.match(/\badd_executable\s*\(/gi) || [])
553
+ .length;
554
+ const libraryCount = (masked.match(/\badd_library\s*\(/gi) || []).length;
555
+ const component = createBuildInputComponent(
556
+ project?.name || basename(dirname(filePath)) || "cmake-project",
557
+ filePath,
558
+ "cmake",
559
+ rootDir,
560
+ );
561
+ const properties = component.properties;
562
+ appendProperty(properties, "cdx:cmake:minimumVersion", minimum?.[1]);
563
+ appendProperty(
564
+ properties,
565
+ "cdx:cmake:languages",
566
+ project?.languages.length ? project.languages.join(", ") : undefined,
567
+ );
568
+ appendProperty(properties, "cdx:cmake:executableCount", executableCount);
569
+ appendProperty(properties, "cdx:cmake:libraryCount", libraryCount);
570
+ return component;
571
+ }
572
+
573
+ /**
574
+ * Parser for make and CMake build inputs (C/C++ and friends).
575
+ *
576
+ * @type {{id: string, patterns: string[], parse(files: string[], options?: Object, context?: {rootDir?: string}): {components: Object[]}}}
577
+ */
578
+ export const makefileFormulationParser = {
579
+ id: "make-build",
580
+ patterns: [
581
+ "**/Makefile",
582
+ "**/makefile",
583
+ "**/GNUmakefile",
584
+ "**/*.mk",
585
+ "**/CMakeLists.txt",
586
+ ],
587
+ parse(files, _options, context = {}) {
588
+ const components = [];
589
+ for (const filePath of files || []) {
590
+ const base = basename(filePath).toLowerCase();
591
+ const component = base.startsWith("cmakelists")
592
+ ? parseCMakeLists(filePath, context.rootDir)
593
+ : parseMakeFile(filePath, context.rootDir);
594
+ if (component) {
595
+ components.push(component);
596
+ }
597
+ }
598
+ return { components };
599
+ },
600
+ };
601
+
602
+ /**
603
+ * Read the directives of a go.mod file. Requirements are counted from
604
+ * single-line `require` directives and the lines of `require ( ... )`
605
+ * blocks only; `replace`, `exclude` and `retract` lines are not
606
+ * requirements. Lines are split into tokens, with no regex over the file.
607
+ *
608
+ * @param {string} content go.mod text
609
+ * @returns {{ module: string|undefined, goVersion: string|undefined, toolchain: string|undefined, requireCount: number, indirectCount: number }}
610
+ */
611
+ function parseGoModContent(content) {
612
+ const result = {
613
+ module: undefined,
614
+ goVersion: undefined,
615
+ toolchain: undefined,
616
+ requireCount: 0,
617
+ indirectCount: 0,
618
+ };
619
+ const unquote = (value) =>
620
+ value && value.length >= 2 && value.startsWith('"') && value.endsWith('"')
621
+ ? value.slice(1, -1)
622
+ : value;
623
+ const countRequirement = (spec, comment) => {
624
+ const parts = tokens(spec);
625
+ if (parts.length < 2 || !parts[1].startsWith("v")) {
626
+ return;
627
+ }
628
+ result.requireCount += 1;
629
+ if (comment && tokens(comment.replaceAll(";", " ")).includes("indirect")) {
630
+ result.indirectCount += 1;
631
+ }
632
+ };
633
+ let block;
634
+ for (const rawLine of boundedLines(content)) {
635
+ const commentStart = rawLine.indexOf("//");
636
+ const code = (
637
+ commentStart === -1 ? rawLine : rawLine.slice(0, commentStart)
638
+ ).trim();
639
+ const comment =
640
+ commentStart === -1 ? "" : rawLine.slice(commentStart + 2).trim();
641
+ if (block) {
642
+ if (code === ")") {
643
+ block = undefined;
644
+ } else if (block === "require" && code) {
645
+ countRequirement(code, comment);
646
+ }
647
+ continue;
648
+ }
649
+ if (!code) {
650
+ continue;
651
+ }
652
+ const words = tokens(code);
653
+ const directive = words[0];
654
+ if (words.length === 2 && words[1] === "(") {
655
+ block = directive;
656
+ continue;
657
+ }
658
+ if (words.length === 1 && directive.length > 1 && directive.endsWith("(")) {
659
+ block = directive.slice(0, -1);
660
+ continue;
661
+ }
662
+ if (directive === "module" && result.module === undefined) {
663
+ result.module = unquote(words[1]);
664
+ } else if (directive === "go" && result.goVersion === undefined) {
665
+ result.goVersion = words[1];
666
+ } else if (directive === "toolchain" && result.toolchain === undefined) {
667
+ result.toolchain = words[1];
668
+ } else if (directive === "require") {
669
+ countRequirement(words.slice(1).join(" "), comment);
670
+ }
671
+ }
672
+ return result;
673
+ }
674
+
675
+ function parseGoMod(filePath, rootDir) {
676
+ const content = readBounded(filePath, rootDir);
677
+ if (!content) {
678
+ return undefined;
679
+ }
680
+ const { module, goVersion, toolchain, requireCount, indirectCount } =
681
+ parseGoModContent(content);
682
+ if (!module) {
683
+ return undefined;
684
+ }
685
+ const component = createBuildInputComponent(module, filePath, "go", rootDir);
686
+ const properties = component.properties;
687
+ appendProperty(properties, "cdx:go:module", module);
688
+ appendProperty(properties, "cdx:go:version", goVersion);
689
+ appendProperty(properties, "cdx:go:toolchain", toolchain);
690
+ appendProperty(properties, "cdx:go:requireCount", requireCount);
691
+ appendProperty(properties, "cdx:go:indirectCount", indirectCount);
692
+ return component;
693
+ }
694
+
695
+ /**
696
+ * Parser for go.mod build inputs. The compiled binaries are read by the
697
+ * binary/OBOM path through the bounded Go buildinfo reader, not here.
698
+ *
699
+ * @type {{id: string, patterns: string[], parse(files: string[], options?: Object, context?: {rootDir?: string}): {components: Object[]}}}
700
+ */
701
+ export const goModuleFormulationParser = {
702
+ id: "go-module",
703
+ patterns: ["**/go.mod"],
704
+ parse(files, _options, context = {}) {
705
+ const components = [];
706
+ for (const filePath of files || []) {
707
+ const component = parseGoMod(filePath, context.rootDir);
708
+ if (component) {
709
+ components.push(component);
710
+ }
711
+ }
712
+ return { components };
713
+ },
714
+ };
715
+
716
+ /**
717
+ * Whether a pip requirement is a direct reference rather than a name with a
718
+ * version specifier: `name @ url` (PEP 508), a bare URL, or a local path.
719
+ * Such a requirement is never an exact pin, even when its URL holds `==`.
720
+ *
721
+ * @param {string} requirement requirement text before any option
722
+ * @returns {boolean} true for a direct reference
723
+ */
724
+ function isPipDirectReference(requirement) {
725
+ const schemeAt = requirement.indexOf("://");
726
+ const at = requirement.indexOf("@");
727
+ if (at !== -1 && (schemeAt === -1 || at < schemeAt)) {
728
+ return true;
729
+ }
730
+ if (schemeAt !== -1) {
731
+ return true;
732
+ }
733
+ return (
734
+ requirement.startsWith(".") ||
735
+ requirement.startsWith("/") ||
736
+ requirement.startsWith("\\") ||
737
+ (requirement[1] === ":" && /^[A-Za-z]$/.test(requirement[0]))
738
+ );
739
+ }
740
+
741
+ /**
742
+ * Count the requirements of a pip requirements or constraints file and how
743
+ * many of them are exact pins (`==` or `===`). Lines ending in a backslash
744
+ * continue on the next line, which is where pip-compile writes the
745
+ * `--hash` options, so hashes are looked for in the joined logical line.
746
+ * Editable installs (`-e`, `--editable`) are requirements but never pins.
747
+ *
748
+ * @param {string} content file text
749
+ * @returns {{ requirementCount: number, pinCount: number, hasHashes: boolean }}
750
+ */
751
+ function parsePipContent(content) {
752
+ let requirementCount = 0;
753
+ let pinCount = 0;
754
+ let hasHashes = false;
755
+ // As in pip's join_lines, a comment line never continues, even when it
756
+ // ends in a backslash; it ends any continuation in progress instead.
757
+ for (const logicalLine of joinContinuationLines(content, {
758
+ commentLinesContinue: false,
759
+ })) {
760
+ const trimmed = logicalLine.trim();
761
+ if (!trimmed || trimmed.startsWith("#")) {
762
+ continue;
763
+ }
764
+ const commentStart = trimmed.indexOf(" #");
765
+ const requirement = (
766
+ commentStart === -1 ? trimmed : trimmed.slice(0, commentStart)
767
+ ).trim();
768
+ if (!requirement) {
769
+ continue;
770
+ }
771
+ const words = tokens(requirement);
772
+ const isEditable =
773
+ words[0] === "--editable" ||
774
+ words[0].startsWith("--editable=") ||
775
+ (words[0].startsWith("-e") && !words[0].startsWith("--"));
776
+ // Other option lines such as --index-url, -r and -c are not
777
+ // requirements.
778
+ if (requirement.startsWith("-") && !isEditable) {
779
+ continue;
780
+ }
781
+ requirementCount += 1;
782
+ if (words.some((word) => word.toLowerCase().startsWith("--hash"))) {
783
+ hasHashes = true;
784
+ }
785
+ if (isEditable) {
786
+ continue;
787
+ }
788
+ // The specifier ends at the first option or environment marker.
789
+ const optionStart = words.findIndex((word) => word.startsWith("--"));
790
+ const specifierWords =
791
+ optionStart === -1 ? words : words.slice(0, optionStart);
792
+ if (isPipDirectReference(specifierWords.join(" "))) {
793
+ continue;
794
+ }
795
+ let specifier = specifierWords.join("");
796
+ const markerStart = specifier.indexOf(";");
797
+ if (markerStart !== -1) {
798
+ specifier = specifier.slice(0, markerStart);
799
+ }
800
+ const pinStart = specifier.indexOf("==");
801
+ if (
802
+ pinStart !== -1 &&
803
+ !specifier.includes(",") &&
804
+ !specifier.slice(pinStart).includes("*")
805
+ ) {
806
+ pinCount += 1;
807
+ }
808
+ }
809
+ return { requirementCount, pinCount, hasHashes };
810
+ }
811
+
812
+ function parsePipRequirements(filePath, rootDir) {
813
+ const content = readBounded(filePath, rootDir);
814
+ if (!content) {
815
+ return undefined;
816
+ }
817
+ const { requirementCount, pinCount, hasHashes } = parsePipContent(content);
818
+ const component = createBuildInputComponent(
819
+ basename(filePath),
820
+ filePath,
821
+ "pip",
822
+ rootDir,
823
+ );
824
+ const properties = component.properties;
825
+ appendProperty(properties, "cdx:pip:requirementCount", requirementCount);
826
+ appendProperty(properties, "cdx:pip:pinCount", pinCount);
827
+ appendProperty(
828
+ properties,
829
+ "cdx:pip:unpinnedCount",
830
+ requirementCount - pinCount,
831
+ );
832
+ appendProperty(
833
+ properties,
834
+ "cdx:pip:hasHashes",
835
+ hasHashes ? "true" : undefined,
836
+ );
837
+ appendProperty(
838
+ properties,
839
+ "cdx:pip:kind",
840
+ /constraints/i.test(basename(filePath)) ? "constraints" : "requirements",
841
+ );
842
+ return component;
843
+ }
844
+
845
+ /**
846
+ * Parser for pip requirements and constraints files.
847
+ *
848
+ * @type {{id: string, patterns: string[], parse(files: string[], options?: Object, context?: {rootDir?: string}): {components: Object[]}}}
849
+ */
850
+ export const pythonRequirementsFormulationParser = {
851
+ id: "python-requirements",
852
+ patterns: [
853
+ "**/requirements*.txt",
854
+ "**/constraints*.txt",
855
+ "**/requirements/*.txt",
856
+ "**/requirements.d/*.txt",
857
+ ],
858
+ parse(files, _options, context = {}) {
859
+ const components = [];
860
+ for (const filePath of files || []) {
861
+ const component = parsePipRequirements(filePath, context.rootDir);
862
+ if (component) {
863
+ components.push(component);
864
+ }
865
+ }
866
+ return { components };
867
+ },
868
+ };
869
+
870
+ function parseCsProj(filePath, rootDir) {
871
+ const content = readBounded(filePath, rootDir);
872
+ if (!content) {
873
+ return undefined;
874
+ }
875
+ const sdk = /\bSdk\s*=\s*"([^"]+)"/.exec(content);
876
+ const targetFramework =
877
+ /<TargetFrameworks?>([^<]+)<\/TargetFrameworks?>/.exec(content);
878
+ const outputType = /<OutputType>([^<]+)<\/OutputType>/.exec(content);
879
+ const version = /<Version>([^<]+)<\/Version>/.exec(content);
880
+ const packageReferenceCount = (
881
+ content.match(/<PackageReference\s+Include=/g) || []
882
+ ).length;
883
+ const component = createBuildInputComponent(
884
+ basename(filePath).replace(/\.csproj$/i, ""),
885
+ filePath,
886
+ "nuget",
887
+ rootDir,
888
+ );
889
+ const properties = component.properties;
890
+ appendProperty(properties, "cdx:msbuild:sdk", sdk?.[1]);
891
+ appendProperty(
892
+ properties,
893
+ "cdx:msbuild:targetFrameworks",
894
+ targetFramework?.[1]?.trim(),
895
+ );
896
+ appendProperty(properties, "cdx:msbuild:outputType", outputType?.[1]?.trim());
897
+ appendProperty(properties, "cdx:msbuild:version", version?.[1]?.trim());
898
+ appendProperty(
899
+ properties,
900
+ "cdx:msbuild:packageReferenceCount",
901
+ packageReferenceCount,
902
+ );
903
+ return component;
904
+ }
905
+
906
+ function parseProjectAssets(filePath, rootDir) {
907
+ const content = readBounded(filePath, rootDir, MAX_ASSETS_READ_BYTES);
908
+ if (!content) {
909
+ return undefined;
910
+ }
911
+ let data;
912
+ try {
913
+ data = JSON.parse(content);
914
+ } catch {
915
+ return undefined;
916
+ }
917
+ if (!data || typeof data !== "object") {
918
+ return undefined;
919
+ }
920
+ const frameworks = Object.keys(data.project?.frameworks || {});
921
+ const libraryCount = Object.keys(data.libraries || {}).length;
922
+ const targetCount = Object.keys(data.targets || {}).length;
923
+ // The restore project name is an MSBuild project name, never a path.
924
+ const projectName = data.project?.restore?.projectName;
925
+ const component = createBuildInputComponent(
926
+ (typeof projectName === "string" &&
927
+ !/[\\/]/.test(projectName) &&
928
+ projectName) ||
929
+ basename(dirname(filePath)) ||
930
+ "nuget-restore",
931
+ filePath,
932
+ "nuget",
933
+ rootDir,
934
+ );
935
+ const properties = component.properties;
936
+ const assetsVersion =
937
+ typeof data.version === "string" ||
938
+ (typeof data.version === "number" && Number.isFinite(data.version))
939
+ ? data.version
940
+ : undefined;
941
+ appendProperty(properties, "cdx:nuget:assetsVersion", assetsVersion);
942
+ appendProperty(
943
+ properties,
944
+ "cdx:nuget:frameworks",
945
+ frameworks.length ? frameworks.slice(0, MAX_LISTED).join(", ") : undefined,
946
+ );
947
+ appendProperty(properties, "cdx:nuget:libraryCount", libraryCount);
948
+ appendProperty(properties, "cdx:nuget:targetCount", targetCount);
949
+ return component;
950
+ }
951
+
952
+ /**
953
+ * Parser for .NET build inputs: project.assets.json (the file `dotnet
954
+ * restore` writes; some projects call it packages.assets.json) and the
955
+ * SDK-style .csproj project files.
956
+ *
957
+ * @type {{id: string, patterns: string[], parse(files: string[], options?: Object, context?: {rootDir?: string}): {components: Object[]}}}
958
+ */
959
+ export const dotnetProjectFormulationParser = {
960
+ id: "dotnet-project",
961
+ patterns: [
962
+ "**/project.assets.json",
963
+ "**/packages.assets.json",
964
+ "**/*.csproj",
965
+ ],
966
+ parse(files, _options, context = {}) {
967
+ const components = [];
968
+ for (const filePath of files || []) {
969
+ const base = basename(filePath).toLowerCase();
970
+ const component = base.endsWith("assets.json")
971
+ ? parseProjectAssets(filePath, context.rootDir)
972
+ : parseCsProj(filePath, context.rootDir);
973
+ if (component) {
974
+ components.push(component);
975
+ }
976
+ }
977
+ return { components };
978
+ },
979
+ };