@quantakrypto/core 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/README.md +76 -27
  2. package/dist/cbom-merge.d.ts.map +1 -1
  3. package/dist/cbom-merge.js +30 -8
  4. package/dist/cbom-merge.js.map +1 -1
  5. package/dist/cbom.d.ts +5 -0
  6. package/dist/cbom.d.ts.map +1 -1
  7. package/dist/cbom.js +132 -37
  8. package/dist/cbom.js.map +1 -1
  9. package/dist/comments.d.ts +2 -23
  10. package/dist/comments.d.ts.map +1 -1
  11. package/dist/comments.js +24 -5
  12. package/dist/comments.js.map +1 -1
  13. package/dist/detect-utils.d.ts +55 -21
  14. package/dist/detect-utils.d.ts.map +1 -1
  15. package/dist/detect-utils.js +133 -7
  16. package/dist/detect-utils.js.map +1 -1
  17. package/dist/detectors/age.d.ts +16 -0
  18. package/dist/detectors/age.d.ts.map +1 -0
  19. package/dist/detectors/age.js +40 -0
  20. package/dist/detectors/age.js.map +1 -0
  21. package/dist/detectors/ansible.d.ts +13 -0
  22. package/dist/detectors/ansible.d.ts.map +1 -0
  23. package/dist/detectors/ansible.js +108 -0
  24. package/dist/detectors/ansible.js.map +1 -0
  25. package/dist/detectors/bicep.d.ts +20 -0
  26. package/dist/detectors/bicep.d.ts.map +1 -0
  27. package/dist/detectors/bicep.js +74 -0
  28. package/dist/detectors/bicep.js.map +1 -0
  29. package/dist/detectors/c.d.ts.map +1 -1
  30. package/dist/detectors/c.js +30 -3
  31. package/dist/detectors/c.js.map +1 -1
  32. package/dist/detectors/cicd.d.ts.map +1 -1
  33. package/dist/detectors/cicd.js +18 -5
  34. package/dist/detectors/cicd.js.map +1 -1
  35. package/dist/detectors/cloud-kms.d.ts +8 -5
  36. package/dist/detectors/cloud-kms.d.ts.map +1 -1
  37. package/dist/detectors/cloud-kms.js +62 -20
  38. package/dist/detectors/cloud-kms.js.map +1 -1
  39. package/dist/detectors/cloudformation.d.ts +8 -0
  40. package/dist/detectors/cloudformation.d.ts.map +1 -1
  41. package/dist/detectors/cloudformation.js +41 -10
  42. package/dist/detectors/cloudformation.js.map +1 -1
  43. package/dist/detectors/codesign.d.ts +50 -0
  44. package/dist/detectors/codesign.d.ts.map +1 -0
  45. package/dist/detectors/codesign.js +143 -0
  46. package/dist/detectors/codesign.js.map +1 -0
  47. package/dist/detectors/csharp.d.ts.map +1 -1
  48. package/dist/detectors/csharp.js +13 -3
  49. package/dist/detectors/csharp.js.map +1 -1
  50. package/dist/detectors/dart.d.ts +35 -0
  51. package/dist/detectors/dart.d.ts.map +1 -0
  52. package/dist/detectors/dart.js +177 -0
  53. package/dist/detectors/dart.js.map +1 -0
  54. package/dist/detectors/database.d.ts +5 -3
  55. package/dist/detectors/database.d.ts.map +1 -1
  56. package/dist/detectors/database.js +45 -8
  57. package/dist/detectors/database.js.map +1 -1
  58. package/dist/detectors/dkim.d.ts +54 -0
  59. package/dist/detectors/dkim.d.ts.map +1 -0
  60. package/dist/detectors/dkim.js +92 -0
  61. package/dist/detectors/dkim.js.map +1 -0
  62. package/dist/detectors/dnssec.d.ts.map +1 -1
  63. package/dist/detectors/dnssec.js +19 -10
  64. package/dist/detectors/dnssec.js.map +1 -1
  65. package/dist/detectors/elixir.d.ts.map +1 -1
  66. package/dist/detectors/elixir.js +34 -2
  67. package/dist/detectors/elixir.js.map +1 -1
  68. package/dist/detectors/java.d.ts.map +1 -1
  69. package/dist/detectors/java.js +33 -3
  70. package/dist/detectors/java.js.map +1 -1
  71. package/dist/detectors/jose.d.ts.map +1 -1
  72. package/dist/detectors/jose.js +13 -3
  73. package/dist/detectors/jose.js.map +1 -1
  74. package/dist/detectors/jwk.d.ts +7 -7
  75. package/dist/detectors/jwk.d.ts.map +1 -1
  76. package/dist/detectors/jwk.js +44 -8
  77. package/dist/detectors/jwk.js.map +1 -1
  78. package/dist/detectors/k8s.d.ts.map +1 -1
  79. package/dist/detectors/k8s.js +5 -2
  80. package/dist/detectors/k8s.js.map +1 -1
  81. package/dist/detectors/keystore.d.ts +19 -0
  82. package/dist/detectors/keystore.d.ts.map +1 -0
  83. package/dist/detectors/keystore.js +76 -0
  84. package/dist/detectors/keystore.js.map +1 -0
  85. package/dist/detectors/mesh.d.ts +7 -8
  86. package/dist/detectors/mesh.d.ts.map +1 -1
  87. package/dist/detectors/mesh.js +17 -35
  88. package/dist/detectors/mesh.js.map +1 -1
  89. package/dist/detectors/messaging.d.ts +2 -2
  90. package/dist/detectors/messaging.d.ts.map +1 -1
  91. package/dist/detectors/messaging.js +19 -12
  92. package/dist/detectors/messaging.js.map +1 -1
  93. package/dist/detectors/objc.d.ts +41 -0
  94. package/dist/detectors/objc.d.ts.map +1 -0
  95. package/dist/detectors/objc.js +146 -0
  96. package/dist/detectors/objc.js.map +1 -0
  97. package/dist/detectors/openpgp.d.ts +20 -0
  98. package/dist/detectors/openpgp.d.ts.map +1 -0
  99. package/dist/detectors/openpgp.js +151 -0
  100. package/dist/detectors/openpgp.js.map +1 -0
  101. package/dist/detectors/pem.d.ts.map +1 -1
  102. package/dist/detectors/pem.js +32 -0
  103. package/dist/detectors/pem.js.map +1 -1
  104. package/dist/detectors/php.d.ts +2 -1
  105. package/dist/detectors/php.d.ts.map +1 -1
  106. package/dist/detectors/php.js +7 -3
  107. package/dist/detectors/php.js.map +1 -1
  108. package/dist/detectors/pkcs11.d.ts +17 -0
  109. package/dist/detectors/pkcs11.d.ts.map +1 -0
  110. package/dist/detectors/pkcs11.js +87 -0
  111. package/dist/detectors/pkcs11.js.map +1 -0
  112. package/dist/detectors/proxy.d.ts +52 -0
  113. package/dist/detectors/proxy.d.ts.map +1 -0
  114. package/dist/detectors/proxy.js +157 -0
  115. package/dist/detectors/proxy.js.map +1 -0
  116. package/dist/detectors/pulumi.d.ts +19 -0
  117. package/dist/detectors/pulumi.d.ts.map +1 -0
  118. package/dist/detectors/pulumi.js +80 -0
  119. package/dist/detectors/pulumi.js.map +1 -0
  120. package/dist/detectors/rust.d.ts.map +1 -1
  121. package/dist/detectors/rust.js +5 -0
  122. package/dist/detectors/rust.js.map +1 -1
  123. package/dist/detectors/secrets.d.ts +4 -1
  124. package/dist/detectors/secrets.d.ts.map +1 -1
  125. package/dist/detectors/secrets.js +5 -21
  126. package/dist/detectors/secrets.js.map +1 -1
  127. package/dist/detectors/solidity.d.ts +50 -0
  128. package/dist/detectors/solidity.d.ts.map +1 -0
  129. package/dist/detectors/solidity.js +108 -0
  130. package/dist/detectors/solidity.js.map +1 -0
  131. package/dist/detectors/source.d.ts.map +1 -1
  132. package/dist/detectors/source.js +53 -6
  133. package/dist/detectors/source.js.map +1 -1
  134. package/dist/detectors/spire.d.ts +49 -0
  135. package/dist/detectors/spire.d.ts.map +1 -0
  136. package/dist/detectors/spire.js +77 -0
  137. package/dist/detectors/spire.js.map +1 -0
  138. package/dist/detectors/ssh-ca.d.ts +72 -0
  139. package/dist/detectors/ssh-ca.d.ts.map +1 -0
  140. package/dist/detectors/ssh-ca.js +119 -0
  141. package/dist/detectors/ssh-ca.js.map +1 -0
  142. package/dist/detectors/supply-chain.d.ts +17 -0
  143. package/dist/detectors/supply-chain.d.ts.map +1 -0
  144. package/dist/detectors/supply-chain.js +89 -0
  145. package/dist/detectors/supply-chain.js.map +1 -0
  146. package/dist/detectors/swift.d.ts +22 -0
  147. package/dist/detectors/swift.d.ts.map +1 -0
  148. package/dist/detectors/swift.js +120 -0
  149. package/dist/detectors/swift.js.map +1 -0
  150. package/dist/detectors/terraform.d.ts.map +1 -1
  151. package/dist/detectors/terraform.js +35 -8
  152. package/dist/detectors/terraform.js.map +1 -1
  153. package/dist/detectors/vault.d.ts +15 -0
  154. package/dist/detectors/vault.d.ts.map +1 -0
  155. package/dist/detectors/vault.js +97 -0
  156. package/dist/detectors/vault.js.map +1 -0
  157. package/dist/detectors/vpn.d.ts +8 -8
  158. package/dist/detectors/vpn.d.ts.map +1 -1
  159. package/dist/detectors/vpn.js +12 -38
  160. package/dist/detectors/vpn.js.map +1 -1
  161. package/dist/detectors/weak-hash.d.ts +71 -0
  162. package/dist/detectors/weak-hash.d.ts.map +1 -0
  163. package/dist/detectors/weak-hash.js +116 -0
  164. package/dist/detectors/weak-hash.js.map +1 -0
  165. package/dist/detectors/webauthn.d.ts +61 -0
  166. package/dist/detectors/webauthn.d.ts.map +1 -0
  167. package/dist/detectors/webauthn.js +109 -0
  168. package/dist/detectors/webauthn.js.map +1 -0
  169. package/dist/detectors/xmldsig.d.ts +23 -0
  170. package/dist/detectors/xmldsig.d.ts.map +1 -0
  171. package/dist/detectors/xmldsig.js +84 -0
  172. package/dist/detectors/xmldsig.js.map +1 -0
  173. package/dist/evidence.d.ts +74 -4
  174. package/dist/evidence.d.ts.map +1 -1
  175. package/dist/evidence.js +71 -0
  176. package/dist/evidence.js.map +1 -1
  177. package/dist/index.d.ts +14 -11
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +17 -11
  180. package/dist/index.js.map +1 -1
  181. package/dist/inventory.d.ts +0 -2
  182. package/dist/inventory.d.ts.map +1 -1
  183. package/dist/inventory.js +15 -6
  184. package/dist/inventory.js.map +1 -1
  185. package/dist/registry.d.ts +9 -4
  186. package/dist/registry.d.ts.map +1 -1
  187. package/dist/registry.js +51 -4
  188. package/dist/registry.js.map +1 -1
  189. package/dist/remediate-pipeline.d.ts +0 -3
  190. package/dist/remediate-pipeline.d.ts.map +1 -1
  191. package/dist/remediate-pipeline.js +1 -1
  192. package/dist/remediate-pipeline.js.map +1 -1
  193. package/dist/remediation.d.ts +10 -0
  194. package/dist/remediation.d.ts.map +1 -1
  195. package/dist/remediation.js +32 -0
  196. package/dist/remediation.js.map +1 -1
  197. package/dist/report.d.ts +9 -0
  198. package/dist/report.d.ts.map +1 -1
  199. package/dist/report.js +31 -1
  200. package/dist/report.js.map +1 -1
  201. package/dist/scan-worker.js +5 -3
  202. package/dist/scan-worker.js.map +1 -1
  203. package/dist/scan.d.ts.map +1 -1
  204. package/dist/scan.js +35 -6
  205. package/dist/scan.js.map +1 -1
  206. package/dist/standards-profiles.d.ts +58 -0
  207. package/dist/standards-profiles.d.ts.map +1 -0
  208. package/dist/standards-profiles.js +100 -0
  209. package/dist/standards-profiles.js.map +1 -0
  210. package/dist/types.d.ts +6 -5
  211. package/dist/types.d.ts.map +1 -1
  212. package/dist/types.js.map +1 -1
  213. package/dist/version.d.ts +1 -1
  214. package/dist/version.js +1 -1
  215. package/dist/version.js.map +1 -1
  216. package/dist/vex.d.ts +39 -0
  217. package/dist/vex.d.ts.map +1 -0
  218. package/dist/vex.js +87 -0
  219. package/dist/vex.js.map +1 -0
  220. package/dist/walk.d.ts +8 -3
  221. package/dist/walk.d.ts.map +1 -1
  222. package/dist/walk.js +32 -1
  223. package/dist/walk.js.map +1 -1
  224. package/package.json +1 -1
  225. package/src/cbom-merge.ts +38 -8
  226. package/src/cbom.ts +153 -39
  227. package/src/comments.ts +25 -6
  228. package/src/detect-utils.ts +133 -11
  229. package/src/detectors/age.ts +60 -0
  230. package/src/detectors/ansible.ts +140 -0
  231. package/src/detectors/bicep.ts +107 -0
  232. package/src/detectors/c.ts +31 -3
  233. package/src/detectors/cicd.ts +18 -5
  234. package/src/detectors/cloud-kms.ts +79 -25
  235. package/src/detectors/cloudformation.ts +45 -10
  236. package/src/detectors/codesign.ts +214 -0
  237. package/src/detectors/csharp.ts +13 -3
  238. package/src/detectors/dart.ts +237 -0
  239. package/src/detectors/database.ts +64 -11
  240. package/src/detectors/dkim.ts +172 -0
  241. package/src/detectors/dnssec.ts +19 -10
  242. package/src/detectors/elixir.ts +33 -2
  243. package/src/detectors/java.ts +37 -3
  244. package/src/detectors/jose.ts +19 -3
  245. package/src/detectors/jwk.ts +59 -17
  246. package/src/detectors/k8s.ts +5 -2
  247. package/src/detectors/keystore.ts +109 -0
  248. package/src/detectors/mesh.ts +21 -45
  249. package/src/detectors/messaging.ts +22 -14
  250. package/src/detectors/objc.ts +209 -0
  251. package/src/detectors/openpgp.ts +194 -0
  252. package/src/detectors/pem.ts +30 -0
  253. package/src/detectors/php.ts +9 -4
  254. package/src/detectors/pkcs11.ts +113 -0
  255. package/src/detectors/proxy.ts +236 -0
  256. package/src/detectors/pulumi.ts +106 -0
  257. package/src/detectors/rust.ts +5 -0
  258. package/src/detectors/secrets.ts +15 -26
  259. package/src/detectors/solidity.ts +176 -0
  260. package/src/detectors/source.ts +48 -5
  261. package/src/detectors/spire.ts +143 -0
  262. package/src/detectors/ssh-ca.ts +215 -0
  263. package/src/detectors/supply-chain.ts +117 -0
  264. package/src/detectors/swift.ts +149 -0
  265. package/src/detectors/terraform.ts +42 -8
  266. package/src/detectors/vault.ts +120 -0
  267. package/src/detectors/vpn.ts +20 -48
  268. package/src/detectors/weak-hash.ts +217 -0
  269. package/src/detectors/webauthn.ts +205 -0
  270. package/src/detectors/xmldsig.ts +115 -0
  271. package/src/evidence.ts +127 -4
  272. package/src/index.ts +41 -13
  273. package/src/inventory.ts +15 -7
  274. package/src/registry.ts +51 -4
  275. package/src/remediate-pipeline.ts +1 -1
  276. package/src/remediation.ts +39 -0
  277. package/src/report.ts +36 -1
  278. package/src/scan-worker.ts +5 -3
  279. package/src/scan.ts +41 -6
  280. package/src/standards-profiles.ts +136 -0
  281. package/src/types.ts +9 -4
  282. package/src/version.ts +1 -1
  283. package/src/vex.ts +122 -0
  284. package/src/walk.ts +33 -2
package/src/vex.ts ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * OpenVEX export — turn a {@link ScanResult} into an OpenVEX 0.2.0 document so
3
+ * the quantum-readiness posture flows into the SAME supply-chain pipeline that
4
+ * already ingests CVE-based VEX (Vulnerability Exploitability eXchange).
5
+ *
6
+ * Reference: OpenVEX spec (https://openvex.dev/, `@context` v0.2.0).
7
+ *
8
+ * Modeling choice (documented, honest): PQC findings have no CVE, so each qScan
9
+ * rule becomes a synthetic vulnerability `QK-<ruleId>`. Every finding is reported
10
+ * `status: "affected"` — the classical, Shor-breakable primitive IS present in
11
+ * the product by construction — with the rule's remediation as the VEX
12
+ * `action_statement`. A `--triage` exposure verdict, when present, is surfaced in
13
+ * `status_notes` (score / priority / rationale). qScan never downgrades a finding
14
+ * to `not_affected`: only the operator can attest a mitigation, so that judgment
15
+ * is deliberately left to them (they can post-process this document).
16
+ */
17
+ import { createHash } from "node:crypto";
18
+
19
+ import type { Finding, ScanResult } from "./types.js";
20
+
21
+ /** A single OpenVEX statement: one synthetic vulnerability over its affected products. */
22
+ export interface OpenVexStatement {
23
+ vulnerability: { name: string; description?: string };
24
+ products: { "@id": string }[];
25
+ status: "not_affected" | "affected" | "fixed" | "under_investigation";
26
+ /** Recommended action for an `affected` product (the rule's remediation). */
27
+ action_statement?: string;
28
+ /** Free-form status detail — carries the `--triage` exposure verdict when present. */
29
+ status_notes?: string;
30
+ }
31
+
32
+ /** An OpenVEX 0.2.0 document. */
33
+ export interface OpenVexDocument {
34
+ "@context": "https://openvex.dev/ns/v0.2.0";
35
+ "@id": string;
36
+ author: string;
37
+ timestamp: string;
38
+ version: number;
39
+ statements: OpenVexStatement[];
40
+ }
41
+
42
+ /** Options for {@link toOpenVex}. */
43
+ export interface OpenVexOptions {
44
+ /** Document author (VEX issuer). Default: `"qScan"`. */
45
+ author?: string;
46
+ }
47
+
48
+ /**
49
+ * A stable product identifier for a finding as an IRI (OpenVEX requires product
50
+ * `@id` to be an IRI): a `file:` URI with the line in the fragment, e.g.
51
+ * `file:src/a.ts#L3`.
52
+ */
53
+ function productId(f: Finding): string {
54
+ return `file:${f.location.file}#L${f.location.line}`;
55
+ }
56
+
57
+ /**
58
+ * Build an OpenVEX 0.2.0 document from a scan result. One statement per distinct
59
+ * rule id, listing every affected `file:line` product. Output is deterministic
60
+ * (statements sorted by vulnerability name, products sorted and de-duplicated;
61
+ * the `@id` and `timestamp` derive from the result), so re-exporting the same
62
+ * scan yields byte-identical VEX.
63
+ */
64
+ export function toOpenVex(result: ScanResult, opts: OpenVexOptions = {}): OpenVexDocument {
65
+ // Group findings by rule id → one VEX statement per rule.
66
+ const byRule = new Map<string, Finding[]>();
67
+ for (const f of result.findings) {
68
+ const list = byRule.get(f.ruleId);
69
+ if (list) list.push(f);
70
+ else byRule.set(f.ruleId, [f]);
71
+ }
72
+
73
+ const statements: OpenVexStatement[] = [...byRule.entries()]
74
+ .sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
75
+ .map(([ruleId, findings]) => {
76
+ const first = findings[0] as Finding;
77
+ // De-duplicate + sort the affected products (a rule can fire many times).
78
+ const products = [...new Set(findings.map(productId))].sort().map((id) => ({ "@id": id }));
79
+
80
+ // Surface a triage verdict (if any finding for this rule carries one). Findings
81
+ // of the same rule share a rule; the most-exposed verdict is the informative one.
82
+ const triaged = findings
83
+ .filter((f) => f.triage)
84
+ .sort((a, b) => (b.triage?.exposureScore ?? 0) - (a.triage?.exposureScore ?? 0))[0]?.triage;
85
+
86
+ const statement: OpenVexStatement = {
87
+ vulnerability: {
88
+ name: `QK-${ruleId}`,
89
+ description: first.message,
90
+ },
91
+ products,
92
+ status: "affected",
93
+ action_statement: first.remediation ?? "Migrate to a NIST PQC standard (FIPS 203/204/205).",
94
+ };
95
+ if (triaged) {
96
+ statement.status_notes =
97
+ `qScan triage — exposure ${triaged.exposureScore}/100, priority ${triaged.priority}: ` +
98
+ triaged.rationale;
99
+ }
100
+ return statement;
101
+ });
102
+
103
+ // Deterministic doc id + timestamp so the same scan yields identical VEX. The
104
+ // triage `status_notes` are part of the identity: a triaged and an untriaged
105
+ // export of the same scan are DIFFERENT documents and must not share an @id.
106
+ const digest = createHash("sha256").update(`${result.root}|${result.toolVersion}`, "utf8");
107
+ for (const s of statements) {
108
+ digest.update(
109
+ `\n${s.vulnerability.name}|${s.products.map((p) => p["@id"]).join(",")}|${s.status_notes ?? ""}`,
110
+ );
111
+ }
112
+ const id = `https://quantakrypto.com/vex/${digest.digest("hex").slice(0, 16)}`;
113
+
114
+ return {
115
+ "@context": "https://openvex.dev/ns/v0.2.0",
116
+ "@id": id,
117
+ author: opts.author ?? "qScan",
118
+ timestamp: result.finishedAt,
119
+ version: 1,
120
+ statements,
121
+ };
122
+ }
package/src/walk.ts CHANGED
@@ -11,7 +11,7 @@ import * as path from "node:path";
11
11
  import { isManifestFile } from "./dependencies.js";
12
12
 
13
13
  /** Directories ignored by default (can be disabled with noDefaultIgnores). */
14
- export const DEFAULT_IGNORES: readonly string[] = [
14
+ const DEFAULT_IGNORES: readonly string[] = [
15
15
  "node_modules",
16
16
  ".git",
17
17
  "dist",
@@ -99,7 +99,7 @@ const BINARY_EXTENSIONS = new Set<string>([
99
99
  ]);
100
100
 
101
101
  /** Options accepted by {@link walkFiles}. */
102
- export interface WalkOptions {
102
+ interface WalkOptions {
103
103
  /**
104
104
  * Restrict to paths matching one of these include patterns (substring or
105
105
  * relative-path-prefix match). When omitted/empty, all files pass.
@@ -222,6 +222,37 @@ export function isBinaryPath(rel: string): boolean {
222
222
  return BINARY_EXTENSIONS.has(ext);
223
223
  }
224
224
 
225
+ /**
226
+ * Binary cryptographic key containers we DO want to scan — Java/PKCS#12 keystores
227
+ * and binary OpenPGP keyrings/messages. Unlike the images/archives above these are
228
+ * read byte-preserving (latin1) and exempted from the minified skip, so the
229
+ * keystore + openpgp detectors can identify them by magic number / packet tag.
230
+ * Kept separate from {@link BINARY_EXTENSIONS} so those detectors can opt in.
231
+ */
232
+ const KEYSTORE_EXTENSIONS = new Set<string>([
233
+ ".jks",
234
+ ".keystore",
235
+ ".jceks",
236
+ ".bks",
237
+ ".p12",
238
+ ".pfx",
239
+ // Binary OpenPGP keyrings / messages (armored .asc is text; handled elsewhere).
240
+ ".gpg",
241
+ ".pgp",
242
+ ".kbx",
243
+ ]);
244
+
245
+ /**
246
+ * True if the path is a binary cryptographic key container we scan byte-preserving.
247
+ * Uses a suffix match (not `extname`) so it agrees with the detectors' `hasExtension`
248
+ * even for a bare dotfile literally named e.g. `.gpg` (where `extname` returns "").
249
+ */
250
+ export function isKeystorePath(rel: string): boolean {
251
+ const lower = rel.toLowerCase();
252
+ for (const ext of KEYSTORE_EXTENSIONS) if (lower.endsWith(ext)) return true;
253
+ return false;
254
+ }
255
+
225
256
  /**
226
257
  * Compound / pattern extensions that mark generated or bundled output we skip
227
258
  * by default (beyond `.min.js` / `.map`, which {@link isBinaryPath} handles).