@quantakrypto/core 0.4.3 → 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 (314) hide show
  1. package/README.md +76 -27
  2. package/dist/cbom-merge.d.ts +9 -0
  3. package/dist/cbom-merge.d.ts.map +1 -0
  4. package/dist/cbom-merge.js +121 -0
  5. package/dist/cbom-merge.js.map +1 -0
  6. package/dist/cbom.d.ts +5 -0
  7. package/dist/cbom.d.ts.map +1 -1
  8. package/dist/cbom.js +132 -37
  9. package/dist/cbom.js.map +1 -1
  10. package/dist/comments.d.ts +6 -26
  11. package/dist/comments.d.ts.map +1 -1
  12. package/dist/comments.js +77 -38
  13. package/dist/comments.js.map +1 -1
  14. package/dist/dependencies.d.ts.map +1 -1
  15. package/dist/dependencies.js +1 -1
  16. package/dist/dependencies.js.map +1 -1
  17. package/dist/detect-utils.d.ts +59 -21
  18. package/dist/detect-utils.d.ts.map +1 -1
  19. package/dist/detect-utils.js +143 -8
  20. package/dist/detect-utils.js.map +1 -1
  21. package/dist/detectors/age.d.ts +16 -0
  22. package/dist/detectors/age.d.ts.map +1 -0
  23. package/dist/detectors/age.js +40 -0
  24. package/dist/detectors/age.js.map +1 -0
  25. package/dist/detectors/ansible.d.ts +13 -0
  26. package/dist/detectors/ansible.d.ts.map +1 -0
  27. package/dist/detectors/ansible.js +108 -0
  28. package/dist/detectors/ansible.js.map +1 -0
  29. package/dist/detectors/bicep.d.ts +20 -0
  30. package/dist/detectors/bicep.d.ts.map +1 -0
  31. package/dist/detectors/bicep.js +74 -0
  32. package/dist/detectors/bicep.js.map +1 -0
  33. package/dist/detectors/c.d.ts +4 -2
  34. package/dist/detectors/c.d.ts.map +1 -1
  35. package/dist/detectors/c.js +222 -4
  36. package/dist/detectors/c.js.map +1 -1
  37. package/dist/detectors/cicd.d.ts +27 -0
  38. package/dist/detectors/cicd.d.ts.map +1 -0
  39. package/dist/detectors/cicd.js +129 -0
  40. package/dist/detectors/cicd.js.map +1 -0
  41. package/dist/detectors/cloud-kms.d.ts +24 -0
  42. package/dist/detectors/cloud-kms.d.ts.map +1 -0
  43. package/dist/detectors/cloud-kms.js +99 -0
  44. package/dist/detectors/cloud-kms.js.map +1 -0
  45. package/dist/detectors/cloudformation.d.ts +38 -0
  46. package/dist/detectors/cloudformation.d.ts.map +1 -0
  47. package/dist/detectors/cloudformation.js +201 -0
  48. package/dist/detectors/cloudformation.js.map +1 -0
  49. package/dist/detectors/codesign.d.ts +50 -0
  50. package/dist/detectors/codesign.d.ts.map +1 -0
  51. package/dist/detectors/codesign.js +143 -0
  52. package/dist/detectors/codesign.js.map +1 -0
  53. package/dist/detectors/csharp.d.ts.map +1 -1
  54. package/dist/detectors/csharp.js +13 -3
  55. package/dist/detectors/csharp.js.map +1 -1
  56. package/dist/detectors/dart.d.ts +35 -0
  57. package/dist/detectors/dart.d.ts.map +1 -0
  58. package/dist/detectors/dart.js +177 -0
  59. package/dist/detectors/dart.js.map +1 -0
  60. package/dist/detectors/database.d.ts +19 -0
  61. package/dist/detectors/database.d.ts.map +1 -0
  62. package/dist/detectors/database.js +97 -0
  63. package/dist/detectors/database.js.map +1 -0
  64. package/dist/detectors/dkim.d.ts +54 -0
  65. package/dist/detectors/dkim.d.ts.map +1 -0
  66. package/dist/detectors/dkim.js +92 -0
  67. package/dist/detectors/dkim.js.map +1 -0
  68. package/dist/detectors/dnssec.d.ts +55 -0
  69. package/dist/detectors/dnssec.d.ts.map +1 -0
  70. package/dist/detectors/dnssec.js +116 -0
  71. package/dist/detectors/dnssec.js.map +1 -0
  72. package/dist/detectors/elixir.d.ts +20 -0
  73. package/dist/detectors/elixir.d.ts.map +1 -0
  74. package/dist/detectors/elixir.js +227 -0
  75. package/dist/detectors/elixir.js.map +1 -0
  76. package/dist/detectors/java.d.ts.map +1 -1
  77. package/dist/detectors/java.js +34 -4
  78. package/dist/detectors/java.js.map +1 -1
  79. package/dist/detectors/jose.d.ts +20 -0
  80. package/dist/detectors/jose.d.ts.map +1 -0
  81. package/dist/detectors/jose.js +72 -0
  82. package/dist/detectors/jose.js.map +1 -0
  83. package/dist/detectors/jwk.d.ts +25 -0
  84. package/dist/detectors/jwk.d.ts.map +1 -0
  85. package/dist/detectors/jwk.js +131 -0
  86. package/dist/detectors/jwk.js.map +1 -0
  87. package/dist/detectors/k8s.d.ts +17 -0
  88. package/dist/detectors/k8s.d.ts.map +1 -0
  89. package/dist/detectors/k8s.js +88 -0
  90. package/dist/detectors/k8s.js.map +1 -0
  91. package/dist/detectors/keystore.d.ts +19 -0
  92. package/dist/detectors/keystore.d.ts.map +1 -0
  93. package/dist/detectors/keystore.js +76 -0
  94. package/dist/detectors/keystore.js.map +1 -0
  95. package/dist/detectors/mesh.d.ts +34 -0
  96. package/dist/detectors/mesh.d.ts.map +1 -0
  97. package/dist/detectors/mesh.js +91 -0
  98. package/dist/detectors/mesh.js.map +1 -0
  99. package/dist/detectors/messaging.d.ts +16 -0
  100. package/dist/detectors/messaging.d.ts.map +1 -0
  101. package/dist/detectors/messaging.js +79 -0
  102. package/dist/detectors/messaging.js.map +1 -0
  103. package/dist/detectors/objc.d.ts +41 -0
  104. package/dist/detectors/objc.d.ts.map +1 -0
  105. package/dist/detectors/objc.js +146 -0
  106. package/dist/detectors/objc.js.map +1 -0
  107. package/dist/detectors/openpgp.d.ts +20 -0
  108. package/dist/detectors/openpgp.d.ts.map +1 -0
  109. package/dist/detectors/openpgp.js +151 -0
  110. package/dist/detectors/openpgp.js.map +1 -0
  111. package/dist/detectors/pem.d.ts.map +1 -1
  112. package/dist/detectors/pem.js +33 -1
  113. package/dist/detectors/pem.js.map +1 -1
  114. package/dist/detectors/php.d.ts +25 -0
  115. package/dist/detectors/php.d.ts.map +1 -0
  116. package/dist/detectors/php.js +171 -0
  117. package/dist/detectors/php.js.map +1 -0
  118. package/dist/detectors/pkcs11.d.ts +17 -0
  119. package/dist/detectors/pkcs11.d.ts.map +1 -0
  120. package/dist/detectors/pkcs11.js +87 -0
  121. package/dist/detectors/pkcs11.js.map +1 -0
  122. package/dist/detectors/proxy.d.ts +52 -0
  123. package/dist/detectors/proxy.d.ts.map +1 -0
  124. package/dist/detectors/proxy.js +157 -0
  125. package/dist/detectors/proxy.js.map +1 -0
  126. package/dist/detectors/pulumi.d.ts +19 -0
  127. package/dist/detectors/pulumi.d.ts.map +1 -0
  128. package/dist/detectors/pulumi.js +80 -0
  129. package/dist/detectors/pulumi.js.map +1 -0
  130. package/dist/detectors/python.d.ts.map +1 -1
  131. package/dist/detectors/python.js +70 -0
  132. package/dist/detectors/python.js.map +1 -1
  133. package/dist/detectors/rust.d.ts.map +1 -1
  134. package/dist/detectors/rust.js +73 -0
  135. package/dist/detectors/rust.js.map +1 -1
  136. package/dist/detectors/secrets.d.ts +24 -0
  137. package/dist/detectors/secrets.d.ts.map +1 -0
  138. package/dist/detectors/secrets.js +63 -0
  139. package/dist/detectors/secrets.js.map +1 -0
  140. package/dist/detectors/solidity.d.ts +50 -0
  141. package/dist/detectors/solidity.d.ts.map +1 -0
  142. package/dist/detectors/solidity.js +108 -0
  143. package/dist/detectors/solidity.js.map +1 -0
  144. package/dist/detectors/source.d.ts.map +1 -1
  145. package/dist/detectors/source.js +243 -53
  146. package/dist/detectors/source.js.map +1 -1
  147. package/dist/detectors/spire.d.ts +49 -0
  148. package/dist/detectors/spire.d.ts.map +1 -0
  149. package/dist/detectors/spire.js +77 -0
  150. package/dist/detectors/spire.js.map +1 -0
  151. package/dist/detectors/ssh-ca.d.ts +72 -0
  152. package/dist/detectors/ssh-ca.d.ts.map +1 -0
  153. package/dist/detectors/ssh-ca.js +119 -0
  154. package/dist/detectors/ssh-ca.js.map +1 -0
  155. package/dist/detectors/stateful-hbs.d.ts.map +1 -1
  156. package/dist/detectors/stateful-hbs.js +9 -6
  157. package/dist/detectors/stateful-hbs.js.map +1 -1
  158. package/dist/detectors/supply-chain.d.ts +17 -0
  159. package/dist/detectors/supply-chain.d.ts.map +1 -0
  160. package/dist/detectors/supply-chain.js +89 -0
  161. package/dist/detectors/supply-chain.js.map +1 -0
  162. package/dist/detectors/swift.d.ts +22 -0
  163. package/dist/detectors/swift.d.ts.map +1 -0
  164. package/dist/detectors/swift.js +120 -0
  165. package/dist/detectors/swift.js.map +1 -0
  166. package/dist/detectors/terraform.d.ts +21 -0
  167. package/dist/detectors/terraform.d.ts.map +1 -0
  168. package/dist/detectors/terraform.js +150 -0
  169. package/dist/detectors/terraform.js.map +1 -0
  170. package/dist/detectors/vault.d.ts +15 -0
  171. package/dist/detectors/vault.d.ts.map +1 -0
  172. package/dist/detectors/vault.js +97 -0
  173. package/dist/detectors/vault.js.map +1 -0
  174. package/dist/detectors/vpn.d.ts +22 -0
  175. package/dist/detectors/vpn.d.ts.map +1 -0
  176. package/dist/detectors/vpn.js +73 -0
  177. package/dist/detectors/vpn.js.map +1 -0
  178. package/dist/detectors/weak-hash.d.ts +71 -0
  179. package/dist/detectors/weak-hash.d.ts.map +1 -0
  180. package/dist/detectors/weak-hash.js +116 -0
  181. package/dist/detectors/weak-hash.js.map +1 -0
  182. package/dist/detectors/webauthn.d.ts +61 -0
  183. package/dist/detectors/webauthn.d.ts.map +1 -0
  184. package/dist/detectors/webauthn.js +109 -0
  185. package/dist/detectors/webauthn.js.map +1 -0
  186. package/dist/detectors/xmldsig.d.ts +23 -0
  187. package/dist/detectors/xmldsig.d.ts.map +1 -0
  188. package/dist/detectors/xmldsig.js +84 -0
  189. package/dist/detectors/xmldsig.js.map +1 -0
  190. package/dist/evidence.d.ts +79 -4
  191. package/dist/evidence.d.ts.map +1 -1
  192. package/dist/evidence.js +76 -0
  193. package/dist/evidence.js.map +1 -1
  194. package/dist/index.d.ts +17 -9
  195. package/dist/index.d.ts.map +1 -1
  196. package/dist/index.js +21 -9
  197. package/dist/index.js.map +1 -1
  198. package/dist/inventory.d.ts +0 -2
  199. package/dist/inventory.d.ts.map +1 -1
  200. package/dist/inventory.js +15 -6
  201. package/dist/inventory.js.map +1 -1
  202. package/dist/policy.d.ts +67 -0
  203. package/dist/policy.d.ts.map +1 -0
  204. package/dist/policy.js +115 -0
  205. package/dist/policy.js.map +1 -0
  206. package/dist/registry.d.ts +9 -4
  207. package/dist/registry.d.ts.map +1 -1
  208. package/dist/registry.js +81 -4
  209. package/dist/registry.js.map +1 -1
  210. package/dist/remediate-pipeline.d.ts +0 -3
  211. package/dist/remediate-pipeline.d.ts.map +1 -1
  212. package/dist/remediate-pipeline.js +23 -2
  213. package/dist/remediate-pipeline.js.map +1 -1
  214. package/dist/remediation.d.ts +10 -0
  215. package/dist/remediation.d.ts.map +1 -1
  216. package/dist/remediation.js +32 -0
  217. package/dist/remediation.js.map +1 -1
  218. package/dist/report.d.ts +9 -0
  219. package/dist/report.d.ts.map +1 -1
  220. package/dist/report.js +38 -1
  221. package/dist/report.js.map +1 -1
  222. package/dist/scan-worker.js +5 -3
  223. package/dist/scan-worker.js.map +1 -1
  224. package/dist/scan.d.ts.map +1 -1
  225. package/dist/scan.js +35 -6
  226. package/dist/scan.js.map +1 -1
  227. package/dist/standards-profiles.d.ts +58 -0
  228. package/dist/standards-profiles.d.ts.map +1 -0
  229. package/dist/standards-profiles.js +100 -0
  230. package/dist/standards-profiles.js.map +1 -0
  231. package/dist/standards.d.ts +96 -0
  232. package/dist/standards.d.ts.map +1 -0
  233. package/dist/standards.js +107 -0
  234. package/dist/standards.js.map +1 -0
  235. package/dist/types.d.ts +6 -5
  236. package/dist/types.d.ts.map +1 -1
  237. package/dist/types.js.map +1 -1
  238. package/dist/version.d.ts +1 -1
  239. package/dist/version.js +1 -1
  240. package/dist/version.js.map +1 -1
  241. package/dist/vex.d.ts +39 -0
  242. package/dist/vex.d.ts.map +1 -0
  243. package/dist/vex.js +87 -0
  244. package/dist/vex.js.map +1 -0
  245. package/dist/walk.d.ts +8 -3
  246. package/dist/walk.d.ts.map +1 -1
  247. package/dist/walk.js +32 -1
  248. package/dist/walk.js.map +1 -1
  249. package/package.json +1 -1
  250. package/src/cbom-merge.ts +146 -0
  251. package/src/cbom.ts +153 -39
  252. package/src/comments.ts +84 -39
  253. package/src/dependencies.ts +2 -1
  254. package/src/detect-utils.ts +146 -12
  255. package/src/detectors/age.ts +60 -0
  256. package/src/detectors/ansible.ts +140 -0
  257. package/src/detectors/bicep.ts +107 -0
  258. package/src/detectors/c.ts +240 -6
  259. package/src/detectors/cicd.ts +171 -0
  260. package/src/detectors/cloud-kms.ts +141 -0
  261. package/src/detectors/cloudformation.ts +250 -0
  262. package/src/detectors/codesign.ts +214 -0
  263. package/src/detectors/csharp.ts +13 -3
  264. package/src/detectors/dart.ts +237 -0
  265. package/src/detectors/database.ts +141 -0
  266. package/src/detectors/dkim.ts +172 -0
  267. package/src/detectors/dnssec.ts +195 -0
  268. package/src/detectors/elixir.ts +268 -0
  269. package/src/detectors/java.ts +39 -4
  270. package/src/detectors/jose.ts +110 -0
  271. package/src/detectors/jwk.ts +170 -0
  272. package/src/detectors/k8s.ts +115 -0
  273. package/src/detectors/keystore.ts +109 -0
  274. package/src/detectors/mesh.ts +130 -0
  275. package/src/detectors/messaging.ts +106 -0
  276. package/src/detectors/objc.ts +209 -0
  277. package/src/detectors/openpgp.ts +194 -0
  278. package/src/detectors/pem.ts +31 -1
  279. package/src/detectors/php.ts +230 -0
  280. package/src/detectors/pkcs11.ts +113 -0
  281. package/src/detectors/proxy.ts +236 -0
  282. package/src/detectors/pulumi.ts +106 -0
  283. package/src/detectors/python.ts +71 -0
  284. package/src/detectors/rust.ts +76 -0
  285. package/src/detectors/secrets.ts +103 -0
  286. package/src/detectors/solidity.ts +176 -0
  287. package/src/detectors/source.ts +283 -73
  288. package/src/detectors/spire.ts +143 -0
  289. package/src/detectors/ssh-ca.ts +215 -0
  290. package/src/detectors/stateful-hbs.ts +9 -6
  291. package/src/detectors/supply-chain.ts +117 -0
  292. package/src/detectors/swift.ts +149 -0
  293. package/src/detectors/terraform.ts +188 -0
  294. package/src/detectors/vault.ts +120 -0
  295. package/src/detectors/vpn.ts +103 -0
  296. package/src/detectors/weak-hash.ts +217 -0
  297. package/src/detectors/webauthn.ts +205 -0
  298. package/src/detectors/xmldsig.ts +115 -0
  299. package/src/evidence.ts +138 -4
  300. package/src/index.ts +50 -12
  301. package/src/inventory.ts +15 -7
  302. package/src/policy.ts +180 -0
  303. package/src/registry.ts +81 -4
  304. package/src/remediate-pipeline.ts +26 -3
  305. package/src/remediation.ts +39 -0
  306. package/src/report.ts +43 -1
  307. package/src/scan-worker.ts +5 -3
  308. package/src/scan.ts +41 -6
  309. package/src/standards-profiles.ts +136 -0
  310. package/src/standards.ts +186 -0
  311. package/src/types.ts +11 -4
  312. package/src/version.ts +1 -1
  313. package/src/vex.ts +122 -0
  314. package/src/walk.ts +33 -2
@@ -0,0 +1,150 @@
1
+ import { eachMatch, findingFromRule, hasExtension, maskBlockComments, maskCommentLines, } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ const TF_EXTENSIONS = [".tf", ".tf.json"];
4
+ // Each attribute is matched in BOTH HCL (`algorithm = "RSA"`) and the `.tf.json`
5
+ // variant (`"algorithm": "RSA"`): an optional quote around the key, and `:` or
6
+ // `=` as the separator. The `(?<![\w"-])` lookbehind stops a longer attribute
7
+ // name (`my_algorithm`) from matching on its suffix.
8
+ // RSA: tls_private_key `"RSA"` plus the Google KMS `RSA_SIGN_…` / `RSA_DECRYPT_…`.
9
+ const RE_TF_RSA = /(?<![\w"-])"?algorithm"?\s*[:=]\s*"RSA(?:_[A-Z0-9_]+)?"/g;
10
+ // ECDSA / EC signing: tls_private_key `"ECDSA"` and Google KMS `"EC_SIGN_…"`.
11
+ const RE_TF_ECDSA = /(?<![\w"-])"?algorithm"?\s*[:=]\s*"(?:ECDSA|EC_SIGN_[A-Z0-9_]+)"/g;
12
+ // EdDSA signing: hashicorp/tls `tls_private_key` `algorithm = "ED25519"` (a supported,
13
+ // classical algorithm the RSA/ECDSA rules miss).
14
+ const RE_TF_ED25519 = /(?<![\w"-])"?algorithm"?\s*[:=]\s*"ED25519"/g;
15
+ // AWS KMS key specs: the legacy `customer_master_key_spec` and the modern `key_spec`
16
+ // alias (aws provider v4+). The `(?<![\w"-])` lookbehind rejects the `key_spec` suffix
17
+ // inside `customer_master_key_spec` (preceded by `_`), so no double-count.
18
+ const RE_TF_KMS_RSA = /(?<![\w"-])"?(?:customer_master_key_spec|key_spec)"?\s*[:=]\s*"RSA_\d+"/g;
19
+ const RE_TF_KMS_EC = /(?<![\w"-])"?(?:customer_master_key_spec|key_spec)"?\s*[:=]\s*"ECC_[A-Z0-9_]+"/g;
20
+ // `key_type` key material: Azure Key Vault (`"RSA"` / `"EC"`, incl. `-HSM`) and
21
+ // HashiCorp Vault PKI, which uses the lowercase `"rsa"` / `"ec"` tokens — so the value
22
+ // is matched case-insensitively. The `"…"` bound keeps `"ec"` from matching `"ecc"`,
23
+ // and `customer_master_key_spec` (AWS) is a different attribute handled above.
24
+ const RE_TF_AZ_RSA = /(?<![\w"-])"?key_type"?\s*[:=]\s*"RSA(?:-HSM)?"/gi;
25
+ const RE_TF_AZ_EC = /(?<![\w"-])"?key_type"?\s*[:=]\s*"EC(?:-HSM)?"/gi;
26
+ const RULE_TF_RSA = {
27
+ id: "tf-rsa-key",
28
+ title: "Terraform RSA key",
29
+ description: "Terraform tls_private_key / KMS RSA key material",
30
+ category: "kem",
31
+ severity: "high",
32
+ confidence: "high",
33
+ algorithm: "RSA",
34
+ hndl: true,
35
+ cwe: CWE_BROKEN_CRYPTO,
36
+ message: "Terraform provisions a classical RSA key, which is not quantum-safe.",
37
+ remediation: "Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).",
38
+ };
39
+ const RULE_TF_ECDSA = {
40
+ id: "tf-ecdsa-key",
41
+ title: "Terraform ECDSA key",
42
+ description: "Terraform tls_private_key / KMS EC signing key",
43
+ category: "signature",
44
+ severity: "high",
45
+ confidence: "high",
46
+ algorithm: "ECDSA",
47
+ hndl: false,
48
+ cwe: CWE_BROKEN_CRYPTO,
49
+ message: "Terraform provisions a classical ECDSA key, forgeable by a quantum attacker.",
50
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
51
+ };
52
+ const RULE_TF_ED25519 = {
53
+ id: "tf-ed25519-key",
54
+ title: "Terraform Ed25519 key",
55
+ description: 'Terraform tls_private_key algorithm = "ED25519"',
56
+ category: "signature",
57
+ severity: "medium",
58
+ confidence: "high",
59
+ algorithm: "EdDSA",
60
+ hndl: false,
61
+ cwe: CWE_BROKEN_CRYPTO,
62
+ message: "Terraform provisions a classical Ed25519 key, forgeable by a quantum attacker.",
63
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
64
+ };
65
+ const RULE_TF_KMS_RSA = {
66
+ id: "tf-kms-rsa",
67
+ title: "Terraform AWS KMS RSA CMK",
68
+ description: 'Terraform aws_kms_key customer_master_key_spec = "RSA_*"',
69
+ category: "kem",
70
+ severity: "high",
71
+ confidence: "high",
72
+ algorithm: "RSA",
73
+ hndl: true,
74
+ cwe: CWE_BROKEN_CRYPTO,
75
+ message: "Terraform provisions a classical RSA KMS customer master key (harvest-now-decrypt-later exposed for encryption CMKs).",
76
+ remediation: "Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.",
77
+ };
78
+ const RULE_TF_KMS_EC = {
79
+ id: "tf-kms-ec",
80
+ title: "Terraform AWS KMS EC CMK",
81
+ description: 'Terraform aws_kms_key customer_master_key_spec = "ECC_*"',
82
+ category: "key-exchange",
83
+ severity: "high",
84
+ confidence: "high",
85
+ algorithm: "ECDH",
86
+ hndl: true,
87
+ cwe: CWE_BROKEN_CRYPTO,
88
+ message: "Terraform provisions a classical EC KMS customer master key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
89
+ remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
90
+ };
91
+ const RULE_TF_AZ_RSA = {
92
+ id: "tf-keyvault-rsa",
93
+ title: "Terraform Azure Key Vault RSA key",
94
+ description: 'Terraform azurerm_key_vault_key key_type = "RSA"',
95
+ category: "kem",
96
+ severity: "high",
97
+ confidence: "high",
98
+ algorithm: "RSA",
99
+ hndl: true,
100
+ cwe: CWE_BROKEN_CRYPTO,
101
+ message: "Terraform provisions a classical RSA Azure Key Vault key, which is not quantum-safe.",
102
+ remediation: "Plan migration to PQC (ML-KEM-768 / ML-DSA-65).",
103
+ };
104
+ const RULE_TF_AZ_EC = {
105
+ id: "tf-keyvault-ec",
106
+ title: "Terraform Azure Key Vault EC key",
107
+ description: 'Terraform azurerm_key_vault_key key_type = "EC"',
108
+ category: "key-exchange",
109
+ severity: "high",
110
+ confidence: "high",
111
+ algorithm: "ECDH",
112
+ hndl: true,
113
+ cwe: CWE_BROKEN_CRYPTO,
114
+ message: "Terraform provisions a classical EC Azure Key Vault key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
115
+ remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
116
+ };
117
+ /** Detects classical asymmetric crypto provisioned in Terraform / OpenTofu. */
118
+ export const terraformDetector = {
119
+ id: "terraform-crypto",
120
+ description: "Classical asymmetric crypto provisioned by Terraform / OpenTofu (IaC)",
121
+ scope: "config",
122
+ language: "any",
123
+ rules: [
124
+ RULE_TF_RSA,
125
+ RULE_TF_ECDSA,
126
+ RULE_TF_ED25519,
127
+ RULE_TF_KMS_RSA,
128
+ RULE_TF_KMS_EC,
129
+ RULE_TF_AZ_RSA,
130
+ RULE_TF_AZ_EC,
131
+ ],
132
+ appliesTo: (f) => hasExtension(f, TF_EXTENSIONS),
133
+ detect({ file, content }) {
134
+ const findings = [];
135
+ // A commented HCL argument (`# algorithm = "RSA"`, `//` lines, and `/* … */` block
136
+ // comments) is not an active resource argument. Mask block comments then line
137
+ // comments; offsets preserved.
138
+ const scan = maskCommentLines(maskBlockComments(content), ["#", "//"]);
139
+ const add = (re, rule) => eachMatch(re, scan, (m) => findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length })));
140
+ add(RE_TF_RSA, RULE_TF_RSA);
141
+ add(RE_TF_ECDSA, RULE_TF_ECDSA);
142
+ add(RE_TF_ED25519, RULE_TF_ED25519);
143
+ add(RE_TF_KMS_RSA, RULE_TF_KMS_RSA);
144
+ add(RE_TF_KMS_EC, RULE_TF_KMS_EC);
145
+ add(RE_TF_AZ_RSA, RULE_TF_AZ_RSA);
146
+ add(RE_TF_AZ_EC, RULE_TF_AZ_EC);
147
+ return findings;
148
+ },
149
+ };
150
+ //# sourceMappingURL=terraform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terraform.js","sourceRoot":"","sources":["../../src/detectors/terraform.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IaC detector: classical asymmetric cryptography provisioned by Terraform /\n * OpenTofu (`.tf`, `.tf.json`). Infrastructure-as-code mints real keys and CMKs\n * that never appear in application source, so this is a distinct surface from the\n * language packs.\n *\n * Covered:\n * - hashicorp/tls `tls_private_key`: `algorithm = \"RSA\" | \"ECDSA\"`\n * - Google Cloud KMS `version_template`: `algorithm = \"RSA_SIGN_…\" | \"EC_SIGN_…\"`\n * - AWS KMS `aws_kms_key`: `customer_master_key_spec = \"RSA_…\" | \"ECC_…\"`\n * - Azure `azurerm_key_vault_key`: `key_type = \"RSA\" | \"EC\"` (+ HSM variants)\n *\n * The HCL assignment forms (`algorithm = \"…\"`, `customer_master_key_spec = \"…\"`,\n * `key_type = \"…\"`) are specific enough inside `.tf` files that the false-positive\n * risk is low. EC keys are classified conservatively as key-agreement-capable\n * (hndl:true) since a provisioned EC key can feed ECDH.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport {\n eachMatch,\n findingFromRule,\n hasExtension,\n maskBlockComments,\n maskCommentLines,\n} from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst TF_EXTENSIONS: readonly string[] = [\".tf\", \".tf.json\"];\n\n// Each attribute is matched in BOTH HCL (`algorithm = \"RSA\"`) and the `.tf.json`\n// variant (`\"algorithm\": \"RSA\"`): an optional quote around the key, and `:` or\n// `=` as the separator. The `(?<![\\w\"-])` lookbehind stops a longer attribute\n// name (`my_algorithm`) from matching on its suffix.\n// RSA: tls_private_key `\"RSA\"` plus the Google KMS `RSA_SIGN_…` / `RSA_DECRYPT_…`.\nconst RE_TF_RSA = /(?<![\\w\"-])\"?algorithm\"?\\s*[:=]\\s*\"RSA(?:_[A-Z0-9_]+)?\"/g;\n// ECDSA / EC signing: tls_private_key `\"ECDSA\"` and Google KMS `\"EC_SIGN_…\"`.\nconst RE_TF_ECDSA = /(?<![\\w\"-])\"?algorithm\"?\\s*[:=]\\s*\"(?:ECDSA|EC_SIGN_[A-Z0-9_]+)\"/g;\n// EdDSA signing: hashicorp/tls `tls_private_key` `algorithm = \"ED25519\"` (a supported,\n// classical algorithm the RSA/ECDSA rules miss).\nconst RE_TF_ED25519 = /(?<![\\w\"-])\"?algorithm\"?\\s*[:=]\\s*\"ED25519\"/g;\n// AWS KMS key specs: the legacy `customer_master_key_spec` and the modern `key_spec`\n// alias (aws provider v4+). The `(?<![\\w\"-])` lookbehind rejects the `key_spec` suffix\n// inside `customer_master_key_spec` (preceded by `_`), so no double-count.\nconst RE_TF_KMS_RSA = /(?<![\\w\"-])\"?(?:customer_master_key_spec|key_spec)\"?\\s*[:=]\\s*\"RSA_\\d+\"/g;\nconst RE_TF_KMS_EC =\n /(?<![\\w\"-])\"?(?:customer_master_key_spec|key_spec)\"?\\s*[:=]\\s*\"ECC_[A-Z0-9_]+\"/g;\n// `key_type` key material: Azure Key Vault (`\"RSA\"` / `\"EC\"`, incl. `-HSM`) and\n// HashiCorp Vault PKI, which uses the lowercase `\"rsa\"` / `\"ec\"` tokens — so the value\n// is matched case-insensitively. The `\"…\"` bound keeps `\"ec\"` from matching `\"ecc\"`,\n// and `customer_master_key_spec` (AWS) is a different attribute handled above.\nconst RE_TF_AZ_RSA = /(?<![\\w\"-])\"?key_type\"?\\s*[:=]\\s*\"RSA(?:-HSM)?\"/gi;\nconst RE_TF_AZ_EC = /(?<![\\w\"-])\"?key_type\"?\\s*[:=]\\s*\"EC(?:-HSM)?\"/gi;\n\nconst RULE_TF_RSA: RuleMeta = {\n id: \"tf-rsa-key\",\n title: \"Terraform RSA key\",\n description: \"Terraform tls_private_key / KMS RSA key material\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Terraform provisions a classical RSA key, which is not quantum-safe.\",\n remediation: \"Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).\",\n};\nconst RULE_TF_ECDSA: RuleMeta = {\n id: \"tf-ecdsa-key\",\n title: \"Terraform ECDSA key\",\n description: \"Terraform tls_private_key / KMS EC signing key\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Terraform provisions a classical ECDSA key, forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_TF_ED25519: RuleMeta = {\n id: \"tf-ed25519-key\",\n title: \"Terraform Ed25519 key\",\n description: 'Terraform tls_private_key algorithm = \"ED25519\"',\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Terraform provisions a classical Ed25519 key, forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_TF_KMS_RSA: RuleMeta = {\n id: \"tf-kms-rsa\",\n title: \"Terraform AWS KMS RSA CMK\",\n description: 'Terraform aws_kms_key customer_master_key_spec = \"RSA_*\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Terraform provisions a classical RSA KMS customer master key (harvest-now-decrypt-later exposed for encryption CMKs).\",\n remediation: \"Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.\",\n};\nconst RULE_TF_KMS_EC: RuleMeta = {\n id: \"tf-kms-ec\",\n title: \"Terraform AWS KMS EC CMK\",\n description: 'Terraform aws_kms_key customer_master_key_spec = \"ECC_*\"',\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Terraform provisions a classical EC KMS customer master key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).\",\n remediation:\n \"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_TF_AZ_RSA: RuleMeta = {\n id: \"tf-keyvault-rsa\",\n title: \"Terraform Azure Key Vault RSA key\",\n description: 'Terraform azurerm_key_vault_key key_type = \"RSA\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Terraform provisions a classical RSA Azure Key Vault key, which is not quantum-safe.\",\n remediation: \"Plan migration to PQC (ML-KEM-768 / ML-DSA-65).\",\n};\nconst RULE_TF_AZ_EC: RuleMeta = {\n id: \"tf-keyvault-ec\",\n title: \"Terraform Azure Key Vault EC key\",\n description: 'Terraform azurerm_key_vault_key key_type = \"EC\"',\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Terraform provisions a classical EC Azure Key Vault key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).\",\n remediation:\n \"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).\",\n};\n\n/** Detects classical asymmetric crypto provisioned in Terraform / OpenTofu. */\nexport const terraformDetector: Detector = {\n id: \"terraform-crypto\",\n description: \"Classical asymmetric crypto provisioned by Terraform / OpenTofu (IaC)\",\n scope: \"config\",\n language: \"any\",\n rules: [\n RULE_TF_RSA,\n RULE_TF_ECDSA,\n RULE_TF_ED25519,\n RULE_TF_KMS_RSA,\n RULE_TF_KMS_EC,\n RULE_TF_AZ_RSA,\n RULE_TF_AZ_EC,\n ],\n appliesTo: (f) => hasExtension(f, TF_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n // A commented HCL argument (`# algorithm = \"RSA\"`, `//` lines, and `/* … */` block\n // comments) is not an active resource argument. Mask block comments then line\n // comments; offsets preserved.\n const scan = maskCommentLines(maskBlockComments(content), [\"#\", \"//\"]);\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, scan, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_TF_RSA, RULE_TF_RSA);\n add(RE_TF_ECDSA, RULE_TF_ECDSA);\n add(RE_TF_ED25519, RULE_TF_ED25519);\n add(RE_TF_KMS_RSA, RULE_TF_KMS_RSA);\n add(RE_TF_KMS_EC, RULE_TF_KMS_EC);\n add(RE_TF_AZ_RSA, RULE_TF_AZ_RSA);\n add(RE_TF_AZ_EC, RULE_TF_AZ_EC);\n return findings;\n },\n};\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Config detector: classical asymmetric keys in native HashiCorp Vault server
3
+ * config / policy (HCL). Terraform-provisioned Vault is caught by the terraform
4
+ * detector (`.tf`); this covers Vault's OWN `.hcl` config — the `transit` and
5
+ * `pki` secrets engines, whose keys sign and wrap real production data.
6
+ *
7
+ * Covered (gated to a `transit`/`pki` marker to avoid firing on unrelated HCL):
8
+ * - transit key `type = "rsa-2048|3072|4096"` (RSA — key wrapping / signing).
9
+ * - transit key `type = "ecdsa-p256|p384|p521"` / `"ed25519"` (classical signing).
10
+ * - pki role `key_type = "rsa" | "ec"`.
11
+ */
12
+ import type { Detector } from "../types.js";
13
+ /** Detects classical asymmetric keys in native Vault HCL config. */
14
+ export declare const vaultDetector: Detector;
15
+ //# sourceMappingURL=vault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/detectors/vault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAkF/D,oEAAoE;AACpE,eAAO,MAAM,aAAa,EAAE,QAyB3B,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { eachMatch, findingFromRule, hasExtension, maskCommentLines } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ const HCL_EXTENSIONS = [".hcl"];
4
+ const RE_TRANSIT_RSA = /\btype\s*=\s*"rsa-\d+"/g;
5
+ const RE_TRANSIT_ECDSA = /\btype\s*=\s*"ecdsa-p\d+"/g;
6
+ const RE_TRANSIT_ED25519 = /\btype\s*=\s*"ed25519"/g;
7
+ const RE_PKI_RSA = /\bkey_type\s*=\s*"rsa"/g;
8
+ const RE_PKI_EC = /\bkey_type\s*=\s*"ec"/g;
9
+ const RULE_TRANSIT_RSA = {
10
+ id: "vault-transit-rsa",
11
+ title: "Vault transit RSA key",
12
+ description: 'Vault transit secrets engine key type = "rsa-*"',
13
+ category: "kem",
14
+ severity: "high",
15
+ confidence: "high",
16
+ algorithm: "RSA",
17
+ hndl: true,
18
+ cwe: CWE_BROKEN_CRYPTO,
19
+ message: "Vault transit provisions a classical RSA key (signing / key wrapping), not quantum-safe.",
20
+ remediation: "Plan migration to PQC (ML-KEM-768 for wrapping, ML-DSA-65 for signing) as Vault adds support.",
21
+ };
22
+ const RULE_TRANSIT_ECDSA = {
23
+ id: "vault-transit-ecdsa",
24
+ title: "Vault transit ECDSA key",
25
+ description: 'Vault transit secrets engine key type = "ecdsa-p*"',
26
+ category: "signature",
27
+ severity: "medium",
28
+ confidence: "high",
29
+ algorithm: "ECDSA",
30
+ hndl: false,
31
+ cwe: CWE_BROKEN_CRYPTO,
32
+ message: "Vault transit provisions a classical ECDSA signing key, forgeable by a quantum attacker.",
33
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
34
+ };
35
+ const RULE_TRANSIT_ED25519 = {
36
+ id: "vault-transit-ed25519",
37
+ title: "Vault transit Ed25519 key",
38
+ description: 'Vault transit secrets engine key type = "ed25519"',
39
+ category: "signature",
40
+ severity: "low",
41
+ confidence: "high",
42
+ algorithm: "EdDSA",
43
+ hndl: false,
44
+ cwe: CWE_BROKEN_CRYPTO,
45
+ message: "Vault transit provisions a classical Ed25519 signing key, forgeable by a quantum attacker.",
46
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
47
+ };
48
+ const RULE_PKI_RSA = {
49
+ id: "vault-pki-rsa",
50
+ title: "Vault PKI RSA role",
51
+ description: 'Vault pki secrets engine role key_type = "rsa"',
52
+ category: "certificate",
53
+ severity: "medium",
54
+ confidence: "high",
55
+ algorithm: "RSA",
56
+ hndl: true,
57
+ cwe: CWE_BROKEN_CRYPTO,
58
+ message: "Vault PKI mints certificates with a classical RSA key, not quantum-safe.",
59
+ remediation: "Plan migration to PQC certificate keys (ML-DSA-65) as the CA chain adds support.",
60
+ };
61
+ const RULE_PKI_EC = {
62
+ id: "vault-pki-ec",
63
+ title: "Vault PKI EC role",
64
+ description: 'Vault pki secrets engine role key_type = "ec"',
65
+ category: "certificate",
66
+ severity: "medium",
67
+ confidence: "high",
68
+ algorithm: "ECDSA",
69
+ hndl: false,
70
+ cwe: CWE_BROKEN_CRYPTO,
71
+ message: "Vault PKI mints certificates with a classical EC key, forgeable by a quantum attacker.",
72
+ remediation: "Plan migration to ML-DSA-65 (FIPS 204) certificate keys.",
73
+ };
74
+ /** Detects classical asymmetric keys in native Vault HCL config. */
75
+ export const vaultDetector = {
76
+ id: "vault-crypto",
77
+ description: "Classical asymmetric keys in native HashiCorp Vault config (transit, pki)",
78
+ scope: "config",
79
+ language: "any",
80
+ rules: [RULE_TRANSIT_RSA, RULE_TRANSIT_ECDSA, RULE_TRANSIT_ED25519, RULE_PKI_RSA, RULE_PKI_EC],
81
+ appliesTo: (f) => hasExtension(f, HCL_EXTENSIONS),
82
+ detect({ file, content }) {
83
+ // Gate: only inside a Vault transit / pki context.
84
+ if (!content.includes("transit") && !content.includes("pki"))
85
+ return [];
86
+ const scan = maskCommentLines(content, ["#", "//"]);
87
+ const findings = [];
88
+ const add = (re, rule) => eachMatch(re, scan, (m) => findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length })));
89
+ add(RE_TRANSIT_RSA, RULE_TRANSIT_RSA);
90
+ add(RE_TRANSIT_ECDSA, RULE_TRANSIT_ECDSA);
91
+ add(RE_TRANSIT_ED25519, RULE_TRANSIT_ED25519);
92
+ add(RE_PKI_RSA, RULE_PKI_RSA);
93
+ add(RE_PKI_EC, RULE_PKI_EC);
94
+ return findings;
95
+ },
96
+ };
97
+ //# sourceMappingURL=vault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.js","sourceRoot":"","sources":["../../src/detectors/vault.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: classical asymmetric keys in native HashiCorp Vault server\n * config / policy (HCL). Terraform-provisioned Vault is caught by the terraform\n * detector (`.tf`); this covers Vault's OWN `.hcl` config — the `transit` and\n * `pki` secrets engines, whose keys sign and wrap real production data.\n *\n * Covered (gated to a `transit`/`pki` marker to avoid firing on unrelated HCL):\n * - transit key `type = \"rsa-2048|3072|4096\"` (RSA — key wrapping / signing).\n * - transit key `type = \"ecdsa-p256|p384|p521\"` / `\"ed25519\"` (classical signing).\n * - pki role `key_type = \"rsa\" | \"ec\"`.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension, maskCommentLines } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst HCL_EXTENSIONS: readonly string[] = [\".hcl\"];\n\nconst RE_TRANSIT_RSA = /\\btype\\s*=\\s*\"rsa-\\d+\"/g;\nconst RE_TRANSIT_ECDSA = /\\btype\\s*=\\s*\"ecdsa-p\\d+\"/g;\nconst RE_TRANSIT_ED25519 = /\\btype\\s*=\\s*\"ed25519\"/g;\nconst RE_PKI_RSA = /\\bkey_type\\s*=\\s*\"rsa\"/g;\nconst RE_PKI_EC = /\\bkey_type\\s*=\\s*\"ec\"/g;\n\nconst RULE_TRANSIT_RSA: RuleMeta = {\n id: \"vault-transit-rsa\",\n title: \"Vault transit RSA key\",\n description: 'Vault transit secrets engine key type = \"rsa-*\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Vault transit provisions a classical RSA key (signing / key wrapping), not quantum-safe.\",\n remediation:\n \"Plan migration to PQC (ML-KEM-768 for wrapping, ML-DSA-65 for signing) as Vault adds support.\",\n};\nconst RULE_TRANSIT_ECDSA: RuleMeta = {\n id: \"vault-transit-ecdsa\",\n title: \"Vault transit ECDSA key\",\n description: 'Vault transit secrets engine key type = \"ecdsa-p*\"',\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Vault transit provisions a classical ECDSA signing key, forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_TRANSIT_ED25519: RuleMeta = {\n id: \"vault-transit-ed25519\",\n title: \"Vault transit Ed25519 key\",\n description: 'Vault transit secrets engine key type = \"ed25519\"',\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Vault transit provisions a classical Ed25519 signing key, forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_PKI_RSA: RuleMeta = {\n id: \"vault-pki-rsa\",\n title: \"Vault PKI RSA role\",\n description: 'Vault pki secrets engine role key_type = \"rsa\"',\n category: \"certificate\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Vault PKI mints certificates with a classical RSA key, not quantum-safe.\",\n remediation: \"Plan migration to PQC certificate keys (ML-DSA-65) as the CA chain adds support.\",\n};\nconst RULE_PKI_EC: RuleMeta = {\n id: \"vault-pki-ec\",\n title: \"Vault PKI EC role\",\n description: 'Vault pki secrets engine role key_type = \"ec\"',\n category: \"certificate\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Vault PKI mints certificates with a classical EC key, forgeable by a quantum attacker.\",\n remediation: \"Plan migration to ML-DSA-65 (FIPS 204) certificate keys.\",\n};\n\n/** Detects classical asymmetric keys in native Vault HCL config. */\nexport const vaultDetector: Detector = {\n id: \"vault-crypto\",\n description: \"Classical asymmetric keys in native HashiCorp Vault config (transit, pki)\",\n scope: \"config\",\n language: \"any\",\n rules: [RULE_TRANSIT_RSA, RULE_TRANSIT_ECDSA, RULE_TRANSIT_ED25519, RULE_PKI_RSA, RULE_PKI_EC],\n appliesTo: (f) => hasExtension(f, HCL_EXTENSIONS),\n detect({ file, content }): Finding[] {\n // Gate: only inside a Vault transit / pki context.\n if (!content.includes(\"transit\") && !content.includes(\"pki\")) return [];\n const scan = maskCommentLines(content, [\"#\", \"//\"]);\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, scan, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_TRANSIT_RSA, RULE_TRANSIT_RSA);\n add(RE_TRANSIT_ECDSA, RULE_TRANSIT_ECDSA);\n add(RE_TRANSIT_ED25519, RULE_TRANSIT_ED25519);\n add(RE_PKI_RSA, RULE_PKI_RSA);\n add(RE_PKI_EC, RULE_PKI_EC);\n return findings;\n },\n};\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Config detector: classical key exchange in network transport / VPN config —
3
+ * WireGuard and IPsec (strongSwan). These are the tunnels carrying "communication
4
+ * between things"; their key exchange is classical and so harvest-now-decrypt-later
5
+ * exposed (a recorded tunnel is decryptable once a CRQC exists).
6
+ *
7
+ * Covered (each gated so it only fires inside the relevant config):
8
+ * - **WireGuard** — `[Interface]`/`[Peer]` sections with `PrivateKey`/`PublicKey`.
9
+ * WireGuard's Noise handshake is Curve25519 with NO standard PQC option, so this
10
+ * is an especially sharp finding: it cannot be made quantum-safe without a
11
+ * tunnel-wrapping layer. The private key is treated as sensitive key material.
12
+ * - **IPsec / strongSwan** — IKE/ESP proposals naming classical DH groups
13
+ * (`modp*` = finite-field DH, `ecp*` = ECDH).
14
+ *
15
+ * `sshd_config` / `ssh_config` KexAlgorithms are intentionally NOT handled here —
16
+ * the language-agnostic SSH token detector in `source.ts` (`ssh-kex-classical`)
17
+ * already covers them; a rule here would double-count.
18
+ */
19
+ import type { Detector } from "../types.js";
20
+ /** Detects classical key exchange in WireGuard / IPsec network config. */
21
+ export declare const vpnDetector: Detector;
22
+ //# sourceMappingURL=vpn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vpn.d.ts","sourceRoot":"","sources":["../../src/detectors/vpn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAwD/D,0EAA0E;AAC1E,eAAO,MAAM,WAAW,EAAE,QA2BzB,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { eachMatch, findingFromRule, hasExtension, maskCommentLines } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ const CONF_EXTENSIONS = [".conf"];
4
+ const RE_WG_KEY = /\b(?:PrivateKey|PublicKey)\s*=\s*[A-Za-z0-9+/]{42,}=/g;
5
+ const RE_IPSEC_MODP = /\bmodp\d+\b/gi;
6
+ const RE_IPSEC_ECP = /\becp\d+(?:bp)?\b/gi;
7
+ const RULE_WG = {
8
+ id: "net-wireguard-x25519",
9
+ title: "WireGuard Curve25519 key",
10
+ description: "WireGuard [Interface]/[Peer] key — Curve25519 Noise handshake (no PQC option)",
11
+ category: "key-exchange",
12
+ severity: "high",
13
+ confidence: "high",
14
+ algorithm: "X25519",
15
+ hndl: true,
16
+ cwe: CWE_BROKEN_CRYPTO,
17
+ sensitive: true,
18
+ message: "WireGuard tunnel keyed by classical Curve25519 (Noise); WireGuard has no standard post-quantum KEM, so the tunnel is harvest-now-decrypt-later exposed until wrapped by a PQC layer.",
19
+ remediation: "Wrap the tunnel in a PQC-hybrid transport (e.g. a TLS 1.3 X25519MLKEM768 layer) or track WireGuard PQC proposals; rotate keys when available.",
20
+ };
21
+ const RULE_IPSEC_DH = {
22
+ id: "net-ipsec-modp-dh",
23
+ title: "IPsec classical DH group (modp)",
24
+ description: "IPsec/strongSwan IKE/ESP proposal names a finite-field DH group (modp*)",
25
+ category: "key-exchange",
26
+ severity: "medium",
27
+ confidence: "high",
28
+ algorithm: "DH",
29
+ hndl: true,
30
+ cwe: CWE_BROKEN_CRYPTO,
31
+ message: "IPsec proposal uses a classical finite-field Diffie-Hellman group (modp*); the tunnel key exchange is harvest-now-decrypt-later exposed.",
32
+ remediation: "Add a PQC/hybrid IKE proposal (ML-KEM) as your IPsec stack supports it.",
33
+ };
34
+ const RULE_IPSEC_EC = {
35
+ id: "net-ipsec-ecp-ecdh",
36
+ title: "IPsec classical ECDH group (ecp)",
37
+ description: "IPsec/strongSwan IKE/ESP proposal names an elliptic-curve DH group (ecp*)",
38
+ category: "key-exchange",
39
+ severity: "medium",
40
+ confidence: "high",
41
+ algorithm: "ECDH",
42
+ hndl: true,
43
+ cwe: CWE_BROKEN_CRYPTO,
44
+ message: "IPsec proposal uses a classical elliptic-curve Diffie-Hellman group (ecp*); the tunnel key exchange is harvest-now-decrypt-later exposed.",
45
+ remediation: "Add a PQC/hybrid IKE proposal (X25519MLKEM768 / ML-KEM) as your IPsec stack supports it.",
46
+ };
47
+ /** Detects classical key exchange in WireGuard / IPsec network config. */
48
+ export const vpnDetector = {
49
+ id: "network-transport-crypto",
50
+ description: "Classical key exchange in network transport / VPN config (WireGuard, IPsec)",
51
+ scope: "config",
52
+ language: "any",
53
+ rules: [RULE_WG, RULE_IPSEC_DH, RULE_IPSEC_EC],
54
+ appliesTo: (f) => hasExtension(f, CONF_EXTENSIONS),
55
+ detect({ file, content }) {
56
+ const findings = [];
57
+ const push = (rule, index, length) => findings.push(findingFromRule(rule, { file, content, index, matchLength: length }));
58
+ // ipsec.conf / wg configs use `#` line comments. Match over comment-masked
59
+ // content (offsets preserved); gates stay on the original.
60
+ const scan = maskCommentLines(content, ["#"]);
61
+ // WireGuard: gated to a WireGuard config section.
62
+ if (content.includes("[Interface]") || content.includes("[Peer]")) {
63
+ eachMatch(RE_WG_KEY, scan, (m) => push(RULE_WG, m.index, m[0].length));
64
+ }
65
+ // IPsec / strongSwan: gated to a proposal assignment.
66
+ if (/\b(?:ike|esp|proposals?|keyexchange)\s*=/i.test(content)) {
67
+ eachMatch(RE_IPSEC_MODP, scan, (m) => push(RULE_IPSEC_DH, m.index, m[0].length));
68
+ eachMatch(RE_IPSEC_ECP, scan, (m) => push(RULE_IPSEC_EC, m.index, m[0].length));
69
+ }
70
+ return findings;
71
+ },
72
+ };
73
+ //# sourceMappingURL=vpn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vpn.js","sourceRoot":"","sources":["../../src/detectors/vpn.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: classical key exchange in network transport / VPN config —\n * WireGuard and IPsec (strongSwan). These are the tunnels carrying \"communication\n * between things\"; their key exchange is classical and so harvest-now-decrypt-later\n * exposed (a recorded tunnel is decryptable once a CRQC exists).\n *\n * Covered (each gated so it only fires inside the relevant config):\n * - **WireGuard** — `[Interface]`/`[Peer]` sections with `PrivateKey`/`PublicKey`.\n * WireGuard's Noise handshake is Curve25519 with NO standard PQC option, so this\n * is an especially sharp finding: it cannot be made quantum-safe without a\n * tunnel-wrapping layer. The private key is treated as sensitive key material.\n * - **IPsec / strongSwan** — IKE/ESP proposals naming classical DH groups\n * (`modp*` = finite-field DH, `ecp*` = ECDH).\n *\n * `sshd_config` / `ssh_config` KexAlgorithms are intentionally NOT handled here —\n * the language-agnostic SSH token detector in `source.ts` (`ssh-kex-classical`)\n * already covers them; a rule here would double-count.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension, maskCommentLines } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst CONF_EXTENSIONS: readonly string[] = [\".conf\"];\n\nconst RE_WG_KEY = /\\b(?:PrivateKey|PublicKey)\\s*=\\s*[A-Za-z0-9+/]{42,}=/g;\nconst RE_IPSEC_MODP = /\\bmodp\\d+\\b/gi;\nconst RE_IPSEC_ECP = /\\becp\\d+(?:bp)?\\b/gi;\n\nconst RULE_WG: RuleMeta = {\n id: \"net-wireguard-x25519\",\n title: \"WireGuard Curve25519 key\",\n description: \"WireGuard [Interface]/[Peer] key — Curve25519 Noise handshake (no PQC option)\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n sensitive: true,\n message:\n \"WireGuard tunnel keyed by classical Curve25519 (Noise); WireGuard has no standard post-quantum KEM, so the tunnel is harvest-now-decrypt-later exposed until wrapped by a PQC layer.\",\n remediation:\n \"Wrap the tunnel in a PQC-hybrid transport (e.g. a TLS 1.3 X25519MLKEM768 layer) or track WireGuard PQC proposals; rotate keys when available.\",\n};\nconst RULE_IPSEC_DH: RuleMeta = {\n id: \"net-ipsec-modp-dh\",\n title: \"IPsec classical DH group (modp)\",\n description: \"IPsec/strongSwan IKE/ESP proposal names a finite-field DH group (modp*)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"IPsec proposal uses a classical finite-field Diffie-Hellman group (modp*); the tunnel key exchange is harvest-now-decrypt-later exposed.\",\n remediation: \"Add a PQC/hybrid IKE proposal (ML-KEM) as your IPsec stack supports it.\",\n};\nconst RULE_IPSEC_EC: RuleMeta = {\n id: \"net-ipsec-ecp-ecdh\",\n title: \"IPsec classical ECDH group (ecp)\",\n description: \"IPsec/strongSwan IKE/ESP proposal names an elliptic-curve DH group (ecp*)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"IPsec proposal uses a classical elliptic-curve Diffie-Hellman group (ecp*); the tunnel key exchange is harvest-now-decrypt-later exposed.\",\n remediation:\n \"Add a PQC/hybrid IKE proposal (X25519MLKEM768 / ML-KEM) as your IPsec stack supports it.\",\n};\n\n/** Detects classical key exchange in WireGuard / IPsec network config. */\nexport const vpnDetector: Detector = {\n id: \"network-transport-crypto\",\n description: \"Classical key exchange in network transport / VPN config (WireGuard, IPsec)\",\n scope: \"config\",\n language: \"any\",\n rules: [RULE_WG, RULE_IPSEC_DH, RULE_IPSEC_EC],\n appliesTo: (f) => hasExtension(f, CONF_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const push = (rule: RuleMeta, index: number, length: number) =>\n findings.push(findingFromRule(rule, { file, content, index, matchLength: length }));\n // ipsec.conf / wg configs use `#` line comments. Match over comment-masked\n // content (offsets preserved); gates stay on the original.\n const scan = maskCommentLines(content, [\"#\"]);\n\n // WireGuard: gated to a WireGuard config section.\n if (content.includes(\"[Interface]\") || content.includes(\"[Peer]\")) {\n eachMatch(RE_WG_KEY, scan, (m) => push(RULE_WG, m.index, m[0].length));\n }\n\n // IPsec / strongSwan: gated to a proposal assignment.\n if (/\\b(?:ike|esp|proposals?|keyexchange)\\s*=/i.test(content)) {\n eachMatch(RE_IPSEC_MODP, scan, (m) => push(RULE_IPSEC_DH, m.index, m[0].length));\n eachMatch(RE_IPSEC_ECP, scan, (m) => push(RULE_IPSEC_EC, m.index, m[0].length));\n }\n return findings;\n },\n};\n"]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Config/source detector: weak hash functions (SHA-1, MD5) used specifically in
3
+ * a DIGITAL-SIGNATURE or X.509 CERTIFICATE algorithm — NOT generic hashing and
4
+ * NOT password hashing.
5
+ *
6
+ * WHY THIS LIVES IN A PQC-READINESS TOOL (quantum-adjacent, not quantum-broken).
7
+ * SHA-1/MD5 are not "quantum-broken" — Grover only halves preimage strength and
8
+ * a CRQC does not conjure signature-hash collisions. We flag them anyway, and
9
+ * frame them as *quantum-adjacent: same migration window*:
10
+ * - NIST SP 800-131A Rev 3 disallows SHA-1 for generating digital signatures
11
+ * and retires SHA-1 entirely by 2030 — the SAME transition window the same
12
+ * document sets for the classical→PQC migration. One standard, one deadline.
13
+ * - CNSA 2.0 pairs its PQC mandate (ML-KEM / ML-DSA) with SHA-384/512; a
14
+ * CNSA-2.0 target that re-keys to PQC must also drop SHA-1 from its
15
+ * signature/certificate chain in the same effort.
16
+ * - A quantum-readiness migration RE-KEYS and RE-SIGNS every certificate and
17
+ * signature anyway. Surfacing weak signature hashes in that same pass costs
18
+ * nothing extra and closes a today-exploitable gap (SHA-1/MD5 signature
19
+ * forgery via chosen-prefix collisions) alongside the future one.
20
+ * We deliberately do NOT claim these hashes are broken by quantum computers.
21
+ *
22
+ * WHY THE SCOPE IS NARROW (signature/certificate ONLY).
23
+ * A bare `sha1(x)` or `sha1sum file` is often a perfectly acceptable
24
+ * non-security checksum (ETags, cache keys, content addressing, Git object ids).
25
+ * PASSWORD hashing with a weak/fast hash is a real weakness too, but it is a
26
+ * DIFFERENT weakness (CWE-916, needs a slow KDF like Argon2/bcrypt/scrypt), a
27
+ * different remediation, and — crucially — is NOT on the PQC migration path, so
28
+ * it does not belong in this detector. We only fire when the weak hash is bound
29
+ * to a signature/certificate algorithm, where "move to SHA-256+ while you re-key
30
+ * for PQC" is exactly the right, single remediation. That binding is enforced by
31
+ * a file-level fast-reject marker (see `hasSignatureMarker`) plus regexes that
32
+ * only match signature/cert *algorithm identifiers*, never a lone hash call.
33
+ *
34
+ * SURFACES COVERED (all verified signature/certificate contexts):
35
+ * 1. Java JCA standard names — `Signature.getInstance("SHA1withRSA")`,
36
+ * `"MD5withRSA"`, `"SHA1withDSA"`, `"SHA1withECDSA"` (JDK Standard Algorithm
37
+ * Names). Regex anchors on the `…with(RSA|DSA|ECDSA)` shape, so the token is
38
+ * unambiguously a signature algorithm, never a bare digest.
39
+ * 2. .NET signing calls — a weak hash passed to `.SignData(…)` / `.SignHash(…)`
40
+ * as `HashAlgorithmName.SHA1` or the legacy string `"SHA1"` (e.g.
41
+ * `RSACryptoServiceProvider.SignData(data, "SHA1")`). Matched only INSIDE the
42
+ * sign call, so an unrelated `HashAlgorithmName.SHA1` used for a checksum is
43
+ * not flagged.
44
+ * 3. X.509 / ASN.1 signature-algorithm identifiers — the OID *names*
45
+ * (`sha1WithRSAEncryption`, `md5WithRSAEncryption`, `ecdsa-with-SHA1`,
46
+ * `dsaWithSHA1`) and their dotted OIDs (`1.2.840.113549.1.1.5` =
47
+ * sha1WithRSAEncryption, `1.2.840.113549.1.1.4` = md5WithRSAEncryption,
48
+ * `1.2.840.10045.4.1` = ecdsa-with-SHA1). These are certificate
49
+ * signatureAlgorithm values by definition.
50
+ * 4. OpenSSL CLI in a signing/cert context — `-sha1` / `-md5` on the same line
51
+ * as `openssl` AND a certificate/signing subcommand or flag
52
+ * (`req` / `x509` / `ca` / `-sign` / `-signkey`). A bare `openssl dgst -sha1
53
+ * file` (a checksum, no `-sign`) is intentionally NOT matched.
54
+ *
55
+ * NOT DUPLICATED: XML-DSig `rsa-sha1` (see `xmldsig.ts`) and DKIM `rsa-sha1`
56
+ * (see `dkim.ts`) already emit SIGNATURE findings on their own surfaces; this
57
+ * detector deliberately does not touch those and focuses on the Java / .NET /
58
+ * X.509 / OpenSSL surfaces above.
59
+ *
60
+ * Category is `hash` (weak/pre-quantum hash usage — the previously-unused
61
+ * category). `hndl: false` — a signature has no confidentiality to harvest.
62
+ * `algorithm: "unknown"` — the weak *hash* is not itself an asymmetric family;
63
+ * the signature it weakens is (RSA/ECDSA/DSA), but the finding is about the hash.
64
+ */
65
+ import type { Detector } from "../types.js";
66
+ /**
67
+ * Detects SHA-1 / MD5 used specifically in signature or certificate algorithms
68
+ * (Java JCA, .NET signing calls, X.509 OID names/OIDs, OpenSSL cert/signing CLI).
69
+ */
70
+ export declare const weakHashDetector: Detector;
71
+ //# sourceMappingURL=weak-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weak-hash.d.ts","sourceRoot":"","sources":["../../src/detectors/weak-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAsH/D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,QA8B9B,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension, maskBlockComments, maskCommentLines, } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ // --- SHA-1 in a signature/certificate algorithm identifier ---------------------
4
+ // Java JCA standard names: `SHA1withRSA`, `SHA1withDSA`, `SHA1withECDSA`. The
5
+ // `…with(RSA|DSA|ECDSA)` shape makes the token unambiguously a signature alg.
6
+ // `-?` tolerates the `SHA-1` spelling; `i` covers case variation in real code.
7
+ const RE_SHA1_JAVA = /\bSHA-?1with(?:RSA|DSA|ECDSA)\b/gi;
8
+ // X.509 / ASN.1 signature-algorithm OID *names*.
9
+ const RE_SHA1_X509 = /\b(?:sha-?1WithRSAEncryption|ecdsa-with-SHA-?1|dsaWithSHA-?1)\b/gi;
10
+ // X.509 signature-algorithm dotted OIDs (sha1WithRSAEncryption, ecdsa-with-SHA1).
11
+ // Lookarounds keep the id from matching inside a longer OID.
12
+ const RE_SHA1_OID = /(?<![\d.])(?:1\.2\.840\.113549\.1\.1\.5|1\.2\.840\.10045\.4\.1)(?![\d.])/g;
13
+ // OpenSSL CLI: `-sha1` on the same line as `openssl` AND an actual SIGNING marker
14
+ // (`req` / `ca` subcommand, or `-sign`/`-signkey`/`-CA`/`-CAkey`), in either order.
15
+ // A bare `x509` subcommand is NOT a signing marker — `openssl x509 -fingerprint
16
+ // -sha1` / `-subject_hash` are read-only thumbprint/identifier ops, not signature
17
+ // generation (audit M1). `openssl dgst -sha1 file` (checksum) is likewise unmatched.
18
+ const OPENSSL_SIGN = String.raw `(?:\breq\b|\bca\b|-sign(?:key)?\b|-CA(?:key)?\b)`;
19
+ const RE_SHA1_OPENSSL = new RegExp(`openssl\\b[^\\n]*?${OPENSSL_SIGN}[^\\n]*?-sha1\\b|openssl\\b[^\\n]*?-sha1\\b[^\\n]*?${OPENSSL_SIGN}`, "gi");
20
+ // .NET: a weak hash bound to a signing call (`.SignData(…)` / `.SignHash(…)`), as
21
+ // `HashAlgorithmName.SHA1` or the legacy `"SHA1"` string arg. Bounded by `;` (the
22
+ // statement), NOT `)` — the first argument is often itself a call
23
+ // (`.SignData(Encoding.UTF8.GetBytes(m), HashAlgorithmName.SHA1, …)`), so a
24
+ // `[^)]` span would stop at the inner `)` and miss it (audit H3).
25
+ const RE_SHA1_DOTNET = /\.Sign(?:Data|Hash)\s*\([^;]*?(?:HashAlgorithmName\.SHA-?1|["']SHA-?1["'])/gi;
26
+ // --- MD5 in a signature/certificate algorithm identifier -----------------------
27
+ const RE_MD5_JAVA = /\bMD5with(?:RSA|DSA|ECDSA)\b/gi;
28
+ const RE_MD5_X509 = /\bmd5WithRSAEncryption\b/gi;
29
+ const RE_MD5_OID = /(?<![\d.])1\.2\.840\.113549\.1\.1\.4(?![\d.])/g;
30
+ const RE_MD5_OPENSSL = new RegExp(`openssl\\b[^\\n]*?${OPENSSL_SIGN}[^\\n]*?-md5\\b|openssl\\b[^\\n]*?-md5\\b[^\\n]*?${OPENSSL_SIGN}`, "gi");
31
+ const RE_MD5_DOTNET = /\.Sign(?:Data|Hash)\s*\([^;]*?(?:HashAlgorithmName\.MD5|["']MD5["'])/gi;
32
+ const RULE_SHA1 = {
33
+ id: "weak-hash-sha1-signature",
34
+ title: "SHA-1 in a signature / certificate algorithm",
35
+ description: "SHA-1 used in a digital-signature or X.509 certificate algorithm (Java/.NET/X.509/OpenSSL)",
36
+ category: "hash",
37
+ severity: "medium",
38
+ confidence: "high",
39
+ algorithm: "unknown",
40
+ hndl: false,
41
+ cwe: CWE_BROKEN_CRYPTO,
42
+ message: "SHA-1 used in a digital-signature/certificate algorithm; SHA-1 is disallowed for signatures (NIST SP 800-131A Rev 3, fully retired 2030 — the same window as the PQC migration) and enables collision-based signature forgery. Quantum-adjacent: migrate the hash alongside the signature algorithm.",
43
+ remediation: "Move signature/certificate hashes to SHA-256 or stronger (SHA-384/512 for CNSA 2.0 targets) and re-issue affected certificates; fold the PQC signature migration (ML-DSA) into the same re-key/re-sign effort.",
44
+ };
45
+ const RULE_MD5 = {
46
+ id: "weak-hash-md5-signature",
47
+ title: "MD5 in a signature / certificate algorithm",
48
+ description: "MD5 used in a digital-signature or X.509 certificate algorithm (Java/.NET/X.509/OpenSSL)",
49
+ category: "hash",
50
+ severity: "high",
51
+ confidence: "high",
52
+ algorithm: "unknown",
53
+ hndl: false,
54
+ cwe: CWE_BROKEN_CRYPTO,
55
+ message: "MD5 used in a digital-signature/certificate algorithm; MD5 signatures are catastrophically broken today (practical chosen-prefix collisions have forged CA certificates) and must be replaced immediately. Quantum-adjacent: migrate the hash alongside the signature algorithm in the PQC re-sign pass.",
56
+ remediation: "Replace MD5 signature/certificate hashes with SHA-256 or stronger (SHA-384/512 for CNSA 2.0 targets) and re-issue affected certificates immediately; fold the PQC signature migration (ML-DSA) into the same re-key/re-sign effort.",
57
+ };
58
+ const WEAK_HASH_RULES = [
59
+ {
60
+ meta: RULE_SHA1,
61
+ res: [RE_SHA1_JAVA, RE_SHA1_X509, RE_SHA1_OID, RE_SHA1_OPENSSL, RE_SHA1_DOTNET],
62
+ },
63
+ { meta: RULE_MD5, res: [RE_MD5_JAVA, RE_MD5_X509, RE_MD5_OID, RE_MD5_OPENSSL, RE_MD5_DOTNET] },
64
+ ];
65
+ /**
66
+ * True when `content` carries a signature/certificate marker — a signal that any
67
+ * weak hash present is bound to a signature or cert, not a bare checksum. This is
68
+ * the guard that keeps `sha1(x)` / `sha1sum` / a password hash from ever firing:
69
+ * without one of these markers the file is not scanned at all.
70
+ */
71
+ function hasSignatureMarker(content) {
72
+ if (/with\s?RSA|with\s?DSA|with\s?ECDSA|WithRSAEncryption|ecdsa-with-SHA-?1|dsaWithSHA-?1/i.test(content)) {
73
+ return true;
74
+ }
75
+ if (/1\.2\.840\.113549\.1\.1\.[45]|1\.2\.840\.10045\.4\.1/.test(content))
76
+ return true;
77
+ if (/Signature\.getInstance|\.Sign(?:Data|Hash)\s*\(/.test(content))
78
+ return true;
79
+ // OpenSSL only counts as a marker with a cert/signing subcommand or flag.
80
+ if (/openssl/i.test(content) && /\b(?:req|x509|ca|-signkey|-sign|dgst)\b/i.test(content)) {
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ /**
86
+ * Detects SHA-1 / MD5 used specifically in signature or certificate algorithms
87
+ * (Java JCA, .NET signing calls, X.509 OID names/OIDs, OpenSSL cert/signing CLI).
88
+ */
89
+ export const weakHashDetector = {
90
+ id: "weak-hash-signature",
91
+ description: "Weak hash (SHA-1/MD5) in a digital-signature or X.509 certificate algorithm",
92
+ scope: "config",
93
+ language: "any",
94
+ rules: WEAK_HASH_RULES.map((r) => r.meta),
95
+ // Skip prose/docs: a page explaining `SHA1withRSA` is not live config/code.
96
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
97
+ detect({ file, content }) {
98
+ // Fast reject on the ORIGINAL content: only scan files whose weak hash is
99
+ // bound to a signature/cert context. A lone `sha1(...)` checksum or a
100
+ // password hash has no such marker and is skipped wholesale.
101
+ if (!hasSignatureMarker(content))
102
+ return [];
103
+ // Mask C-style block comments and `//` / `#` / `;` line comments so a
104
+ // commented-out algorithm can't fire. Offsets are preserved, so finding
105
+ // line/column/snippet for the live lines stay exact.
106
+ const scan = maskCommentLines(maskBlockComments(content), ["//", "#", ";"]);
107
+ const findings = [];
108
+ for (const { meta, res } of WEAK_HASH_RULES) {
109
+ for (const re of res) {
110
+ eachMatch(re, scan, (m) => findings.push(findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length })));
111
+ }
112
+ }
113
+ return findings;
114
+ },
115
+ };
116
+ //# sourceMappingURL=weak-hash.js.map