@cdxgen/cdxgen 13.3.0 → 13.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +9 -6
  2. package/bin/cdxgen.js +90 -26
  3. package/bin/evinse.js +18 -1
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +45 -15
  6. package/bin/tracebom.js +5 -1
  7. package/bin/validate.js +84 -18
  8. package/bin/verify.js +155 -86
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +61 -0
  11. package/data/rules/dependency-sources.yaml +67 -0
  12. package/data/rules/scala-evidence.yaml +115 -0
  13. package/lib/audit/index.js +38 -6
  14. package/lib/cli/index.js +113 -54
  15. package/lib/cli/jsBom.js +50 -29
  16. package/lib/cli/jvmBom.js +665 -138
  17. package/lib/cli/managedBom.js +320 -200
  18. package/lib/cli/nativeBom.js +139 -30
  19. package/lib/core/activity.js +12 -0
  20. package/lib/core/env.js +17 -9
  21. package/lib/core/fs.js +3 -2
  22. package/lib/core/httpClient.js +159 -24
  23. package/lib/core/pluginRun.js +184 -0
  24. package/lib/core/processTree.js +100 -0
  25. package/lib/core/runState.js +39 -0
  26. package/lib/core/toml.js +72 -0
  27. package/lib/ecosystems/asarutils.js +4 -2
  28. package/lib/ecosystems/bunutils.js +5 -7
  29. package/lib/ecosystems/cargoCache.js +231 -8
  30. package/lib/ecosystems/cmakeResolver.js +10 -21
  31. package/lib/ecosystems/cppBuildContext.js +631 -0
  32. package/lib/ecosystems/cppEvidence.js +337 -22
  33. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  34. package/lib/ecosystems/denoutils.js +93 -66
  35. package/lib/ecosystems/ecosystems.js +1967 -788
  36. package/lib/ecosystems/golem.js +28 -12
  37. package/lib/ecosystems/gradleutils.js +314 -30
  38. package/lib/ecosystems/jsEvidence.js +85 -24
  39. package/lib/ecosystems/kosi.js +21 -4
  40. package/lib/ecosystems/npmutils.js +64 -0
  41. package/lib/ecosystems/parsers-dotnet.js +567 -151
  42. package/lib/ecosystems/parsers-elm.js +46 -28
  43. package/lib/ecosystems/parsers-gleam.js +3 -4
  44. package/lib/ecosystems/parsers-go.js +324 -61
  45. package/lib/ecosystems/parsers-js.js +92 -26
  46. package/lib/ecosystems/parsers-julia.js +2 -3
  47. package/lib/ecosystems/parsers-jvm.js +204 -12
  48. package/lib/ecosystems/parsers-misc.js +182 -55
  49. package/lib/ecosystems/parsers-python.js +141 -10
  50. package/lib/ecosystems/parsers-rust.js +4 -4
  51. package/lib/ecosystems/parsers-terraform.js +1573 -19
  52. package/lib/ecosystems/pythonutils.js +102 -6
  53. package/lib/ecosystems/remote/huggingface.js +3 -0
  54. package/lib/ecosystems/rubyutils.js +67 -17
  55. package/lib/ecosystems/rusi.js +22 -9
  56. package/lib/ecosystems/sbtutils.js +243 -187
  57. package/lib/ecosystems/scalacliutils.js +342 -0
  58. package/lib/ecosystems/scalasem.js +2618 -0
  59. package/lib/ecosystems/terraformRegistry.js +288 -0
  60. package/lib/ecosystems/utils.js +2 -0
  61. package/lib/ecosystems/vendoredCode.js +156 -0
  62. package/lib/evinser/evinser.js +578 -69
  63. package/lib/helpers/agenticToolutils.js +2 -2
  64. package/lib/helpers/bomSigner.js +576 -128
  65. package/lib/inventory/aiCollector.js +4 -1
  66. package/lib/inventory/analyzer.js +110 -127
  67. package/lib/inventory/atomUtils.js +171 -48
  68. package/lib/inventory/auditCategories.js +7 -0
  69. package/lib/inventory/buildToolRateLimit.js +144 -0
  70. package/lib/inventory/cacheCommands.js +2 -1
  71. package/lib/inventory/cmakeBuildDirs.js +254 -0
  72. package/lib/inventory/deps.js +501 -124
  73. package/lib/inventory/dosai.js +248 -44
  74. package/lib/inventory/dosaiParsers.js +310 -15
  75. package/lib/inventory/dynamic.js +52 -6
  76. package/lib/inventory/evidenceUtils.js +154 -109
  77. package/lib/inventory/fetchBatch.js +574 -38
  78. package/lib/inventory/fetchDiskCache.js +597 -0
  79. package/lib/inventory/forgeEnricher.js +14 -5
  80. package/lib/inventory/jvmLocalRepos.js +917 -0
  81. package/lib/inventory/mcpConfigParser.js +2 -3
  82. package/lib/inventory/parentSelection.js +146 -0
  83. package/lib/inventory/rustFormulationParser.js +3 -4
  84. package/lib/inventory/scalaCoords.js +153 -0
  85. package/lib/inventory/source.js +8 -8
  86. package/lib/inventory/spdx.js +101 -0
  87. package/lib/inventory/traceRunner.js +88 -7
  88. package/lib/inventory/tracedCrypto.js +294 -0
  89. package/lib/managers/docker.js +33 -32
  90. package/lib/managers/oci.js +24 -2
  91. package/lib/parsers/cmakeCache.js +69 -0
  92. package/lib/parsers/cmakeLists.js +16 -2
  93. package/lib/parsers/cmakePresets.js +400 -0
  94. package/lib/parsers/compileCommands.js +557 -0
  95. package/lib/parsers/strictJson.js +322 -0
  96. package/lib/parsers/terraformConfig.js +1073 -0
  97. package/lib/parsers/terraformSource.js +652 -0
  98. package/lib/server/server.js +23 -0
  99. package/lib/stages/postgen/introspection/markers.js +1 -1
  100. package/lib/stages/postgen/postgen.js +15 -1
  101. package/lib/stages/postgen/specVersionCompat.js +32 -0
  102. package/lib/validator/index.js +25 -10
  103. package/lib/validator/reporters/json.js +1 -0
  104. package/package.json +21 -21
  105. package/types/lib/audit/index.d.ts.map +1 -1
  106. package/types/lib/cli/index.d.ts.map +1 -1
  107. package/types/lib/cli/jsBom.d.ts.map +1 -1
  108. package/types/lib/cli/jvmBom.d.ts +24 -1
  109. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  110. package/types/lib/cli/managedBom.d.ts +7 -4
  111. package/types/lib/cli/managedBom.d.ts.map +1 -1
  112. package/types/lib/cli/nativeBom.d.ts +3 -1
  113. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  114. package/types/lib/core/activity.d.ts.map +1 -1
  115. package/types/lib/core/env.d.ts +9 -0
  116. package/types/lib/core/env.d.ts.map +1 -1
  117. package/types/lib/core/fs.d.ts.map +1 -1
  118. package/types/lib/core/httpClient.d.ts +13 -8
  119. package/types/lib/core/httpClient.d.ts.map +1 -1
  120. package/types/lib/core/pluginRun.d.ts +97 -0
  121. package/types/lib/core/pluginRun.d.ts.map +1 -0
  122. package/types/lib/core/processTree.d.ts +36 -0
  123. package/types/lib/core/processTree.d.ts.map +1 -0
  124. package/types/lib/core/runState.d.ts +29 -0
  125. package/types/lib/core/runState.d.ts.map +1 -0
  126. package/types/lib/core/toml.d.ts +20 -0
  127. package/types/lib/core/toml.d.ts.map +1 -0
  128. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  129. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  130. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  131. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  132. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  133. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  134. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  135. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  136. package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
  137. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  138. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  139. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  140. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  141. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  142. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  143. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  144. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  145. package/types/lib/ecosystems/gradleutils.d.ts +50 -0
  146. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  147. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  148. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  149. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  150. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  151. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  152. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  153. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  154. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  155. package/types/lib/ecosystems/parsers-go.d.ts +44 -2
  156. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  157. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  158. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  159. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  160. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  161. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  162. package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
  163. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  164. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  165. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  166. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  167. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  168. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  169. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  170. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  171. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  172. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  173. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  174. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  175. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  176. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  177. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  178. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  179. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  180. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  181. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  182. package/types/lib/ecosystems/utils.d.ts +2 -2
  183. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  184. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  185. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  186. package/types/lib/evinser/evinser.d.ts +75 -8
  187. package/types/lib/evinser/evinser.d.ts.map +1 -1
  188. package/types/lib/helpers/bomSigner.d.ts +80 -4
  189. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  190. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  191. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  192. package/types/lib/inventory/atomUtils.d.ts +82 -10
  193. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  194. package/types/lib/inventory/auditCategories.d.ts +3 -0
  195. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  196. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  197. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  198. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  199. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  200. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  201. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  202. package/types/lib/inventory/deps.d.ts +59 -1
  203. package/types/lib/inventory/deps.d.ts.map +1 -1
  204. package/types/lib/inventory/dosai.d.ts +57 -8
  205. package/types/lib/inventory/dosai.d.ts.map +1 -1
  206. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  207. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  208. package/types/lib/inventory/dynamic.d.ts +10 -0
  209. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  210. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  211. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  212. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  213. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  214. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  215. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  216. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  217. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  218. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  219. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  220. package/types/lib/inventory/parentSelection.d.ts +34 -0
  221. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  222. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  223. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  224. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  225. package/types/lib/inventory/source.d.ts.map +1 -1
  226. package/types/lib/inventory/spdx.d.ts +9 -0
  227. package/types/lib/inventory/spdx.d.ts.map +1 -1
  228. package/types/lib/inventory/traceRunner.d.ts +12 -0
  229. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  230. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  231. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  232. package/types/lib/managers/docker.d.ts.map +1 -1
  233. package/types/lib/managers/oci.d.ts +3 -1
  234. package/types/lib/managers/oci.d.ts.map +1 -1
  235. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  236. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  237. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  238. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  239. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  240. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  241. package/types/lib/parsers/compileCommands.d.ts +115 -0
  242. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  243. package/types/lib/parsers/strictJson.d.ts +46 -0
  244. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  245. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  246. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  247. package/types/lib/parsers/terraformSource.d.ts +66 -0
  248. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  249. package/types/lib/server/server.d.ts.map +1 -1
  250. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  251. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  252. package/types/lib/validator/index.d.ts +8 -0
  253. package/types/lib/validator/index.d.ts.map +1 -1
  254. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  255. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -0,0 +1,557 @@
1
+ /**
2
+ * Pure reader for JSON compilation databases (`compile_commands.json`).
3
+ *
4
+ * Each entry names a translation unit (`file`), the directory its command ran
5
+ * in (`directory`) and the command, either as an argument list (`arguments`)
6
+ * or as one command line (`command`) split with the quoting rules of the
7
+ * shell it was written for. From the entries this module derives the
8
+ * compilers a build uses, the include directories each unit is compiled
9
+ * with, and the security-hardening options in effect.
10
+ *
11
+ * Layer 1: data in, data out. Locating and reading the file, and running a
12
+ * compiler to learn its version, belong to the callers.
13
+ */
14
+
15
+ import { basename, isAbsolute, join, normalize } from "node:path";
16
+
17
+ /** Launchers that run the compiler named by the next argument. */
18
+ const COMPILER_LAUNCHERS = new Set([
19
+ "ccache",
20
+ "sccache",
21
+ "distcc",
22
+ "icecc",
23
+ "buildcache",
24
+ "env",
25
+ ]);
26
+
27
+ /**
28
+ * Split a POSIX shell command line into words: single quotes keep everything
29
+ * literally, double quotes keep everything except `\"`, `\\`, `\$` and
30
+ * `` \` ``, and a backslash outside quotes escapes the next character.
31
+ *
32
+ * @param {string} command Command line
33
+ * @returns {string[]} Words
34
+ */
35
+ export function splitPosixCommand(command) {
36
+ const words = [];
37
+ let current = "";
38
+ let inWord = false;
39
+ let quote = "";
40
+ for (let i = 0; i < command.length; i++) {
41
+ const c = command[i];
42
+ if (quote === "'") {
43
+ if (c === "'") {
44
+ quote = "";
45
+ } else {
46
+ current += c;
47
+ }
48
+ continue;
49
+ }
50
+ if (quote === '"') {
51
+ if (c === '"') {
52
+ quote = "";
53
+ } else if (
54
+ c === "\\" &&
55
+ i + 1 < command.length &&
56
+ '"\\$`'.includes(command[i + 1])
57
+ ) {
58
+ current += command[++i];
59
+ } else {
60
+ current += c;
61
+ }
62
+ continue;
63
+ }
64
+ if (c === "'" || c === '"') {
65
+ quote = c;
66
+ inWord = true;
67
+ } else if (c === "\\" && i + 1 < command.length) {
68
+ current += command[++i];
69
+ inWord = true;
70
+ } else if (c === " " || c === "\t" || c === "\n" || c === "\r") {
71
+ if (inWord) {
72
+ words.push(current);
73
+ current = "";
74
+ inWord = false;
75
+ }
76
+ } else {
77
+ current += c;
78
+ inWord = true;
79
+ }
80
+ }
81
+ if (inWord) {
82
+ words.push(current);
83
+ }
84
+ return words;
85
+ }
86
+
87
+ /**
88
+ * Split a Windows command line into words as the Microsoft C runtime does:
89
+ * double quotes group, `\"` is a literal quote, and backslashes are literal
90
+ * unless they precede a quote.
91
+ *
92
+ * @param {string} command Command line
93
+ * @returns {string[]} Words
94
+ */
95
+ export function splitWindowsCommand(command) {
96
+ const words = [];
97
+ let current = "";
98
+ let inWord = false;
99
+ let inQuotes = false;
100
+ for (let i = 0; i < command.length; i++) {
101
+ const c = command[i];
102
+ if (c === "\\") {
103
+ let slashes = 0;
104
+ while (i < command.length && command[i] === "\\") {
105
+ slashes++;
106
+ i++;
107
+ }
108
+ if (i < command.length && command[i] === '"') {
109
+ current += "\\".repeat(Math.floor(slashes / 2));
110
+ if (slashes % 2 === 1) {
111
+ current += '"';
112
+ } else {
113
+ inQuotes = !inQuotes;
114
+ }
115
+ } else {
116
+ current += "\\".repeat(slashes);
117
+ i--;
118
+ }
119
+ inWord = true;
120
+ } else if (c === '"') {
121
+ inQuotes = !inQuotes;
122
+ inWord = true;
123
+ } else if ((c === " " || c === "\t") && !inQuotes) {
124
+ if (inWord) {
125
+ words.push(current);
126
+ current = "";
127
+ inWord = false;
128
+ }
129
+ } else {
130
+ current += c;
131
+ inWord = true;
132
+ }
133
+ }
134
+ if (inWord) {
135
+ words.push(current);
136
+ }
137
+ return words;
138
+ }
139
+
140
+ /** The file name of a driver path, lower case, without `.exe`. */
141
+ function driverName(driver) {
142
+ const name = basename(`${driver}`.replaceAll("\\", "/")).toLowerCase();
143
+ return name.endsWith(".exe") ? name.slice(0, -4) : name;
144
+ }
145
+
146
+ /**
147
+ * The compiler family a driver's name implies.
148
+ *
149
+ * @param {string} driver Driver path or name
150
+ * @returns {string} `msvc`, `clang-cl`, `clang`, `gcc`, `nvcc`, `icx`,
151
+ * `icc`, `nvhpc`, `edg` or `unknown`
152
+ */
153
+ export function compilerFamilyOfName(driver) {
154
+ const name = driverName(driver);
155
+ if (name === "cl") {
156
+ return "msvc";
157
+ }
158
+ if (name === "clang-cl" || name.endsWith("-clang-cl")) {
159
+ return "clang-cl";
160
+ }
161
+ if (name === "nvcc") {
162
+ return "nvcc";
163
+ }
164
+ if (["icx", "icpx", "icx-cl", "dpcpp"].includes(name)) {
165
+ return "icx";
166
+ }
167
+ if (["icc", "icpc", "icl"].includes(name)) {
168
+ return "icc";
169
+ }
170
+ if (["nvc", "nvc++", "pgcc", "pgc++"].includes(name)) {
171
+ return "nvhpc";
172
+ }
173
+ if (["cpfe", "eccp", "edgcpfe"].includes(name)) {
174
+ return "edg";
175
+ }
176
+ if (name.includes("clang")) {
177
+ return "clang";
178
+ }
179
+ if (
180
+ name.includes("gcc") ||
181
+ name.includes("g++") ||
182
+ name === "cc" ||
183
+ name === "c++" ||
184
+ name.endsWith("-cc") ||
185
+ name.endsWith("-c++")
186
+ ) {
187
+ return "gcc";
188
+ }
189
+ return "unknown";
190
+ }
191
+
192
+ /** Families whose drivers take MSVC-style options. */
193
+ const MSVC_STYLE = new Set(["msvc", "clang-cl"]);
194
+
195
+ /**
196
+ * The argument list of a database entry, with any compiler launcher
197
+ * (`ccache`, `sccache`, ...) removed so the first argument is the compiler.
198
+ *
199
+ * @param {Object} entry Database entry
200
+ * @returns {string[]} Arguments
201
+ */
202
+ export function entryArguments(entry) {
203
+ let args;
204
+ if (Array.isArray(entry?.arguments)) {
205
+ args = entry.arguments.filter((a) => typeof a === "string");
206
+ } else if (typeof entry?.command === "string") {
207
+ const firstWord = entry.command.trimStart().split(/\s/, 1)[0] || "";
208
+ const family = compilerFamilyOfName(firstWord.replaceAll('"', ""));
209
+ args = MSVC_STYLE.has(family)
210
+ ? splitWindowsCommand(entry.command)
211
+ : splitPosixCommand(entry.command);
212
+ } else {
213
+ return [];
214
+ }
215
+ let start = 0;
216
+ while (
217
+ start < args.length - 1 &&
218
+ COMPILER_LAUNCHERS.has(driverName(args[start]))
219
+ ) {
220
+ start++;
221
+ // `env NAME=value cc ...`
222
+ while (
223
+ start < args.length - 1 &&
224
+ /^[A-Za-z_][A-Za-z0-9_]*=/.test(args[start])
225
+ ) {
226
+ start++;
227
+ }
228
+ }
229
+ return args.slice(start);
230
+ }
231
+
232
+ /**
233
+ * The language a unit is compiled as: from `-x`/`/TP`/`/TC`, the driver
234
+ * name, then the file extension.
235
+ *
236
+ * @param {string[]} args Arguments, compiler first
237
+ * @param {string} file Translation unit
238
+ * @returns {string} `c`, `c++`, `cuda` or `unknown`
239
+ */
240
+ export function unitLanguage(args, file) {
241
+ for (let i = 1; i < args.length; i++) {
242
+ const a = args[i];
243
+ const value =
244
+ a === "-x" ? args[i + 1] : a.startsWith("-x") ? a.slice(2) : undefined;
245
+ if (value) {
246
+ if (value.startsWith("c++")) {
247
+ return "c++";
248
+ }
249
+ if (value === "c" || value.startsWith("c-")) {
250
+ return "c";
251
+ }
252
+ if (value === "cuda") {
253
+ return "cuda";
254
+ }
255
+ }
256
+ if (a === "/TP" || a === "-TP") {
257
+ return "c++";
258
+ }
259
+ if (a === "/TC" || a === "-TC") {
260
+ return "c";
261
+ }
262
+ }
263
+ const name = driverName(args[0] || "");
264
+ if (name === "nvcc") {
265
+ return "cuda";
266
+ }
267
+ if (name.includes("++") || name.endsWith("cpp") || name === "icpc") {
268
+ return "c++";
269
+ }
270
+ const lower = `${file || ""}`.toLowerCase();
271
+ const dot = lower.lastIndexOf(".");
272
+ const ext = dot >= 0 ? lower.slice(dot) : "";
273
+ if (ext === ".c") {
274
+ return `${file}`.endsWith(".C") ? "c++" : "c";
275
+ }
276
+ if (ext === ".cu") {
277
+ return "cuda";
278
+ }
279
+ if ([".cc", ".cpp", ".cxx", ".c++", ".cppm", ".ixx", ".mm"].includes(ext)) {
280
+ return "c++";
281
+ }
282
+ return "unknown";
283
+ }
284
+
285
+ /**
286
+ * The include directories a unit is compiled with, as absolute paths.
287
+ *
288
+ * @param {string[]} args Arguments, compiler first
289
+ * @param {string} directory The entry's working directory
290
+ * @returns {string[]} Directories, in command order
291
+ */
292
+ export function includeDirectories(args, directory) {
293
+ const msvc = MSVC_STYLE.has(compilerFamilyOfName(args[0] || ""));
294
+ const dirs = [];
295
+ const add = (d) => {
296
+ if (!d) {
297
+ return;
298
+ }
299
+ const unquoted = d.replaceAll('"', "");
300
+ dirs.push(
301
+ normalize(
302
+ isAbsolute(unquoted) || !directory
303
+ ? unquoted
304
+ : join(directory, unquoted),
305
+ ),
306
+ );
307
+ };
308
+ for (let i = 1; i < args.length; i++) {
309
+ const a = args[i];
310
+ for (const option of ["-iquote", "-isystem", "-idirafter", "-I"]) {
311
+ if (a === option) {
312
+ add(args[++i]);
313
+ break;
314
+ }
315
+ if (a.startsWith(option)) {
316
+ add(a.slice(option.length).replace(/^=/, ""));
317
+ break;
318
+ }
319
+ }
320
+ if (msvc && (a.startsWith("/I") || a.startsWith("/external:I"))) {
321
+ const option = a.startsWith("/I") ? "/I" : "/external:I";
322
+ if (a === option) {
323
+ add(args[++i]);
324
+ } else {
325
+ add(a.slice(option.length));
326
+ }
327
+ }
328
+ }
329
+ return dirs;
330
+ }
331
+
332
+ /**
333
+ * The security-hardening settings an argument list turns on or off. Later
334
+ * options override earlier ones, as they do for the compiler.
335
+ *
336
+ * @param {string[]} args Compiler or linker arguments
337
+ * @returns {Map<string, string>} Setting name to value:
338
+ * `fortifySource` (level), `stackProtector` (`on`, `strong`, `all`,
339
+ * `off`), `pie` (`on`/`off`), `relro` (`partial`/`full`, from `-z relro`
340
+ * and `-z now`), `cfProtection` (`full`, `branch`, `return`, `none`),
341
+ * `sanitizers` (comma-separated), `glibcxxAssertions` (`on`),
342
+ * `stackClashProtection` (`on`/`off`), `msvcBufferSecurityCheck` (`/GS`
343
+ * `on`/`off`), `msvcControlFlowGuard` (`on`/`off`)
344
+ */
345
+ export function hardeningSettings(args) {
346
+ const settings = new Map();
347
+ const sanitizers = new Set();
348
+ let relro = false;
349
+ let now = false;
350
+ const linkerWords = [];
351
+ for (let i = 0; i < args.length; i++) {
352
+ const a = args[i];
353
+ if (a.startsWith("-Wl,")) {
354
+ linkerWords.push(...a.slice(4).split(","));
355
+ continue;
356
+ }
357
+ if (a === "-z" && i + 1 < args.length) {
358
+ linkerWords.push("-z", args[++i]);
359
+ continue;
360
+ }
361
+ const define =
362
+ a === "-D" || a === "/D"
363
+ ? args[i + 1]
364
+ : /^[-/]D/.test(a)
365
+ ? a.slice(2)
366
+ : undefined;
367
+ if (define !== undefined) {
368
+ if (a === "-D" || a === "/D") {
369
+ i++;
370
+ }
371
+ const [name, value] = define.split("=", 2);
372
+ if (name === "_FORTIFY_SOURCE") {
373
+ settings.set("fortifySource", value === undefined ? "1" : value);
374
+ } else if (name === "_GLIBCXX_ASSERTIONS") {
375
+ settings.set("glibcxxAssertions", "on");
376
+ }
377
+ continue;
378
+ }
379
+ if (/^[-/]U_FORTIFY_SOURCE$/.test(a)) {
380
+ settings.delete("fortifySource");
381
+ continue;
382
+ }
383
+ if (a === "-fstack-protector") {
384
+ settings.set("stackProtector", "on");
385
+ } else if (a === "-fstack-protector-strong") {
386
+ settings.set("stackProtector", "strong");
387
+ } else if (a === "-fstack-protector-all") {
388
+ settings.set("stackProtector", "all");
389
+ } else if (a === "-fno-stack-protector") {
390
+ settings.set("stackProtector", "off");
391
+ } else if (a === "-fPIE" || a === "-fpie" || a === "-pie") {
392
+ settings.set("pie", "on");
393
+ } else if (a === "-fno-pie" || a === "-fno-PIE" || a === "-no-pie") {
394
+ settings.set("pie", "off");
395
+ } else if (a === "-fcf-protection") {
396
+ settings.set("cfProtection", "full");
397
+ } else if (a.startsWith("-fcf-protection=")) {
398
+ settings.set("cfProtection", a.slice("-fcf-protection=".length));
399
+ } else if (a.startsWith("-fsanitize=")) {
400
+ for (const s of a.slice("-fsanitize=".length).split(",")) {
401
+ if (s) {
402
+ sanitizers.add(s);
403
+ }
404
+ }
405
+ } else if (a.startsWith("-fno-sanitize=")) {
406
+ for (const s of a.slice("-fno-sanitize=".length).split(",")) {
407
+ sanitizers.delete(s);
408
+ }
409
+ } else if (a === "-fstack-clash-protection") {
410
+ settings.set("stackClashProtection", "on");
411
+ } else if (a === "-fno-stack-clash-protection") {
412
+ settings.set("stackClashProtection", "off");
413
+ } else if (a === "/GS" || a === "-GS") {
414
+ settings.set("msvcBufferSecurityCheck", "on");
415
+ } else if (a === "/GS-" || a === "-GS-") {
416
+ settings.set("msvcBufferSecurityCheck", "off");
417
+ } else if (a === "/guard:cf" || a === "-guard:cf") {
418
+ settings.set("msvcControlFlowGuard", "on");
419
+ } else if (a === "/guard:cf-" || a === "-guard:cf-") {
420
+ settings.set("msvcControlFlowGuard", "off");
421
+ }
422
+ }
423
+ for (let i = 0; i < linkerWords.length; i++) {
424
+ const w = linkerWords[i];
425
+ const z =
426
+ w === "-z"
427
+ ? linkerWords[++i]
428
+ : w.startsWith("-z")
429
+ ? w.slice(2)
430
+ : undefined;
431
+ if (z === "relro") {
432
+ relro = true;
433
+ } else if (z === "norelro") {
434
+ relro = false;
435
+ now = false;
436
+ } else if (z === "now") {
437
+ now = true;
438
+ } else if (w === "-pie" || w === "--pie") {
439
+ settings.set("pie", "on");
440
+ }
441
+ }
442
+ if (relro) {
443
+ settings.set("relro", now ? "full" : "partial");
444
+ }
445
+ if (sanitizers.size) {
446
+ settings.set("sanitizers", [...sanitizers].sort().join(","));
447
+ }
448
+ return settings;
449
+ }
450
+
451
+ /**
452
+ * Summarise a compilation database.
453
+ *
454
+ * @param {Object[]} entries Parsed database (a JSON array)
455
+ * @returns {{
456
+ * units: number,
457
+ * compilers: Map<string, {driver: string, family: string, units: number, languages: Set<string>}>,
458
+ * includeDirectories: Set<string>,
459
+ * hardening: Map<string, Map<string, number>>,
460
+ * }} `compilers` is keyed by the driver as written; `hardening` maps each
461
+ * setting to the number of units per value
462
+ */
463
+ export function summarizeCompileDatabase(entries) {
464
+ const compilers = new Map();
465
+ const dirs = new Set();
466
+ const hardening = new Map();
467
+ let units = 0;
468
+ for (const entry of Array.isArray(entries) ? entries : []) {
469
+ if (!entry || typeof entry !== "object") {
470
+ continue;
471
+ }
472
+ const args = entryArguments(entry);
473
+ if (!args.length) {
474
+ continue;
475
+ }
476
+ units++;
477
+ const driver = args[0];
478
+ const known = compilers.get(driver) || {
479
+ driver,
480
+ family: compilerFamilyOfName(driver),
481
+ units: 0,
482
+ languages: new Set(),
483
+ };
484
+ known.units++;
485
+ const language = unitLanguage(args, entry.file);
486
+ if (language !== "unknown") {
487
+ known.languages.add(language);
488
+ }
489
+ compilers.set(driver, known);
490
+ const directory =
491
+ typeof entry.directory === "string" ? entry.directory : "";
492
+ for (const d of includeDirectories(args, directory)) {
493
+ dirs.add(d);
494
+ }
495
+ for (const [setting, value] of hardeningSettings(args.slice(1))) {
496
+ const counts = hardening.get(setting) || new Map();
497
+ counts.set(value, (counts.get(value) || 0) + 1);
498
+ hardening.set(setting, counts);
499
+ }
500
+ }
501
+ return { units, compilers, includeDirectories: dirs, hardening };
502
+ }
503
+
504
+ /**
505
+ * Read a compiler's family and version from what it prints for `--version`
506
+ * (or, for MSVC, on start-up).
507
+ *
508
+ * @param {string} banner Output of the version query
509
+ * @param {string} [nameFamily] Family implied by the driver's name
510
+ * @returns {{family: string, version: string|undefined, edgFrontEnd: boolean}}
511
+ */
512
+ export function classifyCompilerBanner(banner, nameFamily = "unknown") {
513
+ const text = `${banner || ""}`;
514
+ const firstLine = text.split(/\r?\n/).find((l) => l.trim().length) || "";
515
+ const edgFrontEnd = text.includes("Edison Design Group");
516
+ let family = nameFamily;
517
+ if (text.includes("Microsoft (R) C/C++")) {
518
+ family = "msvc";
519
+ } else if (firstLine.startsWith("Apple clang")) {
520
+ family = "apple-clang";
521
+ } else if (firstLine.includes("clang version")) {
522
+ // clang-cl prints clang's banner
523
+ family = nameFamily === "clang-cl" ? "clang-cl" : "clang";
524
+ } else if (text.includes("Cuda compilation tools")) {
525
+ family = "nvcc";
526
+ } else if (text.includes("Intel(R) oneAPI")) {
527
+ family = "icx";
528
+ } else if (/^ic(c|pc) \(ICC\)/.test(firstLine)) {
529
+ family = "icc";
530
+ } else if (/^nvc\+?\+? /.test(firstLine) || firstLine.startsWith("pgc")) {
531
+ family = "nvhpc";
532
+ } else if (/\(GCC\)|^gcc |^g\+\+ |Free Software Foundation/.test(text)) {
533
+ family = "gcc";
534
+ } else if (edgFrontEnd) {
535
+ family = "edg";
536
+ }
537
+ return { family, version: versionOf(text, family), edgFrontEnd };
538
+ }
539
+
540
+ /** The first dotted version in the banner line that names the compiler. */
541
+ function versionOf(text, family) {
542
+ const lines = text.split(/\r?\n/);
543
+ const pick =
544
+ family === "nvcc"
545
+ ? lines.find((l) => l.includes("release")) || ""
546
+ : family === "msvc"
547
+ ? lines.find((l) => l.includes("Version")) || ""
548
+ : lines.find((l) => l.trim().length) || "";
549
+ const words = pick.replaceAll(",", " ").split(/\s+/);
550
+ for (const w of family === "gcc" ? [...words].reverse() : words) {
551
+ const v = w.replace(/^V/, "");
552
+ if (/^\d+(\.\d+)+/.test(v)) {
553
+ return v.match(/^\d+(\.\d+)+/)[0];
554
+ }
555
+ }
556
+ return undefined;
557
+ }