@cdxgen/cdxgen 13.4.0 → 13.6.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 (305) hide show
  1. package/README.md +9 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +29 -5
  4. package/bin/evinse.js +8 -0
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +29 -0
  7. package/bin/validate.js +6 -0
  8. package/data/cbomosdb-queries.json +13 -1
  9. package/data/component-tags.json +8 -0
  10. package/data/license-aliases.json +24 -2
  11. package/data/queries-darwin.json +25 -1
  12. package/data/queries-win.json +19 -1
  13. package/data/queries.json +41 -0
  14. package/data/remediations.json +407 -1
  15. package/data/rules/ai-bom.yaml +1 -1
  16. package/data/rules/brotli-decoder.yaml +107 -0
  17. package/data/rules/cbom-security.yaml +235 -4
  18. package/data/rules/go-binaries.yaml +46 -0
  19. package/data/rules/golem-go.yaml +80 -0
  20. package/data/rules/hbom-compliance.yaml +40 -22
  21. package/data/rules/hbom-performance.yaml +7 -1
  22. package/data/rules/host-topology.yaml +1 -1
  23. package/data/rules/obom-runtime.yaml +31 -2
  24. package/data/rules/php-runtime.yaml +143 -0
  25. package/data/rules/vscode-extensions.yaml +75 -0
  26. package/data/spdx-license-list.json +121 -7
  27. package/data/spdx-licenses.json +69 -0
  28. package/data/spdx.schema.json +845 -2
  29. package/lib/audit/index.js +14 -0
  30. package/lib/cli/bomAssembly.js +120 -6
  31. package/lib/cli/cliOptions.js +110 -3
  32. package/lib/cli/index.js +182 -6
  33. package/lib/cli/jsBom.js +153 -64
  34. package/lib/cli/jvmBom.js +363 -66
  35. package/lib/cli/managedBom.js +853 -87
  36. package/lib/cli/nativeBom.js +267 -33
  37. package/lib/core/boundedFile.js +344 -0
  38. package/lib/core/brotliProbe.js +182 -0
  39. package/lib/core/env.js +38 -8
  40. package/lib/core/fs.js +40 -19
  41. package/lib/core/httpClient.js +172 -46
  42. package/lib/core/lockPaths.js +38 -0
  43. package/lib/core/paths.js +24 -1
  44. package/lib/core/processTree.js +5 -0
  45. package/lib/core/projectConfig.js +1 -0
  46. package/lib/core/projectTooling.js +97 -0
  47. package/lib/core/propertySanitizer.js +72 -0
  48. package/lib/core/toolRegistry.js +170 -0
  49. package/lib/ecosystems/cppEvidence.js +263 -106
  50. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  51. package/lib/ecosystems/ecosystems.js +25 -10
  52. package/lib/ecosystems/gemHostProbe.js +301 -0
  53. package/lib/ecosystems/golem.js +550 -6
  54. package/lib/ecosystems/gradleutils.js +858 -244
  55. package/lib/ecosystems/parsers-bazel.js +107 -6
  56. package/lib/ecosystems/parsers-dotnet.js +117 -18
  57. package/lib/ecosystems/parsers-go.js +1090 -367
  58. package/lib/ecosystems/parsers-js.js +505 -22
  59. package/lib/ecosystems/parsers-jvm.js +259 -17
  60. package/lib/ecosystems/parsers-misc.js +438 -64
  61. package/lib/ecosystems/parsers-python.js +100 -12
  62. package/lib/ecosystems/pythonutils.js +128 -2
  63. package/lib/ecosystems/rubyutils.js +544 -34
  64. package/lib/ecosystems/rusi.js +10 -0
  65. package/lib/ecosystems/scalacliutils.js +4 -1
  66. package/lib/ecosystems/scalasem.js +798 -151
  67. package/lib/ecosystems/vendoredCode.js +237 -3
  68. package/lib/ecosystems/venvProbe.js +360 -0
  69. package/lib/evinser/evinser.js +472 -61
  70. package/lib/evinser/swiftsem.js +68 -5
  71. package/lib/helpers/agenticToolutils.js +413 -93
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentSkillSpec.js +17 -0
  74. package/lib/inventory/apkInstalledDb.js +243 -0
  75. package/lib/inventory/atomUtils.js +110 -0
  76. package/lib/inventory/auditCategories.js +3 -3
  77. package/lib/inventory/brotliDecoder.js +142 -0
  78. package/lib/inventory/buildToolRateLimit.js +1 -1
  79. package/lib/inventory/cacheDir.js +52 -9
  80. package/lib/inventory/cbomutils.js +56 -7
  81. package/lib/inventory/certificates.js +2371 -0
  82. package/lib/inventory/communityAiConfigParser.js +4 -9
  83. package/lib/inventory/deps.js +41 -4
  84. package/lib/inventory/display.js +200 -3
  85. package/lib/inventory/dosai.js +639 -63
  86. package/lib/inventory/dotnetRuntime.js +217 -0
  87. package/lib/inventory/dynamic.js +45 -3
  88. package/lib/inventory/envcontext.js +475 -18
  89. package/lib/inventory/evidenceUtils.js +447 -23
  90. package/lib/inventory/fetchBatch.js +1 -1
  91. package/lib/inventory/formulationParsers.js +32 -3
  92. package/lib/inventory/goBuildInfo.js +140 -0
  93. package/lib/inventory/gtfobins.js +13 -0
  94. package/lib/inventory/hbom.js +67 -7
  95. package/lib/inventory/hbomAnalysis.js +16 -0
  96. package/lib/inventory/hostRuntimeProbe.js +686 -0
  97. package/lib/inventory/hostTopology.js +21 -0
  98. package/lib/inventory/jvmLocalRepos.js +9 -2
  99. package/lib/inventory/jvmToolEnv.js +29 -0
  100. package/lib/inventory/licenseEnhancer.js +12 -0
  101. package/lib/inventory/mavenCentralMirror.js +534 -0
  102. package/lib/inventory/nodeBuildInfo.js +218 -0
  103. package/lib/inventory/nodeHostProbe.js +202 -0
  104. package/lib/inventory/osPackagePurl.js +244 -0
  105. package/lib/inventory/osPackageResolver.js +271 -87
  106. package/lib/inventory/osinfo.js +79 -31
  107. package/lib/inventory/osqueryTransform.js +114 -5
  108. package/lib/inventory/phpBuildInfo.js +438 -0
  109. package/lib/inventory/phpHostProbe.js +185 -0
  110. package/lib/inventory/rootfsCerts.js +426 -0
  111. package/lib/inventory/rubyBuildInfo.js +972 -0
  112. package/lib/inventory/rubyHostProbe.js +218 -0
  113. package/lib/inventory/scalaCoords.js +4 -3
  114. package/lib/inventory/scalaDedupe.js +440 -0
  115. package/lib/inventory/source.js +256 -5
  116. package/lib/inventory/spdx.js +144 -0
  117. package/lib/inventory/traceRunner.js +5 -1
  118. package/lib/inventory/tracedCrypto.js +129 -0
  119. package/lib/managers/binary.js +866 -186
  120. package/lib/managers/docker.js +245 -12
  121. package/lib/parsers/email.js +277 -0
  122. package/lib/parsers/largeJson.js +62 -13
  123. package/lib/parsers/vscodeExtensionId.js +183 -0
  124. package/lib/server/server.js +198 -2
  125. package/lib/stages/postgen/annotator.js +17 -1
  126. package/lib/stages/postgen/introspection/reflect.js +4 -1
  127. package/lib/stages/postgen/postgen.js +5 -5
  128. package/lib/stages/postgen/ruleEngine.js +71 -6
  129. package/lib/stages/postgen/specVersionCompat.js +30 -0
  130. package/lib/stages/pregen/envAudit.js +1 -0
  131. package/lib/stages/pregen/pregen.js +231 -62
  132. package/lib/validator/bomValidator.js +32 -6
  133. package/lib/validator/generated/runtime.mjs +13 -0
  134. package/lib/validator/generated/validate-1.6.mjs +27 -8
  135. package/lib/validator/generated/validate-1.7.mjs +32 -13
  136. package/lib/validator/generated/validate-2.0.mjs +31 -12
  137. package/lib/validator/index.js +1 -1
  138. package/package.json +19 -18
  139. package/types/bin/cdxgen-secure.d.ts +3 -0
  140. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  141. package/types/bin/repl.d.ts.map +1 -1
  142. package/types/lib/audit/index.d.ts.map +1 -1
  143. package/types/lib/cli/bomAssembly.d.ts +22 -3
  144. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  145. package/types/lib/cli/cliOptions.d.ts +43 -0
  146. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  147. package/types/lib/cli/index.d.ts.map +1 -1
  148. package/types/lib/cli/jsBom.d.ts +1 -17
  149. package/types/lib/cli/jsBom.d.ts.map +1 -1
  150. package/types/lib/cli/jvmBom.d.ts +20 -0
  151. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  152. package/types/lib/cli/managedBom.d.ts.map +1 -1
  153. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  154. package/types/lib/core/boundedFile.d.ts +70 -0
  155. package/types/lib/core/boundedFile.d.ts.map +1 -0
  156. package/types/lib/core/brotliProbe.d.ts +37 -0
  157. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  158. package/types/lib/core/env.d.ts +22 -2
  159. package/types/lib/core/env.d.ts.map +1 -1
  160. package/types/lib/core/fs.d.ts.map +1 -1
  161. package/types/lib/core/httpClient.d.ts +24 -0
  162. package/types/lib/core/httpClient.d.ts.map +1 -1
  163. package/types/lib/core/lockPaths.d.ts +19 -0
  164. package/types/lib/core/lockPaths.d.ts.map +1 -0
  165. package/types/lib/core/processTree.d.ts +2 -0
  166. package/types/lib/core/processTree.d.ts.map +1 -1
  167. package/types/lib/core/projectConfig.d.ts.map +1 -1
  168. package/types/lib/core/projectTooling.d.ts +41 -0
  169. package/types/lib/core/projectTooling.d.ts.map +1 -0
  170. package/types/lib/core/propertySanitizer.d.ts +26 -0
  171. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  172. package/types/lib/core/toolRegistry.d.ts +36 -0
  173. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  174. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  175. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  176. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  177. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  178. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  179. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  180. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  181. package/types/lib/ecosystems/golem.d.ts +81 -1
  182. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  183. package/types/lib/ecosystems/gradleutils.d.ts +57 -21
  184. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
  187. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  188. package/types/lib/ecosystems/parsers-go.d.ts +85 -13
  189. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  191. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +14 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  199. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  200. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  201. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  202. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  203. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  205. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  206. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  207. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  208. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  209. package/types/lib/evinser/evinser.d.ts +88 -8
  210. package/types/lib/evinser/evinser.d.ts.map +1 -1
  211. package/types/lib/evinser/swiftsem.d.ts +10 -0
  212. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  213. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  214. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  215. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  216. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  217. package/types/lib/inventory/atomUtils.d.ts +33 -0
  218. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  219. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  220. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  221. package/types/lib/inventory/cacheDir.d.ts +12 -4
  222. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  223. package/types/lib/inventory/cbomutils.d.ts +17 -0
  224. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  225. package/types/lib/inventory/certificates.d.ts +205 -0
  226. package/types/lib/inventory/certificates.d.ts.map +1 -0
  227. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  228. package/types/lib/inventory/deps.d.ts +2 -1
  229. package/types/lib/inventory/deps.d.ts.map +1 -1
  230. package/types/lib/inventory/display.d.ts +18 -0
  231. package/types/lib/inventory/display.d.ts.map +1 -1
  232. package/types/lib/inventory/dosai.d.ts +162 -7
  233. package/types/lib/inventory/dosai.d.ts.map +1 -1
  234. package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
  235. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  236. package/types/lib/inventory/dynamic.d.ts +6 -1
  237. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  238. package/types/lib/inventory/envcontext.d.ts +92 -8
  239. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  240. package/types/lib/inventory/evidenceUtils.d.ts +29 -10
  241. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  242. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  243. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  244. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  245. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  246. package/types/lib/inventory/hbom.d.ts.map +1 -1
  247. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  248. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  249. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  250. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  251. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  252. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  253. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  254. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  255. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  256. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  257. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  258. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  259. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  260. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  261. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  262. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  263. package/types/lib/inventory/osPackageResolver.d.ts +44 -2
  264. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  265. package/types/lib/inventory/osinfo.d.ts +31 -6
  266. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  267. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  268. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  269. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  270. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  271. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  272. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  273. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  274. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  275. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  276. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  277. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  278. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  279. package/types/lib/inventory/source.d.ts +38 -0
  280. package/types/lib/inventory/source.d.ts.map +1 -1
  281. package/types/lib/inventory/spdx.d.ts +15 -0
  282. package/types/lib/inventory/spdx.d.ts.map +1 -1
  283. package/types/lib/inventory/traceRunner.d.ts +4 -1
  284. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  285. package/types/lib/inventory/tracedCrypto.d.ts +22 -0
  286. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
  287. package/types/lib/managers/binary.d.ts +10 -0
  288. package/types/lib/managers/binary.d.ts.map +1 -1
  289. package/types/lib/managers/docker.d.ts +24 -0
  290. package/types/lib/managers/docker.d.ts.map +1 -1
  291. package/types/lib/parsers/email.d.ts +19 -0
  292. package/types/lib/parsers/email.d.ts.map +1 -0
  293. package/types/lib/parsers/largeJson.d.ts +17 -3
  294. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  295. package/types/lib/server/server.d.ts +8 -0
  296. package/types/lib/server/server.d.ts.map +1 -1
  297. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  298. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  299. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  300. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  301. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  302. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  303. package/types/lib/stages/pregen/pregen.d.ts +26 -9
  304. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -0,0 +1,301 @@
1
+ // Bounded, read-only inventory of the gems installed on a live host, for
2
+ // `-t os` / obom. osquery has no table for RubyGems, so without this an OBOM
3
+ // lists no gems at all: neither the default gems that ship with each Ruby
4
+ // (resolv, net-http, uri, ...), nor what `gem install` and the distro's
5
+ // ruby-* packages put in the system gem homes, nor the gems of the version
6
+ // manager installs.
7
+ //
8
+ // Gem homes are found in fixed places, one wildcard level at a time: a
9
+ // directory holding a `specifications` directory is a gem home. Only the
10
+ // `*.gemspec` files under specifications/ and specifications/default/ are
11
+ // read, statically; nothing is executed. A gemspec a package database lists is
12
+ // marked with `cdx:os:installedBy`, so a consumer judges it by that package,
13
+ // which may carry backported fixes under the same gem version.
14
+
15
+ import { readdirSync, statSync } from "node:fs";
16
+ import { join, posix, relative, sep, win32 } from "node:path";
17
+ import process from "node:process";
18
+
19
+ import { isDryRun, recordActivity } from "../core/activity.js";
20
+ import { readRegularTextFile } from "../core/boundedFile.js";
21
+ import { safeExistsSync } from "../core/fs.js";
22
+ import { thoughtLog } from "../core/logger.js";
23
+ import { findHostPathOwners } from "../inventory/hostRuntimeProbe.js";
24
+ import {
25
+ expandDirectories,
26
+ homeDirectories,
27
+ windowsRubyRoots,
28
+ } from "../inventory/rubyHostProbe.js";
29
+ import { parseGemspecData } from "./rubyutils.js";
30
+
31
+ const MAX_GEM_HOMES = 128;
32
+ const MAX_GEMSPECS = 8192;
33
+ const MAX_GEMSPEC_BYTES = 1024 * 1024;
34
+
35
+ // Gem homes relative to the root.
36
+ const ROOT_GEM_HOMES = {
37
+ linux: [
38
+ "usr/lib/ruby/gems/*",
39
+ "usr/lib64/ruby/gems/*",
40
+ "usr/share/gems",
41
+ // Debian and Ubuntu ruby-* packages register their gems here
42
+ "usr/share/rubygems-integration/*",
43
+ "usr/lib/*/rubygems-integration/*",
44
+ "usr/local/share/gems",
45
+ "var/lib/gems/*",
46
+ "usr/local/lib/ruby/gems/*",
47
+ "usr/local/bundle",
48
+ "opt/*/lib/ruby/gems/*",
49
+ "opt/rubies/*/lib/ruby/gems/*",
50
+ "opt/hostedtoolcache/Ruby/*/*/lib/ruby/gems/*",
51
+ "usr/local/rbenv/versions/*/lib/ruby/gems/*",
52
+ "usr/local/rvm/rubies/*/lib/ruby/gems/*",
53
+ "usr/local/rvm/gems/*",
54
+ ],
55
+ darwin: [
56
+ "Library/Ruby/Gems/*",
57
+ "usr/local/lib/ruby/gems/*",
58
+ "opt/homebrew/lib/ruby/gems/*",
59
+ "opt/*/lib/ruby/gems/*",
60
+ "opt/rubies/*/lib/ruby/gems/*",
61
+ ],
62
+ };
63
+
64
+ // Gem homes relative to a home directory.
65
+ const USER_GEM_HOMES = [
66
+ ".gem/ruby/*",
67
+ ".local/share/gem/ruby/*",
68
+ ".rbenv/versions/*/lib/ruby/gems/*",
69
+ ".rvm/rubies/*/lib/ruby/gems/*",
70
+ ".rvm/gems/*",
71
+ ".asdf/installs/ruby/*/lib/ruby/gems/*",
72
+ ".local/share/mise/installs/ruby/*/lib/ruby/gems/*",
73
+ ".rubies/*/lib/ruby/gems/*",
74
+ ".frum/versions/*/lib/ruby/gems/*",
75
+ ".local/share/rv/rubies/*/lib/ruby/gems/*",
76
+ ];
77
+
78
+ function listGemspecs(directory) {
79
+ try {
80
+ return readdirSync(directory)
81
+ .filter((name) => name.endsWith(".gemspec"))
82
+ .sort();
83
+ } catch {
84
+ return [];
85
+ }
86
+ }
87
+
88
+ function isDirectory(path) {
89
+ try {
90
+ return statSync(path).isDirectory();
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * The gem homes of a host, each a directory with a `specifications` directory.
98
+ *
99
+ * @param {string} rootPath root directory
100
+ * @param {string} platform `linux`, `darwin` or `win32`
101
+ * @returns {string[]} absolute gem home directories
102
+ */
103
+ export function findGemHomes(rootPath, platform) {
104
+ const context = { rootPath, platform };
105
+ const candidates = [];
106
+ if (platform === "win32") {
107
+ for (const root of windowsRubyRoots(context)) {
108
+ candidates.push(...expandDirectories(root, "lib/ruby/gems/*"));
109
+ }
110
+ } else {
111
+ for (const pattern of ROOT_GEM_HOMES[platform] || []) {
112
+ candidates.push(...expandDirectories(rootPath, pattern));
113
+ }
114
+ for (const home of homeDirectories(context)) {
115
+ for (const pattern of USER_GEM_HOMES) {
116
+ candidates.push(...expandDirectories(home, pattern));
117
+ }
118
+ }
119
+ }
120
+ const homes = [];
121
+ const seen = new Set();
122
+ for (const candidate of candidates) {
123
+ if (
124
+ seen.has(candidate) ||
125
+ !isDirectory(join(candidate, "specifications"))
126
+ ) {
127
+ continue;
128
+ }
129
+ seen.add(candidate);
130
+ homes.push(candidate);
131
+ if (homes.length >= MAX_GEM_HOMES) {
132
+ break;
133
+ }
134
+ }
135
+ return homes;
136
+ }
137
+
138
+ // The path as the host names it, for the package databases and evidence.
139
+ function logicalPath(rootPath, platform, absolutePath) {
140
+ const parts = relative(rootPath, absolutePath).split(sep);
141
+ if (platform === "win32") {
142
+ return win32.join(rootPath, ...parts);
143
+ }
144
+ return posix.join("/", ...parts);
145
+ }
146
+
147
+ /**
148
+ * Inventory the gems of a host's gem homes. One component per distinct gem
149
+ * and version, with an occurrence for each gemspec it was read from, so no
150
+ * host path reaches a purl.
151
+ *
152
+ * @param {Object} [options] options
153
+ * @param {string} [options.rootPath="/"] root directory; tests point it at a built tree
154
+ * @param {string} [options.platform] `linux`, `darwin` or `win32`
155
+ * @param {function(string): (Object[]|undefined)} [options.executeOsQuery] runs
156
+ * an osquery query, for rpm ownership
157
+ * @param {Object[]} [options.packageComponents] the OS packages the scan has
158
+ * inventoried, which name the owners
159
+ * @param {Object} [options.packageDatabases] where the package databases are
160
+ * @returns {Promise<Object[]>} gem components
161
+ */
162
+ export async function probeHostRubyGems(options = {}) {
163
+ const platform = options.platform || process.platform;
164
+ const rootPath =
165
+ options.rootPath ||
166
+ (platform === "win32" ? `${process.env.SystemDrive || "C:"}\\` : "/");
167
+ if (isDryRun) {
168
+ recordActivity({
169
+ capability: "ruby-gem-probe",
170
+ kind: "read",
171
+ reason:
172
+ "Dry run mode blocks the RubyGems probe, which reads gem specifications outside the project.",
173
+ status: "blocked",
174
+ target: rootPath,
175
+ });
176
+ return [];
177
+ }
178
+ if (!safeExistsSync(rootPath)) {
179
+ return [];
180
+ }
181
+ const homes = findGemHomes(rootPath, platform);
182
+ const gemspecs = [];
183
+ for (const home of homes) {
184
+ for (const subdir of [
185
+ "specifications",
186
+ join("specifications", "default"),
187
+ ]) {
188
+ const directory = join(home, subdir);
189
+ for (const name of listGemspecs(directory)) {
190
+ if (gemspecs.length >= MAX_GEMSPECS) {
191
+ break;
192
+ }
193
+ const absolutePath = join(directory, name);
194
+ gemspecs.push({
195
+ absolutePath,
196
+ logical: logicalPath(rootPath, platform, absolutePath),
197
+ });
198
+ }
199
+ }
200
+ }
201
+ if (!gemspecs.length) {
202
+ return [];
203
+ }
204
+ const { owners, unreadable } = findHostPathOwners(
205
+ { ...options, platform, rootPath },
206
+ gemspecs.map((gemspec) => gemspec.logical),
207
+ );
208
+ // An OBOM names a package once; its purl says how the distro named it.
209
+ const packagePurls = new Map();
210
+ for (const component of options.packageComponents || []) {
211
+ if (
212
+ component?.name &&
213
+ typeof component.purl === "string" &&
214
+ /^pkg:(deb|rpm|apk)\//.test(component.purl) &&
215
+ !packagePurls.has(component.name)
216
+ ) {
217
+ packagePurls.set(component.name, decodeURIComponent(component.purl));
218
+ }
219
+ }
220
+ const byRef = new Map();
221
+ for (const gemspec of gemspecs) {
222
+ // Only a regular file under the root, within the cap: any user can leave
223
+ // `~/.gem/ruby/*/specifications/x.gemspec -> /dev/zero` for a root scan.
224
+ const text = readRegularTextFile(
225
+ gemspec.absolutePath,
226
+ MAX_GEMSPEC_BYTES,
227
+ rootPath,
228
+ );
229
+ if (typeof text !== "string") {
230
+ continue;
231
+ }
232
+ // The gemspec files RubyGems writes on install are Ruby source, read here
233
+ // without evaluating them. Thousands of them are no reason to ask the
234
+ // registry about each.
235
+ const [pkg] = await parseGemspecData(text, gemspec.logical, {
236
+ noFetch: true,
237
+ });
238
+ if (!pkg?.name || !pkg.version || !pkg["bom-ref"]) {
239
+ continue;
240
+ }
241
+ const owner = owners.get(gemspec.logical);
242
+ // cdx:os:installedBy holds a purl, as everywhere else; an owner the
243
+ // OBOM did not list cannot be named by one.
244
+ const ownerRef = owner ? packagePurls.get(owner.packageName) : undefined;
245
+ let component = byRef.get(pkg["bom-ref"]);
246
+ if (!component) {
247
+ component = pkg;
248
+ component.evidence = {
249
+ ...(component.evidence || {}),
250
+ occurrences: [],
251
+ };
252
+ byRef.set(pkg["bom-ref"], component);
253
+ }
254
+ component.properties = component.properties || [];
255
+ if (
256
+ ownerRef &&
257
+ !component.properties.some(
258
+ (prop) => prop.name === "cdx:os:installedBy" && prop.value === ownerRef,
259
+ )
260
+ ) {
261
+ component.properties.push({
262
+ name: "cdx:os:installedBy",
263
+ value: ownerRef,
264
+ });
265
+ }
266
+ if (
267
+ !component.evidence.occurrences.some(
268
+ (occurrence) => occurrence.location === gemspec.logical,
269
+ )
270
+ ) {
271
+ component.evidence.occurrences.push({ location: gemspec.logical });
272
+ }
273
+ }
274
+ // A gem a package owns in one gem home and `gem install` put in another is
275
+ // not wholly that package's, so it keeps no owner.
276
+ for (const component of byRef.values()) {
277
+ const owned = component.evidence.occurrences.every((occurrence) =>
278
+ owners.has(occurrence.location),
279
+ );
280
+ if (!owned) {
281
+ component.properties = component.properties.filter(
282
+ (prop) => prop.name !== "cdx:os:installedBy",
283
+ );
284
+ }
285
+ }
286
+ recordActivity({
287
+ capability: "ruby-gem-probe",
288
+ kind: "read",
289
+ reason: `Read ${gemspecs.length} gemspec(s) from ${homes.length} gem home(s) in the fixed places${unreadable.length ? `; the ${unreadable.join(", ")} database could not be read in full, so gem ownership is incomplete` : ""}.`,
290
+ status: "completed",
291
+ target: rootPath,
292
+ });
293
+ if (byRef.size) {
294
+ thoughtLog(
295
+ `I found ${byRef.size} gem(s) in ${homes.length} gem home(s) on this host.`,
296
+ );
297
+ }
298
+ return Array.from(byRef.values()).sort((a, b) =>
299
+ a["bom-ref"] < b["bom-ref"] ? -1 : a["bom-ref"] > b["bom-ref"] ? 1 : 0,
300
+ );
301
+ }