@quantakrypto/core 0.4.2 → 0.4.4

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 (223) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +38 -10
  3. package/dist/agent-types.js.map +1 -1
  4. package/dist/baseline.js.map +1 -1
  5. package/dist/cache.js.map +1 -1
  6. package/dist/cbom-merge.d.ts +9 -0
  7. package/dist/cbom-merge.d.ts.map +1 -0
  8. package/dist/cbom-merge.js +99 -0
  9. package/dist/cbom-merge.js.map +1 -0
  10. package/dist/cbom.js.map +1 -1
  11. package/dist/changed.js.map +1 -1
  12. package/dist/codemods/config-toggle.js.map +1 -1
  13. package/dist/codemods/registry.js.map +1 -1
  14. package/dist/comments.d.ts +29 -2
  15. package/dist/comments.d.ts.map +1 -1
  16. package/dist/comments.js +178 -18
  17. package/dist/comments.js.map +1 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/cwe.js.map +1 -1
  20. package/dist/dependencies.d.ts +3 -2
  21. package/dist/dependencies.d.ts.map +1 -1
  22. package/dist/dependencies.js +138 -2
  23. package/dist/dependencies.js.map +1 -1
  24. package/dist/detect-utils.d.ts +19 -4
  25. package/dist/detect-utils.d.ts.map +1 -1
  26. package/dist/detect-utils.js +43 -6
  27. package/dist/detect-utils.js.map +1 -1
  28. package/dist/detectors/c.d.ts +4 -2
  29. package/dist/detectors/c.d.ts.map +1 -1
  30. package/dist/detectors/c.js +382 -3
  31. package/dist/detectors/c.js.map +1 -1
  32. package/dist/detectors/cicd.d.ts +27 -0
  33. package/dist/detectors/cicd.d.ts.map +1 -0
  34. package/dist/detectors/cicd.js +116 -0
  35. package/dist/detectors/cicd.js.map +1 -0
  36. package/dist/detectors/cloud-kms.d.ts +21 -0
  37. package/dist/detectors/cloud-kms.d.ts.map +1 -0
  38. package/dist/detectors/cloud-kms.js +57 -0
  39. package/dist/detectors/cloud-kms.js.map +1 -0
  40. package/dist/detectors/cloudformation.d.ts +30 -0
  41. package/dist/detectors/cloudformation.d.ts.map +1 -0
  42. package/dist/detectors/cloudformation.js +170 -0
  43. package/dist/detectors/cloudformation.js.map +1 -0
  44. package/dist/detectors/csharp.d.ts.map +1 -1
  45. package/dist/detectors/csharp.js +141 -3
  46. package/dist/detectors/csharp.js.map +1 -1
  47. package/dist/detectors/database.d.ts +17 -0
  48. package/dist/detectors/database.d.ts.map +1 -0
  49. package/dist/detectors/database.js +60 -0
  50. package/dist/detectors/database.js.map +1 -0
  51. package/dist/detectors/dnssec.d.ts +55 -0
  52. package/dist/detectors/dnssec.d.ts.map +1 -0
  53. package/dist/detectors/dnssec.js +107 -0
  54. package/dist/detectors/dnssec.js.map +1 -0
  55. package/dist/detectors/elixir.d.ts +20 -0
  56. package/dist/detectors/elixir.d.ts.map +1 -0
  57. package/dist/detectors/elixir.js +195 -0
  58. package/dist/detectors/elixir.js.map +1 -0
  59. package/dist/detectors/go.d.ts +5 -3
  60. package/dist/detectors/go.d.ts.map +1 -1
  61. package/dist/detectors/go.js +187 -3
  62. package/dist/detectors/go.js.map +1 -1
  63. package/dist/detectors/java.d.ts.map +1 -1
  64. package/dist/detectors/java.js +169 -4
  65. package/dist/detectors/java.js.map +1 -1
  66. package/dist/detectors/jose.d.ts +20 -0
  67. package/dist/detectors/jose.d.ts.map +1 -0
  68. package/dist/detectors/jose.js +62 -0
  69. package/dist/detectors/jose.js.map +1 -0
  70. package/dist/detectors/jwk.d.ts +25 -0
  71. package/dist/detectors/jwk.d.ts.map +1 -0
  72. package/dist/detectors/jwk.js +95 -0
  73. package/dist/detectors/jwk.js.map +1 -0
  74. package/dist/detectors/k8s.d.ts +17 -0
  75. package/dist/detectors/k8s.d.ts.map +1 -0
  76. package/dist/detectors/k8s.js +85 -0
  77. package/dist/detectors/k8s.js.map +1 -0
  78. package/dist/detectors/mesh.d.ts +35 -0
  79. package/dist/detectors/mesh.d.ts.map +1 -0
  80. package/dist/detectors/mesh.js +109 -0
  81. package/dist/detectors/mesh.js.map +1 -0
  82. package/dist/detectors/messaging.d.ts +16 -0
  83. package/dist/detectors/messaging.d.ts.map +1 -0
  84. package/dist/detectors/messaging.js +72 -0
  85. package/dist/detectors/messaging.js.map +1 -0
  86. package/dist/detectors/pem.d.ts.map +1 -1
  87. package/dist/detectors/pem.js +50 -2
  88. package/dist/detectors/pem.js.map +1 -1
  89. package/dist/detectors/php.d.ts +24 -0
  90. package/dist/detectors/php.d.ts.map +1 -0
  91. package/dist/detectors/php.js +167 -0
  92. package/dist/detectors/php.js.map +1 -0
  93. package/dist/detectors/python.d.ts.map +1 -1
  94. package/dist/detectors/python.js +144 -3
  95. package/dist/detectors/python.js.map +1 -1
  96. package/dist/detectors/ruby.d.ts +9 -5
  97. package/dist/detectors/ruby.d.ts.map +1 -1
  98. package/dist/detectors/ruby.js +122 -2
  99. package/dist/detectors/ruby.js.map +1 -1
  100. package/dist/detectors/rust.d.ts +8 -3
  101. package/dist/detectors/rust.d.ts.map +1 -1
  102. package/dist/detectors/rust.js +253 -3
  103. package/dist/detectors/rust.js.map +1 -1
  104. package/dist/detectors/secrets.d.ts +21 -0
  105. package/dist/detectors/secrets.d.ts.map +1 -0
  106. package/dist/detectors/secrets.js +79 -0
  107. package/dist/detectors/secrets.js.map +1 -0
  108. package/dist/detectors/source.d.ts.map +1 -1
  109. package/dist/detectors/source.js +305 -52
  110. package/dist/detectors/source.js.map +1 -1
  111. package/dist/detectors/stateful-hbs.d.ts +22 -0
  112. package/dist/detectors/stateful-hbs.d.ts.map +1 -0
  113. package/dist/detectors/stateful-hbs.js +142 -0
  114. package/dist/detectors/stateful-hbs.js.map +1 -0
  115. package/dist/detectors/terraform.d.ts +21 -0
  116. package/dist/detectors/terraform.d.ts.map +1 -0
  117. package/dist/detectors/terraform.js +123 -0
  118. package/dist/detectors/terraform.js.map +1 -0
  119. package/dist/detectors/vpn.d.ts +22 -0
  120. package/dist/detectors/vpn.d.ts.map +1 -0
  121. package/dist/detectors/vpn.js +99 -0
  122. package/dist/detectors/vpn.js.map +1 -0
  123. package/dist/errors.js.map +1 -1
  124. package/dist/evidence.d.ts +53 -0
  125. package/dist/evidence.d.ts.map +1 -0
  126. package/dist/evidence.js +81 -0
  127. package/dist/evidence.js.map +1 -0
  128. package/dist/index.d.ts +9 -2
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +10 -2
  131. package/dist/index.js.map +1 -1
  132. package/dist/inventory.d.ts +2 -0
  133. package/dist/inventory.d.ts.map +1 -1
  134. package/dist/inventory.js +27 -1
  135. package/dist/inventory.js.map +1 -1
  136. package/dist/parallel.js.map +1 -1
  137. package/dist/patch-policy.js.map +1 -1
  138. package/dist/policy.d.ts +67 -0
  139. package/dist/policy.d.ts.map +1 -0
  140. package/dist/policy.js +115 -0
  141. package/dist/policy.js.map +1 -0
  142. package/dist/redact.d.ts.map +1 -1
  143. package/dist/redact.js +39 -0
  144. package/dist/redact.js.map +1 -1
  145. package/dist/registry.d.ts +2 -2
  146. package/dist/registry.d.ts.map +1 -1
  147. package/dist/registry.js +34 -2
  148. package/dist/registry.js.map +1 -1
  149. package/dist/remediate-pipeline.d.ts +3 -0
  150. package/dist/remediate-pipeline.d.ts.map +1 -1
  151. package/dist/remediate-pipeline.js +71 -0
  152. package/dist/remediate-pipeline.js.map +1 -1
  153. package/dist/remediate-request.js.map +1 -1
  154. package/dist/remediation.d.ts +9 -3
  155. package/dist/remediation.d.ts.map +1 -1
  156. package/dist/remediation.js +17 -5
  157. package/dist/remediation.js.map +1 -1
  158. package/dist/report.d.ts +9 -0
  159. package/dist/report.d.ts.map +1 -1
  160. package/dist/report.js +38 -0
  161. package/dist/report.js.map +1 -1
  162. package/dist/scan-worker.js.map +1 -1
  163. package/dist/scan.d.ts.map +1 -1
  164. package/dist/scan.js +8 -1
  165. package/dist/scan.js.map +1 -1
  166. package/dist/severity.js.map +1 -1
  167. package/dist/standards.d.ts +96 -0
  168. package/dist/standards.d.ts.map +1 -0
  169. package/dist/standards.js +107 -0
  170. package/dist/standards.js.map +1 -0
  171. package/dist/triage.js.map +1 -1
  172. package/dist/types.d.ts +2 -2
  173. package/dist/types.d.ts.map +1 -1
  174. package/dist/types.js.map +1 -1
  175. package/dist/verify.js.map +1 -1
  176. package/dist/version.d.ts +1 -1
  177. package/dist/version.js +1 -1
  178. package/dist/version.js.map +1 -1
  179. package/dist/walk.js.map +1 -1
  180. package/dist/worktree.js.map +1 -1
  181. package/package.json +1 -1
  182. package/src/cbom-merge.ts +116 -0
  183. package/src/comments.ts +183 -18
  184. package/src/dependencies.ts +144 -4
  185. package/src/detect-utils.ts +47 -6
  186. package/src/detectors/c.ts +411 -5
  187. package/src/detectors/cicd.ts +158 -0
  188. package/src/detectors/cloud-kms.ts +87 -0
  189. package/src/detectors/cloudformation.ts +215 -0
  190. package/src/detectors/csharp.ts +154 -3
  191. package/src/detectors/database.ts +88 -0
  192. package/src/detectors/dnssec.ts +186 -0
  193. package/src/detectors/elixir.ts +237 -0
  194. package/src/detectors/go.ts +209 -6
  195. package/src/detectors/java.ts +191 -4
  196. package/src/detectors/jose.ts +94 -0
  197. package/src/detectors/jwk.ts +128 -0
  198. package/src/detectors/k8s.ts +112 -0
  199. package/src/detectors/mesh.ts +154 -0
  200. package/src/detectors/messaging.ts +98 -0
  201. package/src/detectors/pem.ts +54 -2
  202. package/src/detectors/php.ts +225 -0
  203. package/src/detectors/python.ts +153 -3
  204. package/src/detectors/ruby.ts +138 -7
  205. package/src/detectors/rust.ts +282 -7
  206. package/src/detectors/secrets.ts +114 -0
  207. package/src/detectors/source.ts +382 -72
  208. package/src/detectors/stateful-hbs.ts +180 -0
  209. package/src/detectors/terraform.ts +154 -0
  210. package/src/detectors/vpn.ts +131 -0
  211. package/src/evidence.ts +135 -0
  212. package/src/index.ts +15 -1
  213. package/src/inventory.ts +31 -1
  214. package/src/policy.ts +180 -0
  215. package/src/redact.ts +41 -0
  216. package/src/registry.ts +34 -2
  217. package/src/remediate-pipeline.ts +78 -0
  218. package/src/remediation.ts +23 -8
  219. package/src/report.ts +48 -2
  220. package/src/scan.ts +13 -1
  221. package/src/standards.ts +186 -0
  222. package/src/types.ts +3 -1
  223. package/src/version.ts +1 -1
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Source-code detector for classical asymmetric cryptography in PHP — one of the
3
+ * most-deployed backend languages, previously uncovered. Handles the three
4
+ * dominant crypto surfaces:
5
+ *
6
+ * - **ext/openssl** — `openssl_pkey_new()` (classified by its
7
+ * `OPENSSL_KEYTYPE_*` config, defaulting to RSA), `openssl_public_encrypt` /
8
+ * `openssl_private_decrypt` (RSA), and `openssl_sign` / `openssl_verify`.
9
+ * - **phpseclib3** — `RSA::createKey()` / `EC::createKey()` / `DSA::createKey()`.
10
+ * - **libsodium** — `sodium_crypto_box`/`kx` keypairs (X25519) and
11
+ * `sodium_crypto_sign` keypairs (Ed25519).
12
+ *
13
+ * Lexical, like the other packs; the `openssl_*` / `sodium_crypto_*` prefixes and
14
+ * `OPENSSL_KEYTYPE_*` constants are distinctive, so the false-positive rate is low.
15
+ *
16
+ * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later
17
+ * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but
18
+ * forgeable. `openssl_pkey_new` EC and `EC::createKey` are ambiguous (feed BOTH
19
+ * ECDSA and ECDH), classified conservatively as key-exchange-capable (hndl:true).
20
+ */
21
+ import type { Detector, Finding, RuleMeta } from "../types.js";
22
+ import { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
23
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
24
+
25
+ const RE_PHP_PKEY_NEW = /\bopenssl_pkey_new\s*\(/g;
26
+ const RE_PHP_RSA_CRYPT = /\bopenssl_(?:public_encrypt|private_decrypt)\s*\(/g;
27
+ const RE_PHP_SIGN = /\bopenssl_(?:sign|verify)\s*\(/g;
28
+ // phpseclib3 factory methods. The class names are generic, so require `::createKey`.
29
+ const RE_PHP_SECLIB = /\b(RSA|EC|DSA|DH)::createKey\s*\(/g;
30
+ // libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.
31
+ const RE_PHP_SODIUM_X25519 =
32
+ /\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\s*\(|\bsodium_crypto_scalarmult(?:_base)?\s*\(/g;
33
+ const RE_PHP_SODIUM_ED25519 = /\bsodium_crypto_sign_(?:seed_)?keypair\s*\(/g;
34
+
35
+ /** Key-type classification for `openssl_pkey_new`, keyed by its config constant. */
36
+ interface KeyInfo {
37
+ algo: Finding["algorithm"];
38
+ cat: Finding["category"];
39
+ sev: Finding["severity"];
40
+ hndl: boolean;
41
+ label: string;
42
+ remediation?: string;
43
+ }
44
+ const HYBRID = "hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign";
45
+
46
+ const RULE_PHP_KEYGEN: RuleMeta = {
47
+ id: "php-openssl-keygen",
48
+ title: "PHP openssl key generation",
49
+ description: "openssl_pkey_new (RSA/EC/DSA/DH, by OPENSSL_KEYTYPE_*)",
50
+ category: "kem",
51
+ severity: "high",
52
+ confidence: "high",
53
+ algorithm: "RSA",
54
+ hndl: true,
55
+ cwe: CWE_BROKEN_CRYPTO,
56
+ message: "Generates a classical key pair via PHP openssl_pkey_new — not quantum-safe.",
57
+ };
58
+ const RULE_PHP_RSA_CRYPT: RuleMeta = {
59
+ id: "php-openssl-rsa-crypt",
60
+ title: "PHP openssl RSA public-key encryption",
61
+ description: "openssl_public_encrypt / openssl_private_decrypt",
62
+ category: "kem",
63
+ severity: "high",
64
+ confidence: "high",
65
+ algorithm: "RSA",
66
+ hndl: true,
67
+ cwe: CWE_BROKEN_CRYPTO,
68
+ message:
69
+ "RSA public-key encryption/decryption (PHP openssl) is harvest-now-decrypt-later exposed.",
70
+ };
71
+ const RULE_PHP_SIGN: RuleMeta = {
72
+ id: "php-openssl-sign",
73
+ title: "PHP openssl signature",
74
+ description: "openssl_sign / openssl_verify",
75
+ category: "signature",
76
+ severity: "high",
77
+ confidence: "high",
78
+ algorithm: "unknown",
79
+ hndl: false,
80
+ cwe: CWE_BROKEN_CRYPTO,
81
+ message:
82
+ "Classical signature via PHP openssl (RSA/ECDSA/DSA) is forgeable by a quantum attacker.",
83
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
84
+ };
85
+ const RULE_PHP_SECLIB: RuleMeta = {
86
+ id: "php-phpseclib-keygen",
87
+ title: "PHP phpseclib key generation",
88
+ description: "phpseclib3 RSA/EC/DSA/DH ::createKey",
89
+ category: "kem",
90
+ severity: "high",
91
+ confidence: "medium",
92
+ algorithm: "RSA",
93
+ hndl: true,
94
+ cwe: CWE_BROKEN_CRYPTO,
95
+ message: "Generates a classical key pair via phpseclib3 (createKey) — not quantum-safe.",
96
+ };
97
+ const RULE_PHP_SODIUM_X25519: RuleMeta = {
98
+ id: "php-sodium-x25519",
99
+ title: "PHP libsodium X25519 key agreement",
100
+ description: "sodium_crypto_box/kx keypair + scalarmult",
101
+ category: "key-exchange",
102
+ severity: "medium",
103
+ confidence: "high",
104
+ algorithm: "X25519",
105
+ hndl: true,
106
+ cwe: CWE_BROKEN_CRYPTO,
107
+ message:
108
+ "libsodium crypto_box/kx (PHP) uses X25519 key agreement — modern but classical, harvest-now-decrypt-later exposed.",
109
+ };
110
+ const RULE_PHP_SODIUM_ED25519: RuleMeta = {
111
+ id: "php-sodium-ed25519",
112
+ title: "PHP libsodium Ed25519 signature",
113
+ description: "sodium_crypto_sign keypair",
114
+ category: "signature",
115
+ severity: "low",
116
+ confidence: "high",
117
+ algorithm: "EdDSA",
118
+ hndl: false,
119
+ cwe: CWE_BROKEN_CRYPTO,
120
+ message:
121
+ "libsodium crypto_sign (PHP) uses Ed25519 — classical and forgeable by a quantum attacker.",
122
+ };
123
+
124
+ /** phpseclib class name → its classification. */
125
+ const SECLIB_INFO: Record<string, KeyInfo> = {
126
+ RSA: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
127
+ EC: {
128
+ algo: "ECDH",
129
+ cat: "key-exchange",
130
+ sev: "high",
131
+ hndl: true,
132
+ label: "EC (ECDSA/ECDH)",
133
+ remediation: HYBRID,
134
+ },
135
+ DSA: { algo: "DSA", cat: "signature", sev: "high", hndl: false, label: "DSA" },
136
+ DH: { algo: "DH", cat: "key-exchange", sev: "high", hndl: true, label: "Diffie-Hellman" },
137
+ };
138
+
139
+ /** Classify `openssl_pkey_new` by the `OPENSSL_KEYTYPE_*` constant in its config.
140
+ * The window is bounded to the current statement (up to the next `;`, capped at
141
+ * 300 chars) so it can't bleed into a following `openssl_pkey_new` call's key
142
+ * type. Defaults to RSA — openssl_pkey_new's own default private_key_type is RSA
143
+ * when none is given. */
144
+ function classifyPkeyNew(content: string, index: number): KeyInfo {
145
+ const semi = content.indexOf(";", index);
146
+ const end = Math.min(index + 300, semi === -1 ? content.length : semi);
147
+ const w = content.slice(index, end);
148
+ if (/\bOPENSSL_KEYTYPE_EC\b/.test(w)) return SECLIB_INFO.EC;
149
+ if (/\bOPENSSL_KEYTYPE_DSA\b/.test(w)) return SECLIB_INFO.DSA;
150
+ if (/\bOPENSSL_KEYTYPE_DH\b/.test(w)) return SECLIB_INFO.DH;
151
+ return SECLIB_INFO.RSA;
152
+ }
153
+
154
+ /** Detects classical asymmetric crypto in PHP (openssl, phpseclib, libsodium). */
155
+ export const phpDetector: Detector = {
156
+ id: "php-crypto",
157
+ description: "Classical asymmetric crypto in PHP (openssl, phpseclib3, libsodium)",
158
+ scope: "source",
159
+ language: "php",
160
+ rules: [
161
+ RULE_PHP_KEYGEN,
162
+ RULE_PHP_RSA_CRYPT,
163
+ RULE_PHP_SIGN,
164
+ RULE_PHP_SECLIB,
165
+ RULE_PHP_SODIUM_X25519,
166
+ RULE_PHP_SODIUM_ED25519,
167
+ ],
168
+ appliesTo: (f) => hasExtension(f, PHP_EXTENSIONS),
169
+ detect({ file, content }): Finding[] {
170
+ const findings: Finding[] = [];
171
+ const add = (re: RegExp, rule: RuleMeta): void =>
172
+ eachMatch(re, content, (m) =>
173
+ findings.push(
174
+ findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
175
+ ),
176
+ );
177
+
178
+ // openssl_pkey_new — one finding per call, classified by its key-type config.
179
+ eachMatch(RE_PHP_PKEY_NEW, content, (m) => {
180
+ const info = classifyPkeyNew(content, m.index);
181
+ findings.push(
182
+ findingFromRule(
183
+ RULE_PHP_KEYGEN,
184
+ { file, content, index: m.index, matchLength: m[0].length },
185
+ {
186
+ title: `PHP openssl ${info.label} key generation`,
187
+ category: info.cat,
188
+ severity: info.sev,
189
+ algorithm: info.algo,
190
+ hndl: info.hndl,
191
+ message: `Generates a classical ${info.label} key pair via PHP openssl_pkey_new — not quantum-safe.`,
192
+ ...(info.remediation ? { remediation: info.remediation } : {}),
193
+ },
194
+ ),
195
+ );
196
+ });
197
+
198
+ add(RE_PHP_RSA_CRYPT, RULE_PHP_RSA_CRYPT);
199
+ add(RE_PHP_SIGN, RULE_PHP_SIGN);
200
+
201
+ // phpseclib3 ::createKey — classify by the class (RSA/EC/DSA/DH).
202
+ eachMatch(RE_PHP_SECLIB, content, (m) => {
203
+ const info = SECLIB_INFO[m[1]];
204
+ findings.push(
205
+ findingFromRule(
206
+ RULE_PHP_SECLIB,
207
+ { file, content, index: m.index, matchLength: m[0].length },
208
+ {
209
+ title: `PHP phpseclib ${info.label} key generation`,
210
+ category: info.cat,
211
+ severity: info.sev,
212
+ algorithm: info.algo,
213
+ hndl: info.hndl,
214
+ message: `Generates a classical ${info.label} key pair via phpseclib3 createKey — not quantum-safe.`,
215
+ ...(info.remediation ? { remediation: info.remediation } : {}),
216
+ },
217
+ ),
218
+ );
219
+ });
220
+
221
+ add(RE_PHP_SODIUM_X25519, RULE_PHP_SODIUM_X25519);
222
+ add(RE_PHP_SODIUM_ED25519, RULE_PHP_SODIUM_ED25519);
223
+ return findings;
224
+ },
225
+ };
@@ -22,7 +22,7 @@
22
22
  */
23
23
  import type { Detector, Finding, RuleMeta } from "../types.js";
24
24
  import { PYTHON_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
25
- import { CWE_BROKEN_CRYPTO } from "../cwe.js";
25
+ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
26
26
 
27
27
  /* -------------------------------------------------------------------------- */
28
28
  /* Precompiled regexes (module scope) */
@@ -38,8 +38,15 @@ const RE_PY_RSA_ENCRYPT = /\bpadding\.OAEP\s*\(|\bPKCS1_OAEP\.new\s*\(/g;
38
38
  const RE_PY_EC_KEYGEN = /\bec\.generate_private_key\s*\(|\bECC\.generate\s*\(/g;
39
39
  // ECDSA signatures: cryptography `ec.ECDSA(`, paramiko ECDSAKey.
40
40
  const RE_PY_ECDSA = /\bec\.ECDSA\s*\(|\bparamiko\.ECDSAKey\b|\bECDSAKey\.generate\s*\(/g;
41
+ // ECDH key agreement: cryptography `ec.ECDH(` — the actual harvest-now event in
42
+ // `private_key.exchange(ec.ECDH(), peer)`; previously missed entirely (audit F3).
43
+ const RE_PY_ECDH = /\bec\.ECDH\s*\(/g;
41
44
  // DSA: PyCryptodome `DSA.generate(`, paramiko DSSKey.
42
45
  const RE_PY_DSA = /\bDSA\.generate\s*\(|\bparamiko\.DSSKey\b|\bDSSKey\.generate\s*\(/g;
46
+ // hazmat DSA: cryptography `dsa.generate_private_key(` — the `cryptography` DSA
47
+ // keygen path (lowercase module) that RE_PY_DSA's uppercase `DSA.generate` misses
48
+ // (audit F9-python).
49
+ const RE_PY_HAZMAT_DSA = /\bdsa\.generate_private_key\s*\(/g;
43
50
  // Finite-field Diffie-Hellman (cryptography `dh`).
44
51
  const RE_PY_DH = /\bdh\.generate_parameters\s*\(|\bdh\.DHParameterNumbers\s*\(/g;
45
52
  // Modern-but-classical curve primitives (cryptography). `.generate(` anchors so
@@ -48,6 +55,15 @@ const RE_PY_X25519 = /\bX25519PrivateKey\.generate\s*\(/g;
48
55
  const RE_PY_X448 = /\bX448PrivateKey\.generate\s*\(/g;
49
56
  const RE_PY_EDDSA = /\b(?:Ed25519|Ed448)PrivateKey\.generate\s*\(|\bparamiko\.Ed25519Key\b/g;
50
57
 
58
+ // Python TLS misconfiguration. Mirrors the JS `tlsDetector` split of source.ts:
59
+ // certificate-verification bypass (requests `verify=False`, `ssl.CERT_NONE`,
60
+ // `check_hostname=False`, `ssl._create_unverified_context(`) is the high-severity
61
+ // MITM surface (CWE-295); a pinned legacy protocol (`ssl.PROTOCOL_TLSv1`) is the
62
+ // medium-severity weak-strength surface (CWE-326).
63
+ const RE_PY_TLS_REJECT =
64
+ /\bverify\s*=\s*False\b|\bssl\.CERT_NONE\b|\bcheck_hostname\s*=\s*False\b|\bssl\._create_unverified_context\s*\(/g;
65
+ const RE_PY_TLS_LEGACY = /\bPROTOCOL_TLSv1\b/g;
66
+
51
67
  /* -------------------------------------------------------------------------- */
52
68
  /* Rule catalog */
53
69
  /* -------------------------------------------------------------------------- */
@@ -106,6 +122,19 @@ const RULE_PY_ECDSA: RuleMeta = {
106
122
  message: "Classical ECDSA signing (Python) is forgeable by a quantum attacker.",
107
123
  remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
108
124
  };
125
+ const RULE_PY_ECDH: RuleMeta = {
126
+ id: "python-ecdh",
127
+ title: "Python ECDH key agreement",
128
+ description: "cryptography ec.ECDH() exchange",
129
+ category: "key-exchange",
130
+ severity: "high",
131
+ confidence: "high",
132
+ algorithm: "ECDH",
133
+ hndl: true,
134
+ cwe: CWE_BROKEN_CRYPTO,
135
+ message:
136
+ "Elliptic-curve Diffie-Hellman key agreement (Python) is broken by Shor's algorithm (harvest-now-decrypt-later).",
137
+ };
109
138
  const RULE_PY_DSA: RuleMeta = {
110
139
  id: "python-dsa",
111
140
  title: "Python DSA key/usage",
@@ -119,6 +148,20 @@ const RULE_PY_DSA: RuleMeta = {
119
148
  message: "Classical DSA (Python) is deprecated and forgeable by a quantum attacker.",
120
149
  remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
121
150
  };
151
+ const RULE_PY_HAZMAT_DSA: RuleMeta = {
152
+ id: "python-hazmat-dsa",
153
+ title: "Python DSA key generation (cryptography)",
154
+ description: "cryptography dsa.generate_private_key",
155
+ category: "signature",
156
+ severity: "high",
157
+ confidence: "high",
158
+ algorithm: "DSA",
159
+ hndl: false,
160
+ cwe: CWE_BROKEN_CRYPTO,
161
+ message:
162
+ "cryptography dsa.generate_private_key (Python) creates a classical DSA key; DSA is deprecated and forgeable by a quantum attacker.",
163
+ remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
164
+ };
122
165
  const RULE_PY_DH: RuleMeta = {
123
166
  id: "python-dh",
124
167
  title: "Python Diffie-Hellman key exchange",
@@ -137,7 +180,7 @@ const RULE_PY_X25519: RuleMeta = {
137
180
  title: "Python X25519 key exchange",
138
181
  description: "cryptography X25519PrivateKey.generate",
139
182
  category: "key-exchange",
140
- severity: "low",
183
+ severity: "medium",
141
184
  confidence: "high",
142
185
  algorithm: "X25519",
143
186
  hndl: true,
@@ -150,7 +193,7 @@ const RULE_PY_X448: RuleMeta = {
150
193
  title: "Python X448 key exchange",
151
194
  description: "cryptography X448PrivateKey.generate",
152
195
  category: "key-exchange",
153
- severity: "low",
196
+ severity: "medium",
154
197
  confidence: "high",
155
198
  algorithm: "X448",
156
199
  hndl: true,
@@ -169,6 +212,91 @@ const RULE_PY_EDDSA: RuleMeta = {
169
212
  cwe: CWE_BROKEN_CRYPTO,
170
213
  message: "Ed25519/Ed448 (Python) is a modern but still classical signature scheme.",
171
214
  };
215
+ const RULE_PY_TLS_REJECT: RuleMeta = {
216
+ id: "python-tls-reject",
217
+ title: "Python TLS certificate verification disabled",
218
+ description:
219
+ "requests verify=False / ssl.CERT_NONE / check_hostname=False / _create_unverified_context",
220
+ category: "tls",
221
+ severity: "high",
222
+ confidence: "high",
223
+ hndl: false,
224
+ cwe: CWE_CERT_VALIDATION,
225
+ message:
226
+ "TLS certificate verification is disabled (verify=False / CERT_NONE / check_hostname=False / _create_unverified_context), which allows man-in-the-middle attacks.",
227
+ remediation:
228
+ "Enable certificate verification (verify=True, ssl.CERT_REQUIRED, check_hostname=True) and verify certificates properly.",
229
+ };
230
+ const RULE_PY_TLS_LEGACY: RuleMeta = {
231
+ id: "python-tls-legacy-version",
232
+ title: "Python legacy TLS version pinned",
233
+ description: "ssl.PROTOCOL_TLSv1 (TLS 1.0)",
234
+ category: "tls",
235
+ severity: "medium",
236
+ confidence: "high",
237
+ hndl: false,
238
+ cwe: CWE_WEAK_STRENGTH,
239
+ message: "TLS 1.0 (ssl.PROTOCOL_TLSv1) is deprecated and insecure; require TLS 1.3.",
240
+ remediation:
241
+ "Use ssl.PROTOCOL_TLS_CLIENT with minimum_version = ssl.TLSVersion.TLSv1_3 and prefer PQC-hybrid key exchange.",
242
+ };
243
+
244
+ /**
245
+ * Aliasable `cryptography` / PyCryptodome modules → the (method, rule) pairs
246
+ * reachable through them. Lets an aliased module import
247
+ * (`from ... import rsa as _rsa` → `_rsa.generate_private_key(`) resolve back to
248
+ * the same rule the direct `rsa.generate_private_key(` would fire, since the
249
+ * detector's regexes are module-qualified and miss a renamed prefix.
250
+ */
251
+ const PY_MODULE_RULES: Record<string, ReadonlyArray<{ method: string; rule: RuleMeta }>> = {
252
+ rsa: [{ method: "generate_private_key", rule: RULE_PY_RSA_KEYGEN }],
253
+ ec: [
254
+ { method: "generate_private_key", rule: RULE_PY_EC_KEYGEN },
255
+ { method: "ECDSA", rule: RULE_PY_ECDSA },
256
+ { method: "ECDH", rule: RULE_PY_ECDH },
257
+ ],
258
+ dsa: [{ method: "generate_private_key", rule: RULE_PY_HAZMAT_DSA }],
259
+ dh: [{ method: "generate_parameters", rule: RULE_PY_DH }],
260
+ padding: [{ method: "OAEP", rule: RULE_PY_RSA_ENCRYPT }],
261
+ // PyCryptodome factory modules (`.generate(`).
262
+ RSA: [{ method: "generate", rule: RULE_PY_RSA_KEYGEN }],
263
+ ECC: [{ method: "generate", rule: RULE_PY_EC_KEYGEN }],
264
+ DSA: [{ method: "generate", rule: RULE_PY_DSA }],
265
+ };
266
+
267
+ /** Escape a string for interpolation into a dynamically-built RegExp. */
268
+ function escapePyRe(s: string): string {
269
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
270
+ }
271
+
272
+ /**
273
+ * Collect module aliases for the aliasable crypto modules, so a later
274
+ * `<alias>.<method>(` call still resolves. Handles both `from <path> import
275
+ * <mod> as <alias>` (including comma-separated specifiers) and the bare
276
+ * `import <path.mod> as <alias>`. Returns Map<module, alias[]>; an alias equal to
277
+ * its own module name is skipped (matched directly already).
278
+ */
279
+ function collectPyModuleAliases(content: string): Map<string, string[]> {
280
+ const out = new Map<string, string[]>();
281
+ const add = (mod: string, alias: string): void => {
282
+ if (!alias || alias === mod || !(mod in PY_MODULE_RULES)) return;
283
+ const list = out.get(mod) ?? [];
284
+ if (!list.includes(alias)) list.push(alias);
285
+ out.set(mod, list);
286
+ };
287
+ // `from <path> import a as b, c as d` — scan the import list for `X as Y`.
288
+ const fromRe = /(?:^|\n)[ \t]*from\s+[\w.]+\s+import\s+([^\n#]+)/g;
289
+ for (let m = fromRe.exec(content); m; m = fromRe.exec(content)) {
290
+ const specRe = /([A-Za-z_]\w*)\s+as\s+([A-Za-z_]\w*)/g;
291
+ for (let s = specRe.exec(m[1]); s; s = specRe.exec(m[1])) add(s[1], s[2]);
292
+ }
293
+ // `import <path.mod> as <alias>` — the aliased module is the last dotted segment.
294
+ const impRe = /(?:^|\n)[ \t]*import\s+([\w.]+)\s+as\s+([A-Za-z_]\w*)/g;
295
+ for (let m = impRe.exec(content); m; m = impRe.exec(content)) {
296
+ add(m[1].split(".").pop() ?? m[1], m[2]);
297
+ }
298
+ return out;
299
+ }
172
300
 
173
301
  /** Detects classical asymmetric crypto in Python source. */
174
302
  export const pythonDetector: Detector = {
@@ -181,11 +309,15 @@ export const pythonDetector: Detector = {
181
309
  RULE_PY_RSA_ENCRYPT,
182
310
  RULE_PY_EC_KEYGEN,
183
311
  RULE_PY_ECDSA,
312
+ RULE_PY_ECDH,
184
313
  RULE_PY_DSA,
314
+ RULE_PY_HAZMAT_DSA,
185
315
  RULE_PY_DH,
186
316
  RULE_PY_X25519,
187
317
  RULE_PY_X448,
188
318
  RULE_PY_EDDSA,
319
+ RULE_PY_TLS_REJECT,
320
+ RULE_PY_TLS_LEGACY,
189
321
  ],
190
322
  appliesTo: (f) => hasExtension(f, PYTHON_EXTENSIONS),
191
323
  detect({ file, content }): Finding[] {
@@ -201,11 +333,29 @@ export const pythonDetector: Detector = {
201
333
  add(RE_PY_RSA_ENCRYPT, RULE_PY_RSA_ENCRYPT);
202
334
  add(RE_PY_EC_KEYGEN, RULE_PY_EC_KEYGEN);
203
335
  add(RE_PY_ECDSA, RULE_PY_ECDSA);
336
+ add(RE_PY_ECDH, RULE_PY_ECDH);
204
337
  add(RE_PY_DSA, RULE_PY_DSA);
338
+ add(RE_PY_HAZMAT_DSA, RULE_PY_HAZMAT_DSA);
205
339
  add(RE_PY_DH, RULE_PY_DH);
206
340
  add(RE_PY_X25519, RULE_PY_X25519);
207
341
  add(RE_PY_X448, RULE_PY_X448);
208
342
  add(RE_PY_EDDSA, RULE_PY_EDDSA);
343
+ add(RE_PY_TLS_REJECT, RULE_PY_TLS_REJECT);
344
+ add(RE_PY_TLS_LEGACY, RULE_PY_TLS_LEGACY);
345
+
346
+ // Module-alias resolution: `from ... import rsa as _rsa` then
347
+ // `_rsa.generate_private_key(` — the direct regexes are module-qualified
348
+ // (`\brsa\.`) and miss a renamed prefix. Runs on the ORIGINAL content so
349
+ // locations stay exact; fires only for an alias explicitly bound to a known
350
+ // crypto module, so precision is unaffected.
351
+ for (const [mod, aliasList] of collectPyModuleAliases(content)) {
352
+ for (const alias of aliasList) {
353
+ const a = escapePyRe(alias);
354
+ for (const { method, rule } of PY_MODULE_RULES[mod]) {
355
+ add(new RegExp(`\\b${a}\\.${method}\\s*\\(`, "g"), rule);
356
+ }
357
+ }
358
+ }
209
359
 
210
360
  return findings;
211
361
  },
@@ -1,22 +1,40 @@
1
1
  /**
2
2
  * Source-code detector for classical asymmetric cryptography in Ruby. Ruby's
3
3
  * asymmetric crypto is almost entirely `OpenSSL::PKey::{RSA,EC,DSA,DH}`, which
4
- * makes the signals precise. Lexical, same strategy as the other packs.
4
+ * makes the signals precise. Lexical, same strategy as the other packs. Beyond
5
+ * keygen it also covers RSA public-key encryption on a loaded key, DH key
6
+ * agreement, the type-agnostic `OpenSSL::PKey.read` loader, Ed25519 key
7
+ * generation, and disabled TLS peer verification (`VERIFY_NONE`).
5
8
  *
6
9
  * HNDL: RSA (keygen/encryption) and (EC)DH key agreement are harvest-now-
7
- * decrypt-later exposed (hndl:true); DSA signatures are hndl:false but forgeable.
8
- * `OpenSSL::PKey::EC` is ambiguous (feeds ECDSA + ECDH), so it is classified
9
- * conservatively as key-exchange-capable (hndl:true), like the other EC keygen
10
- * rules.
10
+ * decrypt-later exposed (hndl:true); DSA / Ed25519 signatures are hndl:false but
11
+ * forgeable. `OpenSSL::PKey::EC` is ambiguous (feeds ECDSA + ECDH), so it is
12
+ * classified conservatively as key-exchange-capable (hndl:true), like the other
13
+ * EC keygen rules; `OpenSSL::PKey.read` loads a key of unknown type and is
14
+ * likewise treated conservatively.
11
15
  */
12
16
  import type { Detector, Finding, RuleMeta } from "../types.js";
13
17
  import { RUBY_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
14
- import { CWE_BROKEN_CRYPTO } from "../cwe.js";
18
+ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from "../cwe.js";
15
19
 
16
20
  const RE_RB_RSA = /\bOpenSSL::PKey::RSA\.(?:new|generate)\s*\(/g;
17
21
  const RE_RB_EC = /\bOpenSSL::PKey::EC\.(?:new|generate)\s*\(/g;
18
22
  const RE_RB_DSA = /\bOpenSSL::PKey::DSA\.(?:new|generate)\s*\(/g;
19
23
  const RE_RB_DH = /\bOpenSSL::PKey::DH\.new\s*\(/g;
24
+ // RSA public-key encryption on an already-loaded key (audit F4-ruby / F9).
25
+ const RE_RB_RSA_CRYPT = /\.public_encrypt\b|\.private_decrypt\b/g;
26
+ // Finite-field DH shared-secret agreement.
27
+ const RE_RB_DH_AGREE = /\bdh_compute_key\s*\(/g;
28
+ // Type-agnostic key loader — could be RSA/EC/DSA/DH (conservative).
29
+ const RE_RB_PKEY_READ = /\bOpenSSL::PKey\.read\s*\(/g;
30
+ // Ed25519 signing key via the generic generate_key factory.
31
+ const RE_RB_ED25519 = /\bOpenSSL::PKey\.generate_key\s*\(\s*["']ED25519["']/g;
32
+ // `ed25519` gem — Ed25519::SigningKey / VerifyKey (signature keys).
33
+ const RE_RB_ED25519_GEM = /\bEd25519::(?:SigningKey|VerifyKey)\b/g;
34
+ // `rbnacl` (libsodium) — Curve25519/X25519 key agreement: PrivateKey, Box, GroupElement.
35
+ const RE_RB_RBNACL = /\bRbNaCl::(?:PrivateKey|Box|GroupElement)\b/g;
36
+ // TLS peer verification disabled (mirrors the JS tlsDetector rejectUnauthorized rule).
37
+ const RE_RB_TLS_VERIFY_NONE = /\bOpenSSL::SSL::VERIFY_NONE\b/g;
20
38
 
21
39
  const RULE_RB_RSA: RuleMeta = {
22
40
  id: "ruby-rsa",
@@ -71,6 +89,100 @@ const RULE_RB_DH: RuleMeta = {
71
89
  message:
72
90
  "Finite-field Diffie-Hellman (Ruby/OpenSSL) is broken by Shor's algorithm (harvest-now-decrypt-later).",
73
91
  };
92
+ const RULE_RB_RSA_CRYPT: RuleMeta = {
93
+ id: "ruby-rsa-crypt",
94
+ title: "Ruby RSA public-key encryption",
95
+ description: "OpenSSL::PKey::RSA#public_encrypt / #private_decrypt",
96
+ category: "kem",
97
+ severity: "high",
98
+ confidence: "high",
99
+ algorithm: "RSA",
100
+ hndl: true,
101
+ cwe: CWE_BROKEN_CRYPTO,
102
+ message:
103
+ "RSA public-key encryption/decryption (Ruby/OpenSSL) is harvest-now-decrypt-later exposed.",
104
+ };
105
+ const RULE_RB_DH_AGREE: RuleMeta = {
106
+ id: "ruby-dh-agree",
107
+ title: "Ruby Diffie-Hellman key agreement",
108
+ description: "OpenSSL DH compute_key shared-secret agreement",
109
+ category: "key-exchange",
110
+ severity: "high",
111
+ confidence: "high",
112
+ algorithm: "DH",
113
+ hndl: true,
114
+ cwe: CWE_BROKEN_CRYPTO,
115
+ message:
116
+ "Finite-field Diffie-Hellman key agreement (Ruby/OpenSSL) is broken by Shor's algorithm (harvest-now-decrypt-later).",
117
+ };
118
+ const RULE_RB_PKEY_READ: RuleMeta = {
119
+ id: "ruby-pkey-read",
120
+ title: "Ruby PKey loaded from serialized key",
121
+ description: "OpenSSL::PKey.read (type-agnostic key loader)",
122
+ category: "key-exchange",
123
+ severity: "high",
124
+ confidence: "medium",
125
+ algorithm: "unknown",
126
+ hndl: true,
127
+ cwe: CWE_BROKEN_CRYPTO,
128
+ message:
129
+ "Loads a classical asymmetric key of unknown type (RSA/EC/DSA/DH) via OpenSSL::PKey.read. Treated conservatively as key-exchange-capable (harvest-now-decrypt-later).",
130
+ remediation:
131
+ "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
132
+ };
133
+ const RULE_RB_ED25519: RuleMeta = {
134
+ id: "ruby-ed25519",
135
+ title: "Ruby Ed25519 key generation",
136
+ description: 'OpenSSL::PKey.generate_key("ED25519")',
137
+ category: "signature",
138
+ severity: "low",
139
+ confidence: "high",
140
+ algorithm: "EdDSA",
141
+ hndl: false,
142
+ cwe: CWE_BROKEN_CRYPTO,
143
+ message:
144
+ "Generates an Ed25519 signing key (Ruby/OpenSSL) — modern but classical, and forgeable by a quantum attacker.",
145
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
146
+ };
147
+ const RULE_RB_ED25519_GEM: RuleMeta = {
148
+ id: "ruby-ed25519-gem",
149
+ title: "Ruby Ed25519 signature (ed25519 gem)",
150
+ description: "ed25519 gem Ed25519::SigningKey / Ed25519::VerifyKey",
151
+ category: "signature",
152
+ severity: "low",
153
+ confidence: "high",
154
+ algorithm: "EdDSA",
155
+ hndl: false,
156
+ cwe: CWE_BROKEN_CRYPTO,
157
+ message:
158
+ "Ed25519 signing/verification via the `ed25519` gem — modern but classical, and forgeable by a quantum attacker.",
159
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
160
+ };
161
+ const RULE_RB_RBNACL: RuleMeta = {
162
+ id: "ruby-rbnacl",
163
+ title: "Ruby X25519 key agreement (rbnacl)",
164
+ description: "rbnacl (libsodium) RbNaCl::PrivateKey / Box / GroupElement",
165
+ category: "key-exchange",
166
+ severity: "medium",
167
+ confidence: "high",
168
+ algorithm: "X25519",
169
+ hndl: true,
170
+ cwe: CWE_BROKEN_CRYPTO,
171
+ message:
172
+ "Curve25519/X25519 key agreement via the `rbnacl` gem (libsodium) — modern but classical key agreement, harvest-now-decrypt-later exposed.",
173
+ };
174
+ const RULE_RB_TLS_VERIFY_NONE: RuleMeta = {
175
+ id: "ruby-tls-verify-none",
176
+ title: "Ruby TLS certificate verification disabled",
177
+ description: "OpenSSL::SSL::VERIFY_NONE",
178
+ category: "tls",
179
+ severity: "high",
180
+ confidence: "high",
181
+ hndl: false,
182
+ cwe: CWE_CERT_VALIDATION,
183
+ message: "OpenSSL::SSL::VERIFY_NONE disables TLS peer certificate verification (MITM risk).",
184
+ remediation: "Use OpenSSL::SSL::VERIFY_PEER and verify the certificate chain.",
185
+ };
74
186
 
75
187
  /** Detects classical asymmetric crypto in Ruby (OpenSSL::PKey). */
76
188
  export const rubyDetector: Detector = {
@@ -78,7 +190,19 @@ export const rubyDetector: Detector = {
78
190
  description: "Classical asymmetric crypto in Ruby (OpenSSL::PKey::{RSA,EC,DSA,DH})",
79
191
  scope: "source",
80
192
  language: "ruby",
81
- rules: [RULE_RB_RSA, RULE_RB_EC, RULE_RB_DSA, RULE_RB_DH],
193
+ rules: [
194
+ RULE_RB_RSA,
195
+ RULE_RB_EC,
196
+ RULE_RB_DSA,
197
+ RULE_RB_DH,
198
+ RULE_RB_RSA_CRYPT,
199
+ RULE_RB_DH_AGREE,
200
+ RULE_RB_PKEY_READ,
201
+ RULE_RB_ED25519,
202
+ RULE_RB_ED25519_GEM,
203
+ RULE_RB_RBNACL,
204
+ RULE_RB_TLS_VERIFY_NONE,
205
+ ],
82
206
  appliesTo: (f) => hasExtension(f, RUBY_EXTENSIONS),
83
207
  detect({ file, content }): Finding[] {
84
208
  const findings: Finding[] = [];
@@ -92,6 +216,13 @@ export const rubyDetector: Detector = {
92
216
  add(RE_RB_EC, RULE_RB_EC);
93
217
  add(RE_RB_DSA, RULE_RB_DSA);
94
218
  add(RE_RB_DH, RULE_RB_DH);
219
+ add(RE_RB_RSA_CRYPT, RULE_RB_RSA_CRYPT);
220
+ add(RE_RB_DH_AGREE, RULE_RB_DH_AGREE);
221
+ add(RE_RB_PKEY_READ, RULE_RB_PKEY_READ);
222
+ add(RE_RB_ED25519, RULE_RB_ED25519);
223
+ add(RE_RB_ED25519_GEM, RULE_RB_ED25519_GEM);
224
+ add(RE_RB_RBNACL, RULE_RB_RBNACL);
225
+ add(RE_RB_TLS_VERIFY_NONE, RULE_RB_TLS_VERIFY_NONE);
95
226
  return findings;
96
227
  },
97
228
  };