@quantakrypto/core 0.4.4 → 0.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 (302) 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/crypto-agility.d.ts +158 -0
  14. package/dist/crypto-agility.d.ts.map +1 -0
  15. package/dist/crypto-agility.js +285 -0
  16. package/dist/crypto-agility.js.map +1 -0
  17. package/dist/dependencies.d.ts.map +1 -1
  18. package/dist/dependencies.js +180 -18
  19. package/dist/dependencies.js.map +1 -1
  20. package/dist/detect-utils.d.ts +55 -21
  21. package/dist/detect-utils.d.ts.map +1 -1
  22. package/dist/detect-utils.js +133 -7
  23. package/dist/detect-utils.js.map +1 -1
  24. package/dist/detectors/age.d.ts +16 -0
  25. package/dist/detectors/age.d.ts.map +1 -0
  26. package/dist/detectors/age.js +40 -0
  27. package/dist/detectors/age.js.map +1 -0
  28. package/dist/detectors/ansible.d.ts +13 -0
  29. package/dist/detectors/ansible.d.ts.map +1 -0
  30. package/dist/detectors/ansible.js +108 -0
  31. package/dist/detectors/ansible.js.map +1 -0
  32. package/dist/detectors/bicep.d.ts +20 -0
  33. package/dist/detectors/bicep.d.ts.map +1 -0
  34. package/dist/detectors/bicep.js +74 -0
  35. package/dist/detectors/bicep.js.map +1 -0
  36. package/dist/detectors/c.d.ts.map +1 -1
  37. package/dist/detectors/c.js +30 -3
  38. package/dist/detectors/c.js.map +1 -1
  39. package/dist/detectors/cicd.d.ts.map +1 -1
  40. package/dist/detectors/cicd.js +18 -5
  41. package/dist/detectors/cicd.js.map +1 -1
  42. package/dist/detectors/cloud-kms.d.ts +8 -5
  43. package/dist/detectors/cloud-kms.d.ts.map +1 -1
  44. package/dist/detectors/cloud-kms.js +62 -20
  45. package/dist/detectors/cloud-kms.js.map +1 -1
  46. package/dist/detectors/cloudformation.d.ts +8 -0
  47. package/dist/detectors/cloudformation.d.ts.map +1 -1
  48. package/dist/detectors/cloudformation.js +41 -10
  49. package/dist/detectors/cloudformation.js.map +1 -1
  50. package/dist/detectors/codesign.d.ts +50 -0
  51. package/dist/detectors/codesign.d.ts.map +1 -0
  52. package/dist/detectors/codesign.js +143 -0
  53. package/dist/detectors/codesign.js.map +1 -0
  54. package/dist/detectors/csharp.d.ts.map +1 -1
  55. package/dist/detectors/csharp.js +13 -3
  56. package/dist/detectors/csharp.js.map +1 -1
  57. package/dist/detectors/dart.d.ts +35 -0
  58. package/dist/detectors/dart.d.ts.map +1 -0
  59. package/dist/detectors/dart.js +177 -0
  60. package/dist/detectors/dart.js.map +1 -0
  61. package/dist/detectors/database.d.ts +5 -3
  62. package/dist/detectors/database.d.ts.map +1 -1
  63. package/dist/detectors/database.js +45 -8
  64. package/dist/detectors/database.js.map +1 -1
  65. package/dist/detectors/dkim.d.ts +54 -0
  66. package/dist/detectors/dkim.d.ts.map +1 -0
  67. package/dist/detectors/dkim.js +92 -0
  68. package/dist/detectors/dkim.js.map +1 -0
  69. package/dist/detectors/dnssec.d.ts.map +1 -1
  70. package/dist/detectors/dnssec.js +19 -10
  71. package/dist/detectors/dnssec.js.map +1 -1
  72. package/dist/detectors/elixir.d.ts.map +1 -1
  73. package/dist/detectors/elixir.js +34 -2
  74. package/dist/detectors/elixir.js.map +1 -1
  75. package/dist/detectors/java.d.ts.map +1 -1
  76. package/dist/detectors/java.js +33 -3
  77. package/dist/detectors/java.js.map +1 -1
  78. package/dist/detectors/jose.d.ts.map +1 -1
  79. package/dist/detectors/jose.js +13 -3
  80. package/dist/detectors/jose.js.map +1 -1
  81. package/dist/detectors/jwk.d.ts +7 -7
  82. package/dist/detectors/jwk.d.ts.map +1 -1
  83. package/dist/detectors/jwk.js +44 -8
  84. package/dist/detectors/jwk.js.map +1 -1
  85. package/dist/detectors/k8s.d.ts.map +1 -1
  86. package/dist/detectors/k8s.js +5 -2
  87. package/dist/detectors/k8s.js.map +1 -1
  88. package/dist/detectors/keystore.d.ts +19 -0
  89. package/dist/detectors/keystore.d.ts.map +1 -0
  90. package/dist/detectors/keystore.js +76 -0
  91. package/dist/detectors/keystore.js.map +1 -0
  92. package/dist/detectors/mesh.d.ts +7 -8
  93. package/dist/detectors/mesh.d.ts.map +1 -1
  94. package/dist/detectors/mesh.js +17 -35
  95. package/dist/detectors/mesh.js.map +1 -1
  96. package/dist/detectors/messaging.d.ts +2 -2
  97. package/dist/detectors/messaging.d.ts.map +1 -1
  98. package/dist/detectors/messaging.js +19 -12
  99. package/dist/detectors/messaging.js.map +1 -1
  100. package/dist/detectors/objc.d.ts +41 -0
  101. package/dist/detectors/objc.d.ts.map +1 -0
  102. package/dist/detectors/objc.js +146 -0
  103. package/dist/detectors/objc.js.map +1 -0
  104. package/dist/detectors/openpgp.d.ts +20 -0
  105. package/dist/detectors/openpgp.d.ts.map +1 -0
  106. package/dist/detectors/openpgp.js +151 -0
  107. package/dist/detectors/openpgp.js.map +1 -0
  108. package/dist/detectors/pem.d.ts.map +1 -1
  109. package/dist/detectors/pem.js +32 -0
  110. package/dist/detectors/pem.js.map +1 -1
  111. package/dist/detectors/php.d.ts +2 -1
  112. package/dist/detectors/php.d.ts.map +1 -1
  113. package/dist/detectors/php.js +7 -3
  114. package/dist/detectors/php.js.map +1 -1
  115. package/dist/detectors/pkcs11.d.ts +17 -0
  116. package/dist/detectors/pkcs11.d.ts.map +1 -0
  117. package/dist/detectors/pkcs11.js +87 -0
  118. package/dist/detectors/pkcs11.js.map +1 -0
  119. package/dist/detectors/proxy.d.ts +52 -0
  120. package/dist/detectors/proxy.d.ts.map +1 -0
  121. package/dist/detectors/proxy.js +157 -0
  122. package/dist/detectors/proxy.js.map +1 -0
  123. package/dist/detectors/pulumi.d.ts +19 -0
  124. package/dist/detectors/pulumi.d.ts.map +1 -0
  125. package/dist/detectors/pulumi.js +80 -0
  126. package/dist/detectors/pulumi.js.map +1 -0
  127. package/dist/detectors/rust.d.ts.map +1 -1
  128. package/dist/detectors/rust.js +5 -0
  129. package/dist/detectors/rust.js.map +1 -1
  130. package/dist/detectors/secrets.d.ts +4 -1
  131. package/dist/detectors/secrets.d.ts.map +1 -1
  132. package/dist/detectors/secrets.js +5 -21
  133. package/dist/detectors/secrets.js.map +1 -1
  134. package/dist/detectors/solidity.d.ts +50 -0
  135. package/dist/detectors/solidity.d.ts.map +1 -0
  136. package/dist/detectors/solidity.js +108 -0
  137. package/dist/detectors/solidity.js.map +1 -0
  138. package/dist/detectors/source.d.ts.map +1 -1
  139. package/dist/detectors/source.js +105 -6
  140. package/dist/detectors/source.js.map +1 -1
  141. package/dist/detectors/spire.d.ts +49 -0
  142. package/dist/detectors/spire.d.ts.map +1 -0
  143. package/dist/detectors/spire.js +77 -0
  144. package/dist/detectors/spire.js.map +1 -0
  145. package/dist/detectors/ssh-ca.d.ts +72 -0
  146. package/dist/detectors/ssh-ca.d.ts.map +1 -0
  147. package/dist/detectors/ssh-ca.js +119 -0
  148. package/dist/detectors/ssh-ca.js.map +1 -0
  149. package/dist/detectors/supply-chain.d.ts +17 -0
  150. package/dist/detectors/supply-chain.d.ts.map +1 -0
  151. package/dist/detectors/supply-chain.js +89 -0
  152. package/dist/detectors/supply-chain.js.map +1 -0
  153. package/dist/detectors/swift.d.ts +22 -0
  154. package/dist/detectors/swift.d.ts.map +1 -0
  155. package/dist/detectors/swift.js +120 -0
  156. package/dist/detectors/swift.js.map +1 -0
  157. package/dist/detectors/terraform.d.ts.map +1 -1
  158. package/dist/detectors/terraform.js +35 -8
  159. package/dist/detectors/terraform.js.map +1 -1
  160. package/dist/detectors/vault.d.ts +15 -0
  161. package/dist/detectors/vault.d.ts.map +1 -0
  162. package/dist/detectors/vault.js +97 -0
  163. package/dist/detectors/vault.js.map +1 -0
  164. package/dist/detectors/vpn.d.ts +8 -8
  165. package/dist/detectors/vpn.d.ts.map +1 -1
  166. package/dist/detectors/vpn.js +12 -38
  167. package/dist/detectors/vpn.js.map +1 -1
  168. package/dist/detectors/weak-hash.d.ts +71 -0
  169. package/dist/detectors/weak-hash.d.ts.map +1 -0
  170. package/dist/detectors/weak-hash.js +116 -0
  171. package/dist/detectors/weak-hash.js.map +1 -0
  172. package/dist/detectors/webauthn.d.ts +61 -0
  173. package/dist/detectors/webauthn.d.ts.map +1 -0
  174. package/dist/detectors/webauthn.js +109 -0
  175. package/dist/detectors/webauthn.js.map +1 -0
  176. package/dist/detectors/xmldsig.d.ts +23 -0
  177. package/dist/detectors/xmldsig.d.ts.map +1 -0
  178. package/dist/detectors/xmldsig.js +84 -0
  179. package/dist/detectors/xmldsig.js.map +1 -0
  180. package/dist/evidence.d.ts +74 -4
  181. package/dist/evidence.d.ts.map +1 -1
  182. package/dist/evidence.js +71 -0
  183. package/dist/evidence.js.map +1 -1
  184. package/dist/hndl.d.ts +241 -0
  185. package/dist/hndl.d.ts.map +1 -0
  186. package/dist/hndl.js +752 -0
  187. package/dist/hndl.js.map +1 -0
  188. package/dist/index.d.ts +18 -11
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +21 -11
  191. package/dist/index.js.map +1 -1
  192. package/dist/inventory.d.ts +0 -2
  193. package/dist/inventory.d.ts.map +1 -1
  194. package/dist/inventory.js +15 -6
  195. package/dist/inventory.js.map +1 -1
  196. package/dist/parallel.d.ts.map +1 -1
  197. package/dist/parallel.js +22 -23
  198. package/dist/parallel.js.map +1 -1
  199. package/dist/registry.d.ts +9 -4
  200. package/dist/registry.d.ts.map +1 -1
  201. package/dist/registry.js +51 -4
  202. package/dist/registry.js.map +1 -1
  203. package/dist/remediate-pipeline.d.ts +0 -3
  204. package/dist/remediate-pipeline.d.ts.map +1 -1
  205. package/dist/remediate-pipeline.js +1 -1
  206. package/dist/remediate-pipeline.js.map +1 -1
  207. package/dist/remediation.d.ts +10 -0
  208. package/dist/remediation.d.ts.map +1 -1
  209. package/dist/remediation.js +32 -0
  210. package/dist/remediation.js.map +1 -1
  211. package/dist/report.d.ts +17 -0
  212. package/dist/report.d.ts.map +1 -1
  213. package/dist/report.js +106 -20
  214. package/dist/report.js.map +1 -1
  215. package/dist/scan-worker.js +5 -3
  216. package/dist/scan-worker.js.map +1 -1
  217. package/dist/scan.d.ts.map +1 -1
  218. package/dist/scan.js +35 -6
  219. package/dist/scan.js.map +1 -1
  220. package/dist/standards-profiles.d.ts +58 -0
  221. package/dist/standards-profiles.d.ts.map +1 -0
  222. package/dist/standards-profiles.js +100 -0
  223. package/dist/standards-profiles.js.map +1 -0
  224. package/dist/types.d.ts +7 -6
  225. package/dist/types.d.ts.map +1 -1
  226. package/dist/types.js.map +1 -1
  227. package/dist/version.d.ts +1 -1
  228. package/dist/version.js +1 -1
  229. package/dist/version.js.map +1 -1
  230. package/dist/vex.d.ts +39 -0
  231. package/dist/vex.d.ts.map +1 -0
  232. package/dist/vex.js +87 -0
  233. package/dist/vex.js.map +1 -0
  234. package/dist/walk.d.ts +39 -4
  235. package/dist/walk.d.ts.map +1 -1
  236. package/dist/walk.js +62 -10
  237. package/dist/walk.js.map +1 -1
  238. package/package.json +1 -1
  239. package/src/cbom-merge.ts +38 -8
  240. package/src/cbom.ts +153 -39
  241. package/src/comments.ts +25 -6
  242. package/src/crypto-agility.ts +407 -0
  243. package/src/dependencies.ts +182 -16
  244. package/src/detect-utils.ts +133 -11
  245. package/src/detectors/age.ts +60 -0
  246. package/src/detectors/ansible.ts +140 -0
  247. package/src/detectors/bicep.ts +107 -0
  248. package/src/detectors/c.ts +31 -3
  249. package/src/detectors/cicd.ts +18 -5
  250. package/src/detectors/cloud-kms.ts +79 -25
  251. package/src/detectors/cloudformation.ts +45 -10
  252. package/src/detectors/codesign.ts +214 -0
  253. package/src/detectors/csharp.ts +13 -3
  254. package/src/detectors/dart.ts +237 -0
  255. package/src/detectors/database.ts +64 -11
  256. package/src/detectors/dkim.ts +172 -0
  257. package/src/detectors/dnssec.ts +19 -10
  258. package/src/detectors/elixir.ts +33 -2
  259. package/src/detectors/java.ts +37 -3
  260. package/src/detectors/jose.ts +19 -3
  261. package/src/detectors/jwk.ts +59 -17
  262. package/src/detectors/k8s.ts +5 -2
  263. package/src/detectors/keystore.ts +109 -0
  264. package/src/detectors/mesh.ts +21 -45
  265. package/src/detectors/messaging.ts +22 -14
  266. package/src/detectors/objc.ts +209 -0
  267. package/src/detectors/openpgp.ts +194 -0
  268. package/src/detectors/pem.ts +30 -0
  269. package/src/detectors/php.ts +9 -4
  270. package/src/detectors/pkcs11.ts +113 -0
  271. package/src/detectors/proxy.ts +236 -0
  272. package/src/detectors/pulumi.ts +106 -0
  273. package/src/detectors/rust.ts +5 -0
  274. package/src/detectors/secrets.ts +15 -26
  275. package/src/detectors/solidity.ts +176 -0
  276. package/src/detectors/source.ts +110 -5
  277. package/src/detectors/spire.ts +143 -0
  278. package/src/detectors/ssh-ca.ts +215 -0
  279. package/src/detectors/supply-chain.ts +117 -0
  280. package/src/detectors/swift.ts +149 -0
  281. package/src/detectors/terraform.ts +42 -8
  282. package/src/detectors/vault.ts +120 -0
  283. package/src/detectors/vpn.ts +20 -48
  284. package/src/detectors/weak-hash.ts +217 -0
  285. package/src/detectors/webauthn.ts +205 -0
  286. package/src/detectors/xmldsig.ts +115 -0
  287. package/src/evidence.ts +127 -4
  288. package/src/hndl.ts +1012 -0
  289. package/src/index.ts +93 -13
  290. package/src/inventory.ts +15 -7
  291. package/src/parallel.ts +21 -20
  292. package/src/registry.ts +51 -4
  293. package/src/remediate-pipeline.ts +1 -1
  294. package/src/remediation.ts +39 -0
  295. package/src/report.ts +120 -20
  296. package/src/scan-worker.ts +5 -3
  297. package/src/scan.ts +41 -6
  298. package/src/standards-profiles.ts +136 -0
  299. package/src/types.ts +18 -5
  300. package/src/version.ts +1 -1
  301. package/src/vex.ts +122 -0
  302. package/src/walk.ts +80 -12
@@ -1,4 +1,4 @@
1
- import { JS_TS_EXTENSIONS, DOC_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
1
+ import { JS_TS_EXTENSIONS, DOC_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, enclosingObject, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
2
2
  import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
3
3
  /* -------------------------------------------------------------------------- */
4
4
  /* Precompiled regexes (module scope — never recreated per file) */
@@ -6,6 +6,14 @@ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cw
6
6
  // `rsa-pss` is listed before `rsa` so the alternation consumes the full token
7
7
  // (ordered alternation would otherwise match `rsa` and reject the `-pss` tail).
8
8
  const RE_GENERATE_KEYPAIR = /generateKeyPair(?:Sync)?\s*\(\s*['"`](rsa-pss|rsa|ec|dsa|dh|x25519|x448|ed25519|ed448)['"`]/g;
9
+ // Variable-typed key generation: `generateKeyPair(kind, …)` where the algorithm
10
+ // argument is an IDENTIFIER (a variable), not a quoted literal, so the concrete
11
+ // family is unknown at scan time but a classical key is still being generated.
12
+ // The lookbehind anchors the call to a word boundary so it does not fire inside
13
+ // a longer identifier (e.g. `myGenerateKeyPair(`); the first argument must be a
14
+ // bare identifier immediately followed by `,` or `)` so a quoted-literal call
15
+ // (handled by RE_GENERATE_KEYPAIR) never matches here.
16
+ const RE_GENERATE_KEYPAIR_VAR = /(?<![\w$])generateKeyPair(?:Sync)?\s*\(\s*([A-Za-z_$][\w$]*)\s*[,)]/g;
9
17
  const KEYGEN_INFO = {
10
18
  rsa: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
11
19
  // RSA-PSS is signature-only, so classify it as a (forgeable) signature
@@ -108,6 +116,18 @@ function escapeRe(s) {
108
116
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
109
117
  }
110
118
  const RE_CREATE_SIGN_VERIFY = /create(?:Sign|Verify)\s*\(/g;
119
+ // Computed-member (bracket) access to a Node `crypto` method:
120
+ // `crypto['createSign'](…)`, `c["createECDH"](…)`. Bracket access defeats the
121
+ // dotted-call regexes above (which require `name(`), so match a quoted method
122
+ // name inside a `[ '…' ]( ` computed call and route it to the right rule. The
123
+ // method names here take no key-type argument, so no argument parsing is needed;
124
+ // `generateKeyPair(Sync)` bracket access is handled separately (it carries the
125
+ // key-type literal). None of these overlap the dotted regexes (those require the
126
+ // name immediately before `(`, whereas here it is followed by a closing quote).
127
+ const RE_BRACKET_CRYPTO_METHOD = /\[\s*['"`](createSign|createVerify|createECDH|createDiffieHellman|createDiffieHellmanGroup|publicEncrypt|privateDecrypt)['"`]\s*\]\s*\(/g;
128
+ // `crypto['generateKeyPairSync']('rsa', …)`: bracket access carrying the quoted
129
+ // key-type literal, classified exactly like the dotted `generateKeyPair` call.
130
+ const RE_BRACKET_KEYGEN = /\[\s*['"`]generateKeyPair(?:Sync)?['"`]\s*\]\s*\(\s*['"`](rsa-pss|rsa|ec|dsa|dh|x25519|x448|ed25519|ed448)['"`]/g;
111
131
  // One-shot crypto.sign/verify(algorithm, data, key). A LOOKBEHIND (not a
112
132
  // consumed char) anchors it so it doesn't fire inside identifiers like `assign(`
113
133
  // or `createSign(` (handled by the dedicated createSign/createVerify rule) —
@@ -147,10 +167,14 @@ const RE_TLS_REJECT = /rejectUnauthorized\s*:\s*false/g;
147
167
  // Hardened cipher regex: bounded spans (no unbounded `[^'"`]*` straddling the
148
168
  // alternation), single-quote-style anchoring removed in favour of {0,256} bounds
149
169
  // so worst-case backtracking is linear in the bound, not the file (P0-6).
150
- // The `(?<![!-])` lookbehind skips OpenSSL EXCLUSION syntax — `!MD5` / `-RC4`
151
- // DISABLE those ciphers, so a hardened list like `...:!aNULL:!MD5:!RC4` must not
152
- // be flagged as weak (audit: crypto #7). A genuinely-enabled `RC4` still matches.
153
- const RE_TLS_WEAK_CIPHER = /ciphers\s*:\s*['"`][^'"`\n]{0,256}?\b(?<![!-])(RC4|DES|3DES|MD5|NULL|EXPORT|aNULL|eNULL)\b[^'"`\n]{0,256}?['"`]/gi;
170
+ // Skip OpenSSL EXCLUSION syntax — `!MD5` / `:-RC4` / `!ECDHE-RSA-RC4-SHA` DISABLE
171
+ // those ciphers, so a hardened list must not be flagged as weak (audit: crypto #7). The
172
+ // single variable-length lookbehind walks back over the suite-name chars to the
173
+ // ELEMENT boundary (`:` / whitespace / comma / quote / start) and rejects the match
174
+ // when that element begins with `!` or `-` — covering a bare `!RC4`, a space-separated
175
+ // `HIGH !RC4`, and a full-suite `!ECDHE-RSA-RC4-SHA`. The intra-name hyphen in a
176
+ // genuinely-enabled `ECDHE-RSA-RC4-SHA` (no leading `!`/`-`) still matches.
177
+ const RE_TLS_WEAK_CIPHER = /ciphers\s*:\s*['"`][^'"`\n]{0,256}?\b(?<![:'"`,\s]\s*[!-][\w-]{0,64})(RC4|DES|3DES|MD5|NULL|EXPORT|aNULL|eNULL)\b[^'"`\n]{0,256}?['"`]/gi;
154
178
  /* -------------------------------------------------------------------------- */
155
179
  /* Node.js `crypto` module */
156
180
  /* -------------------------------------------------------------------------- */
@@ -280,6 +304,19 @@ const nodeCryptoDetector = {
280
304
  eachMatch(RE_GENERATE_KEYPAIR, content, (m) => {
281
305
  pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length);
282
306
  });
307
+ // generateKeyPair(Sync)(<variable>, …): key type passed as a variable, so the
308
+ // family is unknown; emit the generic keygen rule (unknown / key-exchange /
309
+ // HNDL-conservative) rather than missing the classical key generation.
310
+ eachMatch(RE_GENERATE_KEYPAIR_VAR, content, (m) => {
311
+ findings.push(findingFromRule(RULE_NODE_KEYGEN, { file, content, index: m.index, matchLength: m[0].length }, {
312
+ title: "Classical key generation (variable key type)",
313
+ message: "Generates a classical asymmetric key pair (key type passed as a variable), which is not quantum-safe.",
314
+ }));
315
+ });
316
+ // Bracket (computed-member) key generation: crypto['generateKeyPairSync']('rsa').
317
+ eachMatch(RE_BRACKET_KEYGEN, content, (m) => {
318
+ pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length);
319
+ });
283
320
  // Import-alias resolution: follow `import { generateKeyPairSync as gk }` (and
284
321
  // the CommonJS destructure-rename) so an aliased call still detects. Only the
285
322
  // keygen / ECDH / DH constructors are resolved — their classification is
@@ -322,6 +359,25 @@ const nodeCryptoDetector = {
322
359
  matchLength: m[0].length,
323
360
  }));
324
361
  });
362
+ // Bracket (computed-member) access to argument-free crypto methods:
363
+ // crypto['createSign'](…), c["createECDH"](…). Route each to its rule.
364
+ eachMatch(RE_BRACKET_CRYPTO_METHOD, content, (m) => {
365
+ const loc = { file, content, index: m.index, matchLength: m[0].length };
366
+ const method = m[1];
367
+ if (method === "createSign" || method === "createVerify") {
368
+ findings.push(findingFromRule(RULE_NODE_SIGN, loc));
369
+ }
370
+ else if (method === "createECDH") {
371
+ findings.push(findingFromRule(RULE_NODE_ECDH, loc));
372
+ }
373
+ else if (method === "publicEncrypt" || method === "privateDecrypt") {
374
+ findings.push(findingFromRule(RULE_NODE_RSA_ENCRYPT, loc));
375
+ }
376
+ else {
377
+ // createDiffieHellman / createDiffieHellmanGroup: finite-field DH.
378
+ findings.push(findingFromRule(RULE_NODE_DH, loc));
379
+ }
380
+ });
325
381
  // One-shot crypto.sign(algorithm, data, key) / crypto.verify(...) (Node ≥ 12).
326
382
  eachMatch(RE_ONESHOT_SIGN_VERIFY, content, (m) => {
327
383
  findings.push(findingFromRule(RULE_NODE_SIGN_ONESHOT, {
@@ -433,7 +489,9 @@ const webCryptoDetector = {
433
489
  algorithm = name === "X448" ? "X448" : "X25519";
434
490
  category = "key-exchange";
435
491
  hndl = true;
436
- severity = "low"; // modern but classical
492
+ // `medium`, aligned with X25519/X448 key agreement in node/rust/go — HNDL-exposed
493
+ // key agreement scores the same regardless of which surface uses it.
494
+ severity = "medium";
437
495
  }
438
496
  else if (name === "ED25519" || name === "ED448") {
439
497
  algorithm = "EdDSA";
@@ -659,8 +717,21 @@ const jwtDetector = {
659
717
  appliesTo: (f) => hasExtension(f, JWT_HOST_EXTENSIONS),
660
718
  detect({ file, content }) {
661
719
  const findings = [];
720
+ // Deferral guards against double-counting:
721
+ // - webCryptoDetector owns a quoted `RSA-OAEP`/`ECDH-ES` sitting next to a
722
+ // `subtle.*(` call (it matches the `RSA-OAEP`/`ECDH` name). Collect the subtle
723
+ // call offsets so the JOSE key-management loops can skip those. (In non-JS host
724
+ // files there are no subtle calls, so nothing is skipped.)
725
+ // - jwkDetector owns any alg/`kty` that lives inside a JWK object; skip a token
726
+ // whose enclosing `{…}` carries a `"kty"` so a JWK's own `alg` is not counted by
727
+ // both detectors. Mirrors the same guard jose.ts applies for other languages.
728
+ const subtleCalls = [];
729
+ eachMatch(RE_SUBTLE_CALL, content, (m) => subtleCalls.push(m.index));
730
+ const inJwk = (index) => enclosingObject(content, index).includes('"kty"');
662
731
  // Classical JWS signature alg tokens. Anchored to quotes to avoid words.
663
732
  eachMatch(RE_JWT_ALG, content, (m) => {
733
+ if (inJwk(m.index))
734
+ return; // jwk-{rsa,ec,eddsa} owns a JWK's own alg
664
735
  const alg = m[1];
665
736
  let algorithm;
666
737
  if (alg.startsWith("RS") || alg.startsWith("PS"))
@@ -677,6 +748,10 @@ const jwtDetector = {
677
748
  });
678
749
  // JOSE ECDH-ES key agreement (and ECDH-ES+A*KW) — confidentiality, HNDL.
679
750
  eachMatch(RE_JOSE_ECDH, content, (m) => {
751
+ if (inJwk(m.index))
752
+ return; // jwk-ec owns a JWK's own alg
753
+ if (nearSortedCall(subtleCalls, m.index, 400))
754
+ return; // webcrypto owns it
680
755
  findings.push(findingFromRule(RULE_JOSE_ECDH, { file, content, index: m.index, matchLength: m[0].length }, {
681
756
  title: `JOSE key agreement ${m[1]}`,
682
757
  message: `JOSE "${m[1]}" performs classical ECDH key agreement — harvest-now-decrypt-later exposed.`,
@@ -684,6 +759,12 @@ const jwtDetector = {
684
759
  });
685
760
  // JOSE RSA key transport (RSA-OAEP / RSA1_5) — classical RSA encryption, HNDL.
686
761
  eachMatch(RE_JOSE_KEM, content, (m) => {
762
+ if (inJwk(m.index))
763
+ return; // jwk-rsa owns a JWK's own alg
764
+ // webCryptoDetector's regex matches `RSA-OAEP` but NOT `RSA1_5`, so only defer the
765
+ // OAEP form near a subtle call — deferring RSA1_5 would drop it entirely.
766
+ if (m[1].startsWith("RSA-OAEP") && nearSortedCall(subtleCalls, m.index, 400))
767
+ return;
687
768
  findings.push(findingFromRule(RULE_JOSE_RSA_OAEP, { file, content, index: m.index, matchLength: m[0].length }, {
688
769
  title: `JOSE RSA key transport ${m[1]}`,
689
770
  message: `JOSE "${m[1]}" is classical RSA key transport — harvest-now-decrypt-later exposed.`,
@@ -782,6 +863,20 @@ const RE_CERT_SIG_ALG = /\b(sha(?:1|256|384|512)WithRSAEncryption|ecdsa-with-SHA
782
863
  // Shor-broken key agreement — the harvest-now-decrypt-later surface a lexical
783
864
  // scan misses when only the `crypto/*` API is matched, not the negotiated kex.
784
865
  const RE_SSH_KEX = /\b(diffie-hellman-group(?:1|14|15|16|17|18)(?:-sha1|-sha256|-sha512)?|diffie-hellman-group-exchange-sha(?:1|256)|ecdh-sha2-nistp(?:256|384|521)|curve25519-sha256)\b/g;
866
+ /**
867
+ * True when the match at `index` sits inside a DOCUMENTATION field value — a
868
+ * `description` / `help` / `summary` / `doc` / `comment` / `note` assignment
869
+ * (`key = "…"` or `key: "…"`), as used by Terraform/Packer variable descriptions,
870
+ * JSON-schema docs, etc. Classical KEX/cipher algorithm names LISTED in such a field
871
+ * are prose ("acceptable values include ecdh-sha2-nistp256, …"), not an active
872
+ * setting, so a transport-token rule must not fire on them. Scans the current line's
873
+ * prefix only (cheap; the common single-line description case).
874
+ */
875
+ function inDocFieldValue(content, index) {
876
+ const lineStart = content.lastIndexOf("\n", index - 1) + 1;
877
+ const prefix = content.slice(lineStart, index);
878
+ return /\b(?:description|help|summary|doc|comment|note)\b\s*[:=]/i.test(prefix);
879
+ }
785
880
  // A bare key-type token (`ssh-rsa`, `ssh-ed25519`, …) is real SSH crypto only
786
881
  // when it is EITHER (a) followed by base64 key material — an actual
787
882
  // authorized_keys / known_hosts entry — OR (b) one of ≥2 DISTINCT ssh key/host-key
@@ -910,6 +1005,8 @@ const sshCertDetector = {
910
1005
  });
911
1006
  // SSH key-exchange algorithm identifiers (finite-field DH / ECDH / X25519).
912
1007
  eachMatch(RE_SSH_KEX, content, (m) => {
1008
+ if (inDocFieldValue(content, m.index))
1009
+ return; // names listed in a doc string
913
1010
  const tok = m[1];
914
1011
  const algorithm = tok.startsWith("diffie-hellman")
915
1012
  ? "DH"
@@ -965,6 +1062,8 @@ const tlsClassicalKexDetector = {
965
1062
  detect({ file, content }) {
966
1063
  const findings = [];
967
1064
  eachMatch(RE_TLS_CLASSICAL_KEX, content, (m) => {
1065
+ if (inDocFieldValue(content, m.index))
1066
+ return; // names listed in a doc string
968
1067
  const tok = m[0];
969
1068
  const algorithm = tok.includes("ECDH") ? "ECDH" : "DH";
970
1069
  findings.push(findingFromRule(RULE_TLS_CLASSICAL_KEX, { file, content, index: m.index, matchLength: m[0].length }, {