@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,215 @@
1
+ /**
2
+ * Config detector: classical SSH **certificate-authority** cryptography — the
3
+ * OpenSSH certificate mechanism, in which a CA key signs host/user certificates
4
+ * (`*-cert-v01@openssh.com`) that servers and clients then trust in place of
5
+ * bare public keys.
6
+ *
7
+ * This is DIFFERENT from the `ssh-kex-classical` rule in `source.ts`, and from
8
+ * the `ssh-public-key` rule there — deliberately non-overlapping:
9
+ * - `ssh-kex-classical` is about the SSH *key-exchange* (finite-field DH, ECDH,
10
+ * curve25519): session-key *agreement*, `category: "key-exchange"`,
11
+ * `hndl: true` — a passively-recorded handshake is decryptable once a CRQC
12
+ * exists (harvest-now-decrypt-later confidentiality loss).
13
+ * - `ssh-public-key` flags bare `ssh-rsa AAAA…` / `ecdsa-sha2-* …` key lines.
14
+ * - THIS detector flags the SSH **certificate / CA signing** surface: the
15
+ * `*-cert-v01@openssh.com` cert key types, the `@cert-authority` known_hosts
16
+ * marker, the `TrustedUserCAKeys` / `HostCertificate` sshd directives, and
17
+ * `ssh-keygen -s` (the command that signs a certificate with a CA key).
18
+ *
19
+ * WHY it matters for PQC: an SSH certificate is a *signature* made by a CA key
20
+ * over a principal's public key + validity constraints. There is no
21
+ * confidentiality to harvest (so `hndl: false`), but every such signature
22
+ * becomes FORGEABLE the moment a cryptographically-relevant quantum computer
23
+ * (CRQC) can recover the classical CA private key from its public key. Forging a
24
+ * CA signature lets an attacker mint host or user certificates that every relying
25
+ * party accepts — a full trust-root compromise. SSH CA keys are also unusually
26
+ * long-lived (they are the anchor an entire fleet trusts and are rotated
27
+ * rarely), so the exposure window is large. Hence `category: "signature"`,
28
+ * `severity: "medium"`, `confidence: "high"`.
29
+ *
30
+ * Two match shapes:
31
+ * 1. The cert key type, which NAMES the signing algorithm → an accurate
32
+ * {@link AlgorithmFamily}:
33
+ * - `ssh-rsa-cert-v01@openssh.com`, `rsa-sha2-256/512-cert-v01@openssh.com` → RSA
34
+ * - `ecdsa-sha2-nistp256/384/521-cert-v01@openssh.com` → ECDSA
35
+ * - `ssh-ed25519-cert-v01@openssh.com` → EdDSA
36
+ * 2. The CA *deployment* directives — `TrustedUserCAKeys` / `HostCertificate`
37
+ * (sshd_config) and `ssh-keygen -s` (the CA signing command). These are the
38
+ * MOST COMMON way an SSH CA is configured, but they don't name the algorithm
39
+ * (that lives in the referenced key file), so they emit the `ssh-ca-config`
40
+ * rule with `algorithm: "unknown"` and `confidence: "medium"`.
41
+ *
42
+ * Precision:
43
+ * - Fast reject: `detect()` bails unless the file carries an SSH-CA marker
44
+ * (`cert-v01@openssh.com`, `@cert-authority`, `TrustedUserCAKeys`,
45
+ * `HostCertificate`, or `ssh-keygen -s`) — so the algorithm tokens can't be
46
+ * reached on unrelated config.
47
+ * - The cert-type tokens are the full `*-cert-v01@openssh.com` strings. The
48
+ * trailing `-cert-v01@openssh.com` suffix distinguishes a CA certificate type
49
+ * from the plain `ssh-rsa` / `ssh-ed25519` public-key tokens handled by
50
+ * `ssh-public-key`, so a non-cert `ssh-ed25519 AAAA…` line NEVER fires a cert
51
+ * rule here.
52
+ * - Comment lines (`#`) are masked with {@link maskCommentLines} (offsets
53
+ * preserved) so a commented-out cert line can't fire. Known_hosts
54
+ * `@cert-authority` lines start with `@`, not `#`, so masking leaves them
55
+ * live — exactly what we want.
56
+ * - Doc/prose files are excluded ({@link DOC_EXTENSIONS}), and so are program
57
+ * SOURCE files ({@link ANALYZABLE_SOURCE_EXTENSIONS}) — a vendored SSH library
58
+ * or constants table that spells a cert type as a string literal is not live
59
+ * CA config. SSH CA config lives in extensionless / `.conf` / `.pub` files.
60
+ *
61
+ * The matched line frequently carries the CA public key's base64 blob (a
62
+ * `@cert-authority` known_hosts entry), so findings are marked `sensitive` and
63
+ * reporters drop the snippet, consistent with `ssh-public-key`.
64
+ */
65
+ import type { Detector, Finding, RuleMeta } from "../types.js";
66
+ import {
67
+ eachMatch,
68
+ findingFromRule,
69
+ hasExtension,
70
+ maskCommentLines,
71
+ DOC_EXTENSIONS,
72
+ ANALYZABLE_SOURCE_EXTENSIONS,
73
+ } from "../detect-utils.js";
74
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
75
+
76
+ // --- OpenSSH certificate key types (`*-cert-v01@openssh.com`), by signing family. ---
77
+ // The `\b` after `.com` is a real boundary (`m` is a word char); the leading `\b`
78
+ // keeps `ssh-rsa-cert-v01@…` from matching inside a longer token. RSA covers the
79
+ // legacy `ssh-rsa` cert type plus the SHA-2 RSA cert types (`rsa-sha2-256/512`)
80
+ // OpenSSH emits for RSA CA/host certificates.
81
+ const RE_CERT_RSA = /\b(?:ssh-rsa|rsa-sha2-(?:256|512))-cert-v01@openssh\.com\b/g;
82
+ const RE_CERT_ECDSA = /\becdsa-sha2-nistp(?:256|384|521)-cert-v01@openssh\.com\b/g;
83
+ const RE_CERT_EDDSA = /\bssh-ed25519-cert-v01@openssh\.com\b/g;
84
+ // The canonical CA *deployment* directives — the most common way an SSH CA is
85
+ // configured (sshd_config `TrustedUserCAKeys` / `HostCertificate`, and the CA
86
+ // signing command `ssh-keygen -s`). These name the CA usage but not the algorithm
87
+ // (that lives in the referenced key file), so the finding is `algorithm: "unknown"`.
88
+ const RE_CA_DIRECTIVE = /\bTrustedUserCAKeys\b|\bHostCertificate\b|\bssh-keygen\s+-s\b/g;
89
+
90
+ const REMEDIATION =
91
+ "No post-quantum SSH certificate format is standardized yet — track OpenSSH release notes and IETF work on PQC signatures for SSH. SSH CA keys are long-lived trust roots, so plan for their rotation to a PQC signing algorithm (e.g. ML-DSA) as soon as a cert format lands, and keep validity periods short in the interim.";
92
+
93
+ const RULE_CA_RSA: RuleMeta = {
94
+ id: "ssh-ca-rsa-cert",
95
+ title: "SSH certificate authority — RSA signing",
96
+ description: "OpenSSH RSA certificate key type (ssh-rsa/rsa-sha2-*-cert-v01@openssh.com)",
97
+ category: "signature",
98
+ severity: "medium",
99
+ confidence: "high",
100
+ algorithm: "RSA",
101
+ hndl: false,
102
+ cwe: CWE_BROKEN_CRYPTO,
103
+ sensitive: true,
104
+ message:
105
+ "SSH certificate uses a classical RSA key type (ssh-rsa/rsa-sha2-*-cert-v01@openssh.com); the CA signature is forgeable once a CRQC can recover the RSA CA key.",
106
+ remediation: REMEDIATION,
107
+ };
108
+ const RULE_CA_ECDSA: RuleMeta = {
109
+ id: "ssh-ca-ecdsa-cert",
110
+ title: "SSH certificate authority — ECDSA signing",
111
+ description: "OpenSSH ECDSA certificate key type (ecdsa-sha2-nistp*-cert-v01@openssh.com)",
112
+ category: "signature",
113
+ severity: "medium",
114
+ confidence: "high",
115
+ algorithm: "ECDSA",
116
+ hndl: false,
117
+ cwe: CWE_BROKEN_CRYPTO,
118
+ sensitive: true,
119
+ message:
120
+ "SSH certificate uses a classical ECDSA key type (ecdsa-sha2-nistp*-cert-v01@openssh.com); the CA signature is forgeable once a CRQC can recover the ECDSA CA key.",
121
+ remediation: REMEDIATION,
122
+ };
123
+ const RULE_CA_EDDSA: RuleMeta = {
124
+ id: "ssh-ca-ed25519-cert",
125
+ title: "SSH certificate authority — EdDSA signing",
126
+ description: "OpenSSH Ed25519 certificate key type (ssh-ed25519-cert-v01@openssh.com)",
127
+ category: "signature",
128
+ severity: "medium",
129
+ confidence: "high",
130
+ algorithm: "EdDSA",
131
+ hndl: false,
132
+ cwe: CWE_BROKEN_CRYPTO,
133
+ sensitive: true,
134
+ message:
135
+ "SSH certificate uses a classical Ed25519 key type (ssh-ed25519-cert-v01@openssh.com); modern but still classical — the CA signature is forgeable once a CRQC can recover the Ed25519 CA key.",
136
+ remediation: REMEDIATION,
137
+ };
138
+
139
+ const RULE_CA_CONFIG: RuleMeta = {
140
+ id: "ssh-ca-config",
141
+ title: "SSH certificate authority configured",
142
+ description:
143
+ "OpenSSH CA deployment directive (TrustedUserCAKeys / HostCertificate / ssh-keygen -s)",
144
+ category: "signature",
145
+ severity: "medium",
146
+ // The directive names CA usage, not the algorithm — hence a lower confidence and
147
+ // an unknown family; the CA key file itself carries the specific algorithm.
148
+ confidence: "medium",
149
+ algorithm: "unknown",
150
+ hndl: false,
151
+ cwe: CWE_BROKEN_CRYPTO,
152
+ message:
153
+ "SSH certificate authority is configured (TrustedUserCAKeys / HostCertificate / ssh-keygen -s); the CA signing key is a long-lived classical trust root whose signatures become forgeable once a CRQC exists — verify the CA key's algorithm.",
154
+ remediation: REMEDIATION,
155
+ };
156
+
157
+ interface SshCaRule {
158
+ meta: RuleMeta;
159
+ re: RegExp;
160
+ }
161
+
162
+ const SSH_CA_RULES: readonly SshCaRule[] = [
163
+ { meta: RULE_CA_RSA, re: RE_CERT_RSA },
164
+ { meta: RULE_CA_ECDSA, re: RE_CERT_ECDSA },
165
+ { meta: RULE_CA_EDDSA, re: RE_CERT_EDDSA },
166
+ { meta: RULE_CA_CONFIG, re: RE_CA_DIRECTIVE },
167
+ ];
168
+
169
+ /** True when `content` carries some SSH-CA-specific marker (not just a bare token). */
170
+ function hasSshCaMarker(content: string): boolean {
171
+ return (
172
+ content.includes("cert-v01@openssh.com") ||
173
+ content.includes("@cert-authority") ||
174
+ content.includes("TrustedUserCAKeys") ||
175
+ content.includes("HostCertificate") ||
176
+ content.includes("ssh-keygen -s")
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Detects classical SSH certificate-authority signing (OpenSSH cert key types)
182
+ * in SSH config, sshd_config, and known_hosts files. Distinct from SSH
183
+ * key-exchange (`ssh-kex-classical`): this is the certificate SIGNATURE surface.
184
+ */
185
+ export const sshCaDetector: Detector = {
186
+ id: "ssh-ca",
187
+ description: "Classical SSH certificate-authority signing (OpenSSH *-cert-v01@openssh.com)",
188
+ scope: "config",
189
+ language: "any",
190
+ rules: SSH_CA_RULES.map((r) => r.meta),
191
+ // Apply broadly (SSH CA config lives in many differently-named / extensionless
192
+ // files: sshd_config, known_hosts, *.pub) but never on prose/docs OR on program
193
+ // SOURCE files — a vendored SSH library or a constants table that spells out
194
+ // `ssh-ed25519-cert-v01@openssh.com` as a string literal is not a live CA config.
195
+ // The strict fast-reject in detect() is the remaining gate.
196
+ appliesTo: (f) =>
197
+ !hasExtension(f, DOC_EXTENSIONS) && !hasExtension(f, ANALYZABLE_SOURCE_EXTENSIONS),
198
+ detect({ file, content }): Finding[] {
199
+ if (!hasSshCaMarker(content)) return [];
200
+
201
+ // Mask `#` comment lines so a commented-out cert line can't fire; offsets are
202
+ // preserved. `@cert-authority` known_hosts lines start with `@`, not `#`, so
203
+ // they are intentionally left live.
204
+ const scan = maskCommentLines(content, ["#"]);
205
+ const findings: Finding[] = [];
206
+ for (const { meta, re } of SSH_CA_RULES) {
207
+ eachMatch(re, scan, (m) =>
208
+ findings.push(
209
+ findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length }),
210
+ ),
211
+ );
212
+ }
213
+ return findings;
214
+ },
215
+ };
@@ -39,8 +39,9 @@ interface HbsRule {
39
39
 
40
40
  const HBS_RULES: HbsRule[] = [
41
41
  {
42
- // LMS parameter set, e.g. LMS_SHA256_M32_H10 (RFC 8554 / SP 800-208).
43
- re: /\bLMS_SHA256_[MN]\d+_[HW]\d+\b/g,
42
+ // LMS parameter set, e.g. LMS_SHA256_M32_H10 / LMS_SHAKE_M24_H10 (SP 800-208
43
+ // adds SHAKE256 and the 192-bit M24/N24 sets to RFC 8554's SHA-256 sets).
44
+ re: /\bLMS_(?:SHA256|SHAKE(?:256)?)_[MN]\d+_[HW]\d+\b/g,
44
45
  meta: {
45
46
  id: "stateful-hbs-lms-param",
46
47
  title: "LMS parameter set (stateful hash-based signature)",
@@ -93,8 +94,9 @@ const HBS_RULES: HbsRule[] = [
93
94
  },
94
95
  },
95
96
  {
96
- // XMSS parameter set, e.g. XMSS-SHA2_10_256 (RFC 8391 / SP 800-208).
97
- re: /\bXMSS-SHA2_\d+_256\b/g,
97
+ // XMSS parameter set, e.g. XMSS-SHA2_10_256 / XMSS-SHAKE256_10_192 (SP 800-208
98
+ // adds the SHAKE256 and 192-bit variants to RFC 8391's SHA-2/256 sets).
99
+ re: /\bXMSS-(?:SHA2|SHAKE(?:256)?)_\d+_(?:192|256)\b/g,
98
100
  meta: {
99
101
  id: "stateful-hbs-xmss-param",
100
102
  title: "XMSS parameter set (stateful hash-based signature)",
@@ -111,8 +113,9 @@ const HBS_RULES: HbsRule[] = [
111
113
  },
112
114
  },
113
115
  {
114
- // XMSSMT (multi-tree XMSS) parameter set, e.g. XMSSMT-SHA2_20/2_256.
115
- re: /\bXMSSMT-SHA2_\d+\b/g,
116
+ // XMSSMT (multi-tree XMSS) parameter set, e.g. XMSSMT-SHA2_20/2_256 or the
117
+ // SP 800-208 SHAKE256 variant XMSSMT-SHAKE256_20/2_256.
118
+ re: /\bXMSSMT-(?:SHA2|SHAKE(?:256)?)_\d+\b/g,
116
119
  meta: {
117
120
  id: "stateful-hbs-xmssmt-param",
118
121
  title: "XMSSMT parameter set (stateful hash-based signature)",
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Config detector: classical supply-chain / artifact signing beyond the cosign +
3
+ * GPG cases the `cicd` detector covers — Docker Content Trust (Notary v1 / TUF),
4
+ * CNCF Notation, and in-toto. These sign container images, OCI artifacts, and
5
+ * build provenance with classical keys (ECDSA / RSA / Ed25519). Like all signing,
6
+ * this is the signature-side analogue of harvest-now-decrypt-later: a signature
7
+ * produced today is forgeable once a CRQC exists, and the signed artifact keeps
8
+ * verifying against that classical key for years — so these are `hndl:false` but
9
+ * real quantum-migration debt.
10
+ *
11
+ * Gated to CI pipeline files, Dockerfiles, and shell scripts, over comment-masked
12
+ * content, and keyed on distinctive command/env tokens to keep false positives low.
13
+ */
14
+ import type { Detector, Finding, RuleMeta } from "../types.js";
15
+ import { eachMatch, findingFromRule, maskCommentLines } from "../detect-utils.js";
16
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
17
+
18
+ /** CI pipeline files, Dockerfiles, and shell scripts — where signing runs. */
19
+ function isSigningContext(filePath: string): boolean {
20
+ const lower = filePath.toLowerCase();
21
+ const base = lower.split("/").pop() ?? lower;
22
+ return (
23
+ (lower.includes(".github/workflows/") && (lower.endsWith(".yml") || lower.endsWith(".yaml"))) ||
24
+ base === ".gitlab-ci.yml" ||
25
+ lower.endsWith(".gitlab-ci.yml") ||
26
+ base === "jenkinsfile" ||
27
+ lower.endsWith(".jenkinsfile") ||
28
+ base === "dockerfile" ||
29
+ base.startsWith("dockerfile.") ||
30
+ lower.endsWith(".dockerfile") ||
31
+ lower.endsWith(".sh") ||
32
+ lower.endsWith(".bash")
33
+ );
34
+ }
35
+
36
+ interface ScRule {
37
+ re: RegExp;
38
+ meta: RuleMeta;
39
+ }
40
+
41
+ const SC_RULES: ScRule[] = [
42
+ {
43
+ re: /\bDOCKER_CONTENT_TRUST\s*[:=]\s*["']?1\b|\bdocker\s+trust\s+sign\b/g,
44
+ meta: {
45
+ id: "sc-docker-content-trust",
46
+ title: "Docker Content Trust signing (Notary v1)",
47
+ description: "Docker Content Trust / docker trust sign — Notary v1 (TUF) classical keys",
48
+ category: "signature",
49
+ severity: "medium",
50
+ confidence: "high",
51
+ algorithm: "ECDSA",
52
+ hndl: false,
53
+ cwe: CWE_BROKEN_CRYPTO,
54
+ message:
55
+ "Docker Content Trust signs images with Notary v1 (TUF) classical keys (ECDSA/Ed25519); signatures are forgeable once a CRQC exists.",
56
+ remediation: "Track sigstore/Notary v2 (Notation) PQC roadmap; plan hybrid image signing.",
57
+ },
58
+ },
59
+ {
60
+ re: /\bnotation\s+(?:sign|key\s+generate|cert\s+generate)\b/g,
61
+ meta: {
62
+ id: "sc-notation-sign",
63
+ title: "Notation artifact signing",
64
+ description: "CNCF Notation signing of OCI artifacts with classical keys",
65
+ category: "signature",
66
+ severity: "medium",
67
+ confidence: "high",
68
+ algorithm: "RSA",
69
+ hndl: false,
70
+ cwe: CWE_BROKEN_CRYPTO,
71
+ message:
72
+ "Notation signs OCI artifacts with a classical key (RSA/ECDSA); signatures are forgeable once a CRQC exists.",
73
+ remediation: "Track Notation's PQC signature support (ML-DSA) and plan migration.",
74
+ },
75
+ },
76
+ {
77
+ re: /\bin[-_]toto[-_]?run\b|\bin-toto\b/g,
78
+ meta: {
79
+ id: "sc-in-toto",
80
+ title: "in-toto supply-chain signing",
81
+ description: "in-toto build provenance signed with classical keys",
82
+ category: "signature",
83
+ severity: "medium",
84
+ confidence: "high",
85
+ algorithm: "RSA",
86
+ hndl: false,
87
+ cwe: CWE_BROKEN_CRYPTO,
88
+ message:
89
+ "in-toto signs build provenance with a classical key (RSA/Ed25519); the attestation is forgeable once a CRQC exists.",
90
+ remediation: "Track in-toto/DSSE PQC signature support and plan migration.",
91
+ },
92
+ },
93
+ ];
94
+
95
+ /** Detects classical Docker Content Trust / Notation / in-toto signing. */
96
+ export const supplyChainDetector: Detector = {
97
+ id: "supply-chain-signing",
98
+ description: "Classical container/artifact signing (Docker Content Trust, Notation, in-toto)",
99
+ scope: "config",
100
+ language: "any",
101
+ rules: SC_RULES.map((r) => r.meta),
102
+ appliesTo: isSigningContext,
103
+ detect({ file, content }): Finding[] {
104
+ // `#` covers YAML/Dockerfile/shell; `//` covers Jenkinsfile (Groovy), which has no
105
+ // extension so it is never centrally comment-stripped.
106
+ const scan = maskCommentLines(content, ["#", "//"]);
107
+ const findings: Finding[] = [];
108
+ for (const rule of SC_RULES) {
109
+ eachMatch(rule.re, scan, (m) => {
110
+ findings.push(
111
+ findingFromRule(rule.meta, { file, content, index: m.index, matchLength: m[0].length }),
112
+ );
113
+ });
114
+ }
115
+ return findings;
116
+ },
117
+ };
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Source detector for classical asymmetric cryptography in Swift (Apple platforms) —
3
+ * Apple CryptoKit and the older Security framework (`SecKey*`). Lexical, same strategy
4
+ * as the other language packs; the API names are distinctive enough to keep the
5
+ * false-positive risk low. Comment/string suppression is handled centrally
6
+ * (`.swift` is a C-style comment language).
7
+ *
8
+ * CryptoKit:
9
+ * - `P256|P384|P521.Signing.PrivateKey` → ECDSA signature (hndl:false)
10
+ * - `P256|P384|P521.KeyAgreement.PrivateKey` → ECDH key agreement (hndl:true)
11
+ * - `Curve25519.Signing.PrivateKey` → Ed25519 signature (hndl:false)
12
+ * - `Curve25519.KeyAgreement.PrivateKey` → X25519 key agreement (hndl:true)
13
+ * (`SecureEnclave.P256.…` forms are covered by the same P-curve regexes.)
14
+ *
15
+ * Security framework:
16
+ * - `SecKeyCreateRandomKey` with `kSecAttrKeyTypeRSA` → RSA
17
+ * - … with `kSecAttrKeyTypeEC` / `kSecAttrKeyTypeECSECPrimeRandom` → EC
18
+ */
19
+ import type { Detector, Finding, RuleMeta } from "../types.js";
20
+ import { SWIFT_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
21
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
22
+
23
+ // CryptoKit P-curve keys, split by usage (Signing vs KeyAgreement). `SecureEnclave.`
24
+ // may prefix the P-curve type, so it is allowed optionally.
25
+ const RE_SWIFT_P_SIGN = /\b(?:SecureEnclave\.)?P(?:256|384|521)\.Signing\.PrivateKey\b/g;
26
+ const RE_SWIFT_P_KEX = /\b(?:SecureEnclave\.)?P(?:256|384|521)\.KeyAgreement\.PrivateKey\b/g;
27
+ // CryptoKit Curve25519: Signing = Ed25519 (EdDSA); KeyAgreement = X25519.
28
+ const RE_SWIFT_ED25519 = /\bCurve25519\.Signing\.PrivateKey\b/g;
29
+ const RE_SWIFT_X25519 = /\bCurve25519\.KeyAgreement\.PrivateKey\b/g;
30
+ // Security framework key-type attributes (used with SecKeyCreateRandomKey).
31
+ const RE_SWIFT_SEC_RSA = /\bkSecAttrKeyTypeRSA\b/g;
32
+ const RE_SWIFT_SEC_EC = /\bkSecAttrKeyType(?:EC|ECSECPrimeRandom)\b/g;
33
+
34
+ const RULE_SWIFT_ECDSA: RuleMeta = {
35
+ id: "swift-ecdsa",
36
+ title: "Swift CryptoKit ECDSA signing key",
37
+ description: "CryptoKit P256/P384/P521 Signing.PrivateKey",
38
+ category: "signature",
39
+ severity: "high",
40
+ confidence: "high",
41
+ algorithm: "ECDSA",
42
+ hndl: false,
43
+ cwe: CWE_BROKEN_CRYPTO,
44
+ message: "CryptoKit P-curve ECDSA signing (Swift) is forgeable by a quantum attacker.",
45
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
46
+ };
47
+ const RULE_SWIFT_ECDH: RuleMeta = {
48
+ id: "swift-ecdh",
49
+ title: "Swift CryptoKit ECDH key agreement",
50
+ description: "CryptoKit P256/P384/P521 KeyAgreement.PrivateKey",
51
+ category: "key-exchange",
52
+ severity: "high",
53
+ confidence: "high",
54
+ algorithm: "ECDH",
55
+ hndl: true,
56
+ cwe: CWE_BROKEN_CRYPTO,
57
+ message: "CryptoKit P-curve ECDH key agreement (Swift) is harvest-now-decrypt-later exposed.",
58
+ remediation: "Migrate key agreement to hybrid X25519MLKEM768 (ML-KEM-768).",
59
+ };
60
+ const RULE_SWIFT_ED25519: RuleMeta = {
61
+ id: "swift-ed25519",
62
+ title: "Swift CryptoKit Ed25519 signing key",
63
+ description: "CryptoKit Curve25519.Signing.PrivateKey (Ed25519)",
64
+ category: "signature",
65
+ // `low`, aligned with Ed25519 in every other source pack (go/rust/ruby/python/node)
66
+ // — the same primitive must not flip CI exit codes based on which language wrote it.
67
+ severity: "low",
68
+ confidence: "high",
69
+ algorithm: "EdDSA",
70
+ hndl: false,
71
+ cwe: CWE_BROKEN_CRYPTO,
72
+ message: "CryptoKit Ed25519 signing (Swift) is classical and forgeable by a quantum attacker.",
73
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
74
+ };
75
+ const RULE_SWIFT_X25519: RuleMeta = {
76
+ id: "swift-x25519",
77
+ title: "Swift CryptoKit X25519 key agreement",
78
+ description: "CryptoKit Curve25519.KeyAgreement.PrivateKey (X25519)",
79
+ category: "key-exchange",
80
+ severity: "medium",
81
+ confidence: "high",
82
+ algorithm: "X25519",
83
+ hndl: true,
84
+ cwe: CWE_BROKEN_CRYPTO,
85
+ message:
86
+ "CryptoKit X25519 key agreement (Swift) is classical and harvest-now-decrypt-later exposed.",
87
+ remediation: "Migrate key agreement to hybrid X25519MLKEM768 (ML-KEM-768).",
88
+ };
89
+ const RULE_SWIFT_RSA: RuleMeta = {
90
+ id: "swift-rsa",
91
+ title: "Swift Security-framework RSA key",
92
+ description: "SecKeyCreateRandomKey with kSecAttrKeyTypeRSA",
93
+ category: "kem",
94
+ severity: "high",
95
+ confidence: "high",
96
+ algorithm: "RSA",
97
+ hndl: true,
98
+ cwe: CWE_BROKEN_CRYPTO,
99
+ message: "Security-framework RSA key (Swift) is classical and not quantum-safe.",
100
+ remediation: "Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).",
101
+ };
102
+ const RULE_SWIFT_SEC_EC: RuleMeta = {
103
+ id: "swift-sec-ec",
104
+ title: "Swift Security-framework EC key",
105
+ description: "SecKeyCreateRandomKey with kSecAttrKeyTypeEC",
106
+ category: "key-exchange",
107
+ severity: "high",
108
+ confidence: "high",
109
+ algorithm: "ECDH",
110
+ hndl: true,
111
+ cwe: CWE_BROKEN_CRYPTO,
112
+ message:
113
+ "Security-framework EC key (Swift); EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
114
+ remediation:
115
+ "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
116
+ };
117
+
118
+ /** Detects classical asymmetric crypto in Swift (CryptoKit + Security framework). */
119
+ export const swiftDetector: Detector = {
120
+ id: "swift-crypto",
121
+ description: "Classical asymmetric crypto in Swift (CryptoKit, Security framework)",
122
+ scope: "source",
123
+ language: "swift",
124
+ rules: [
125
+ RULE_SWIFT_ECDSA,
126
+ RULE_SWIFT_ECDH,
127
+ RULE_SWIFT_ED25519,
128
+ RULE_SWIFT_X25519,
129
+ RULE_SWIFT_RSA,
130
+ RULE_SWIFT_SEC_EC,
131
+ ],
132
+ appliesTo: (f) => hasExtension(f, SWIFT_EXTENSIONS),
133
+ detect({ file, content }): Finding[] {
134
+ const findings: Finding[] = [];
135
+ const add = (re: RegExp, rule: RuleMeta) =>
136
+ eachMatch(re, content, (m) =>
137
+ findings.push(
138
+ findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
139
+ ),
140
+ );
141
+ add(RE_SWIFT_P_SIGN, RULE_SWIFT_ECDSA);
142
+ add(RE_SWIFT_P_KEX, RULE_SWIFT_ECDH);
143
+ add(RE_SWIFT_ED25519, RULE_SWIFT_ED25519);
144
+ add(RE_SWIFT_X25519, RULE_SWIFT_X25519);
145
+ add(RE_SWIFT_SEC_RSA, RULE_SWIFT_RSA);
146
+ add(RE_SWIFT_SEC_EC, RULE_SWIFT_SEC_EC);
147
+ return findings;
148
+ },
149
+ };