@quantakrypto/core 0.4.3 → 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 (163) hide show
  1. package/dist/cbom-merge.d.ts +9 -0
  2. package/dist/cbom-merge.d.ts.map +1 -0
  3. package/dist/cbom-merge.js +99 -0
  4. package/dist/cbom-merge.js.map +1 -0
  5. package/dist/comments.d.ts +4 -3
  6. package/dist/comments.d.ts.map +1 -1
  7. package/dist/comments.js +53 -33
  8. package/dist/comments.js.map +1 -1
  9. package/dist/dependencies.d.ts.map +1 -1
  10. package/dist/dependencies.js +1 -1
  11. package/dist/dependencies.js.map +1 -1
  12. package/dist/detect-utils.d.ts +5 -1
  13. package/dist/detect-utils.d.ts.map +1 -1
  14. package/dist/detect-utils.js +11 -2
  15. package/dist/detect-utils.js.map +1 -1
  16. package/dist/detectors/c.d.ts +4 -2
  17. package/dist/detectors/c.d.ts.map +1 -1
  18. package/dist/detectors/c.js +192 -1
  19. package/dist/detectors/c.js.map +1 -1
  20. package/dist/detectors/cicd.d.ts +27 -0
  21. package/dist/detectors/cicd.d.ts.map +1 -0
  22. package/dist/detectors/cicd.js +116 -0
  23. package/dist/detectors/cicd.js.map +1 -0
  24. package/dist/detectors/cloud-kms.d.ts +21 -0
  25. package/dist/detectors/cloud-kms.d.ts.map +1 -0
  26. package/dist/detectors/cloud-kms.js +57 -0
  27. package/dist/detectors/cloud-kms.js.map +1 -0
  28. package/dist/detectors/cloudformation.d.ts +30 -0
  29. package/dist/detectors/cloudformation.d.ts.map +1 -0
  30. package/dist/detectors/cloudformation.js +170 -0
  31. package/dist/detectors/cloudformation.js.map +1 -0
  32. package/dist/detectors/database.d.ts +17 -0
  33. package/dist/detectors/database.d.ts.map +1 -0
  34. package/dist/detectors/database.js +60 -0
  35. package/dist/detectors/database.js.map +1 -0
  36. package/dist/detectors/dnssec.d.ts +55 -0
  37. package/dist/detectors/dnssec.d.ts.map +1 -0
  38. package/dist/detectors/dnssec.js +107 -0
  39. package/dist/detectors/dnssec.js.map +1 -0
  40. package/dist/detectors/elixir.d.ts +20 -0
  41. package/dist/detectors/elixir.d.ts.map +1 -0
  42. package/dist/detectors/elixir.js +195 -0
  43. package/dist/detectors/elixir.js.map +1 -0
  44. package/dist/detectors/java.d.ts.map +1 -1
  45. package/dist/detectors/java.js +1 -1
  46. package/dist/detectors/java.js.map +1 -1
  47. package/dist/detectors/jose.d.ts +20 -0
  48. package/dist/detectors/jose.d.ts.map +1 -0
  49. package/dist/detectors/jose.js +62 -0
  50. package/dist/detectors/jose.js.map +1 -0
  51. package/dist/detectors/jwk.d.ts +25 -0
  52. package/dist/detectors/jwk.d.ts.map +1 -0
  53. package/dist/detectors/jwk.js +95 -0
  54. package/dist/detectors/jwk.js.map +1 -0
  55. package/dist/detectors/k8s.d.ts +17 -0
  56. package/dist/detectors/k8s.d.ts.map +1 -0
  57. package/dist/detectors/k8s.js +85 -0
  58. package/dist/detectors/k8s.js.map +1 -0
  59. package/dist/detectors/mesh.d.ts +35 -0
  60. package/dist/detectors/mesh.d.ts.map +1 -0
  61. package/dist/detectors/mesh.js +109 -0
  62. package/dist/detectors/mesh.js.map +1 -0
  63. package/dist/detectors/messaging.d.ts +16 -0
  64. package/dist/detectors/messaging.d.ts.map +1 -0
  65. package/dist/detectors/messaging.js +72 -0
  66. package/dist/detectors/messaging.js.map +1 -0
  67. package/dist/detectors/pem.js +1 -1
  68. package/dist/detectors/pem.js.map +1 -1
  69. package/dist/detectors/php.d.ts +24 -0
  70. package/dist/detectors/php.d.ts.map +1 -0
  71. package/dist/detectors/php.js +167 -0
  72. package/dist/detectors/php.js.map +1 -0
  73. package/dist/detectors/python.d.ts.map +1 -1
  74. package/dist/detectors/python.js +70 -0
  75. package/dist/detectors/python.js.map +1 -1
  76. package/dist/detectors/rust.d.ts.map +1 -1
  77. package/dist/detectors/rust.js +68 -0
  78. package/dist/detectors/rust.js.map +1 -1
  79. package/dist/detectors/secrets.d.ts +21 -0
  80. package/dist/detectors/secrets.d.ts.map +1 -0
  81. package/dist/detectors/secrets.js +79 -0
  82. package/dist/detectors/secrets.js.map +1 -0
  83. package/dist/detectors/source.d.ts.map +1 -1
  84. package/dist/detectors/source.js +190 -47
  85. package/dist/detectors/source.js.map +1 -1
  86. package/dist/detectors/stateful-hbs.d.ts.map +1 -1
  87. package/dist/detectors/stateful-hbs.js +9 -6
  88. package/dist/detectors/stateful-hbs.js.map +1 -1
  89. package/dist/detectors/terraform.d.ts +21 -0
  90. package/dist/detectors/terraform.d.ts.map +1 -0
  91. package/dist/detectors/terraform.js +123 -0
  92. package/dist/detectors/terraform.js.map +1 -0
  93. package/dist/detectors/vpn.d.ts +22 -0
  94. package/dist/detectors/vpn.d.ts.map +1 -0
  95. package/dist/detectors/vpn.js +99 -0
  96. package/dist/detectors/vpn.js.map +1 -0
  97. package/dist/evidence.d.ts +5 -0
  98. package/dist/evidence.d.ts.map +1 -1
  99. package/dist/evidence.js +5 -0
  100. package/dist/evidence.js.map +1 -1
  101. package/dist/index.d.ts +5 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +6 -0
  104. package/dist/index.js.map +1 -1
  105. package/dist/policy.d.ts +67 -0
  106. package/dist/policy.d.ts.map +1 -0
  107. package/dist/policy.js +115 -0
  108. package/dist/policy.js.map +1 -0
  109. package/dist/registry.d.ts.map +1 -1
  110. package/dist/registry.js +30 -0
  111. package/dist/registry.js.map +1 -1
  112. package/dist/remediate-pipeline.d.ts.map +1 -1
  113. package/dist/remediate-pipeline.js +22 -1
  114. package/dist/remediate-pipeline.js.map +1 -1
  115. package/dist/report.d.ts.map +1 -1
  116. package/dist/report.js +7 -0
  117. package/dist/report.js.map +1 -1
  118. package/dist/standards.d.ts +96 -0
  119. package/dist/standards.d.ts.map +1 -0
  120. package/dist/standards.js +107 -0
  121. package/dist/standards.js.map +1 -0
  122. package/dist/types.d.ts +1 -1
  123. package/dist/types.d.ts.map +1 -1
  124. package/dist/types.js.map +1 -1
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/dist/version.js.map +1 -1
  128. package/package.json +1 -1
  129. package/src/cbom-merge.ts +116 -0
  130. package/src/comments.ts +59 -33
  131. package/src/dependencies.ts +2 -1
  132. package/src/detect-utils.ts +14 -2
  133. package/src/detectors/c.ts +209 -3
  134. package/src/detectors/cicd.ts +158 -0
  135. package/src/detectors/cloud-kms.ts +87 -0
  136. package/src/detectors/cloudformation.ts +215 -0
  137. package/src/detectors/database.ts +88 -0
  138. package/src/detectors/dnssec.ts +186 -0
  139. package/src/detectors/elixir.ts +237 -0
  140. package/src/detectors/java.ts +2 -1
  141. package/src/detectors/jose.ts +94 -0
  142. package/src/detectors/jwk.ts +128 -0
  143. package/src/detectors/k8s.ts +112 -0
  144. package/src/detectors/mesh.ts +154 -0
  145. package/src/detectors/messaging.ts +98 -0
  146. package/src/detectors/pem.ts +1 -1
  147. package/src/detectors/php.ts +225 -0
  148. package/src/detectors/python.ts +71 -0
  149. package/src/detectors/rust.ts +71 -0
  150. package/src/detectors/secrets.ts +114 -0
  151. package/src/detectors/source.ts +235 -68
  152. package/src/detectors/stateful-hbs.ts +9 -6
  153. package/src/detectors/terraform.ts +154 -0
  154. package/src/detectors/vpn.ts +131 -0
  155. package/src/evidence.ts +11 -0
  156. package/src/index.ts +10 -0
  157. package/src/policy.ts +180 -0
  158. package/src/registry.ts +30 -0
  159. package/src/remediate-pipeline.ts +25 -2
  160. package/src/report.ts +7 -0
  161. package/src/standards.ts +186 -0
  162. package/src/types.ts +2 -0
  163. package/src/version.ts +1 -1
@@ -0,0 +1,167 @@
1
+ import { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ const RE_PHP_PKEY_NEW = /\bopenssl_pkey_new\s*\(/g;
4
+ const RE_PHP_RSA_CRYPT = /\bopenssl_(?:public_encrypt|private_decrypt)\s*\(/g;
5
+ const RE_PHP_SIGN = /\bopenssl_(?:sign|verify)\s*\(/g;
6
+ // phpseclib3 factory methods. The class names are generic, so require `::createKey`.
7
+ const RE_PHP_SECLIB = /\b(RSA|EC|DSA|DH)::createKey\s*\(/g;
8
+ // libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.
9
+ const RE_PHP_SODIUM_X25519 = /\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\s*\(|\bsodium_crypto_scalarmult(?:_base)?\s*\(/g;
10
+ const RE_PHP_SODIUM_ED25519 = /\bsodium_crypto_sign_(?:seed_)?keypair\s*\(/g;
11
+ const HYBRID = "hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign";
12
+ const RULE_PHP_KEYGEN = {
13
+ id: "php-openssl-keygen",
14
+ title: "PHP openssl key generation",
15
+ description: "openssl_pkey_new (RSA/EC/DSA/DH, by OPENSSL_KEYTYPE_*)",
16
+ category: "kem",
17
+ severity: "high",
18
+ confidence: "high",
19
+ algorithm: "RSA",
20
+ hndl: true,
21
+ cwe: CWE_BROKEN_CRYPTO,
22
+ message: "Generates a classical key pair via PHP openssl_pkey_new — not quantum-safe.",
23
+ };
24
+ const RULE_PHP_RSA_CRYPT = {
25
+ id: "php-openssl-rsa-crypt",
26
+ title: "PHP openssl RSA public-key encryption",
27
+ description: "openssl_public_encrypt / openssl_private_decrypt",
28
+ category: "kem",
29
+ severity: "high",
30
+ confidence: "high",
31
+ algorithm: "RSA",
32
+ hndl: true,
33
+ cwe: CWE_BROKEN_CRYPTO,
34
+ message: "RSA public-key encryption/decryption (PHP openssl) is harvest-now-decrypt-later exposed.",
35
+ };
36
+ const RULE_PHP_SIGN = {
37
+ id: "php-openssl-sign",
38
+ title: "PHP openssl signature",
39
+ description: "openssl_sign / openssl_verify",
40
+ category: "signature",
41
+ severity: "high",
42
+ confidence: "high",
43
+ algorithm: "unknown",
44
+ hndl: false,
45
+ cwe: CWE_BROKEN_CRYPTO,
46
+ message: "Classical signature via PHP openssl (RSA/ECDSA/DSA) is forgeable by a quantum attacker.",
47
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
48
+ };
49
+ const RULE_PHP_SECLIB = {
50
+ id: "php-phpseclib-keygen",
51
+ title: "PHP phpseclib key generation",
52
+ description: "phpseclib3 RSA/EC/DSA/DH ::createKey",
53
+ category: "kem",
54
+ severity: "high",
55
+ confidence: "medium",
56
+ algorithm: "RSA",
57
+ hndl: true,
58
+ cwe: CWE_BROKEN_CRYPTO,
59
+ message: "Generates a classical key pair via phpseclib3 (createKey) — not quantum-safe.",
60
+ };
61
+ const RULE_PHP_SODIUM_X25519 = {
62
+ id: "php-sodium-x25519",
63
+ title: "PHP libsodium X25519 key agreement",
64
+ description: "sodium_crypto_box/kx keypair + scalarmult",
65
+ category: "key-exchange",
66
+ severity: "medium",
67
+ confidence: "high",
68
+ algorithm: "X25519",
69
+ hndl: true,
70
+ cwe: CWE_BROKEN_CRYPTO,
71
+ message: "libsodium crypto_box/kx (PHP) uses X25519 key agreement — modern but classical, harvest-now-decrypt-later exposed.",
72
+ };
73
+ const RULE_PHP_SODIUM_ED25519 = {
74
+ id: "php-sodium-ed25519",
75
+ title: "PHP libsodium Ed25519 signature",
76
+ description: "sodium_crypto_sign keypair",
77
+ category: "signature",
78
+ severity: "low",
79
+ confidence: "high",
80
+ algorithm: "EdDSA",
81
+ hndl: false,
82
+ cwe: CWE_BROKEN_CRYPTO,
83
+ message: "libsodium crypto_sign (PHP) uses Ed25519 — classical and forgeable by a quantum attacker.",
84
+ };
85
+ /** phpseclib class name → its classification. */
86
+ const SECLIB_INFO = {
87
+ RSA: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
88
+ EC: {
89
+ algo: "ECDH",
90
+ cat: "key-exchange",
91
+ sev: "high",
92
+ hndl: true,
93
+ label: "EC (ECDSA/ECDH)",
94
+ remediation: HYBRID,
95
+ },
96
+ DSA: { algo: "DSA", cat: "signature", sev: "high", hndl: false, label: "DSA" },
97
+ DH: { algo: "DH", cat: "key-exchange", sev: "high", hndl: true, label: "Diffie-Hellman" },
98
+ };
99
+ /** Classify `openssl_pkey_new` by the `OPENSSL_KEYTYPE_*` constant in its config.
100
+ * The window is bounded to the current statement (up to the next `;`, capped at
101
+ * 300 chars) so it can't bleed into a following `openssl_pkey_new` call's key
102
+ * type. Defaults to RSA — openssl_pkey_new's own default private_key_type is RSA
103
+ * when none is given. */
104
+ function classifyPkeyNew(content, index) {
105
+ const semi = content.indexOf(";", index);
106
+ const end = Math.min(index + 300, semi === -1 ? content.length : semi);
107
+ const w = content.slice(index, end);
108
+ if (/\bOPENSSL_KEYTYPE_EC\b/.test(w))
109
+ return SECLIB_INFO.EC;
110
+ if (/\bOPENSSL_KEYTYPE_DSA\b/.test(w))
111
+ return SECLIB_INFO.DSA;
112
+ if (/\bOPENSSL_KEYTYPE_DH\b/.test(w))
113
+ return SECLIB_INFO.DH;
114
+ return SECLIB_INFO.RSA;
115
+ }
116
+ /** Detects classical asymmetric crypto in PHP (openssl, phpseclib, libsodium). */
117
+ export const phpDetector = {
118
+ id: "php-crypto",
119
+ description: "Classical asymmetric crypto in PHP (openssl, phpseclib3, libsodium)",
120
+ scope: "source",
121
+ language: "php",
122
+ rules: [
123
+ RULE_PHP_KEYGEN,
124
+ RULE_PHP_RSA_CRYPT,
125
+ RULE_PHP_SIGN,
126
+ RULE_PHP_SECLIB,
127
+ RULE_PHP_SODIUM_X25519,
128
+ RULE_PHP_SODIUM_ED25519,
129
+ ],
130
+ appliesTo: (f) => hasExtension(f, PHP_EXTENSIONS),
131
+ detect({ file, content }) {
132
+ const findings = [];
133
+ const add = (re, rule) => eachMatch(re, content, (m) => findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length })));
134
+ // openssl_pkey_new — one finding per call, classified by its key-type config.
135
+ eachMatch(RE_PHP_PKEY_NEW, content, (m) => {
136
+ const info = classifyPkeyNew(content, m.index);
137
+ findings.push(findingFromRule(RULE_PHP_KEYGEN, { file, content, index: m.index, matchLength: m[0].length }, {
138
+ title: `PHP openssl ${info.label} key generation`,
139
+ category: info.cat,
140
+ severity: info.sev,
141
+ algorithm: info.algo,
142
+ hndl: info.hndl,
143
+ message: `Generates a classical ${info.label} key pair via PHP openssl_pkey_new — not quantum-safe.`,
144
+ ...(info.remediation ? { remediation: info.remediation } : {}),
145
+ }));
146
+ });
147
+ add(RE_PHP_RSA_CRYPT, RULE_PHP_RSA_CRYPT);
148
+ add(RE_PHP_SIGN, RULE_PHP_SIGN);
149
+ // phpseclib3 ::createKey — classify by the class (RSA/EC/DSA/DH).
150
+ eachMatch(RE_PHP_SECLIB, content, (m) => {
151
+ const info = SECLIB_INFO[m[1]];
152
+ findings.push(findingFromRule(RULE_PHP_SECLIB, { file, content, index: m.index, matchLength: m[0].length }, {
153
+ title: `PHP phpseclib ${info.label} key generation`,
154
+ category: info.cat,
155
+ severity: info.sev,
156
+ algorithm: info.algo,
157
+ hndl: info.hndl,
158
+ message: `Generates a classical ${info.label} key pair via phpseclib3 createKey — not quantum-safe.`,
159
+ ...(info.remediation ? { remediation: info.remediation } : {}),
160
+ }));
161
+ });
162
+ add(RE_PHP_SODIUM_X25519, RULE_PHP_SODIUM_X25519);
163
+ add(RE_PHP_SODIUM_ED25519, RULE_PHP_SODIUM_ED25519);
164
+ return findings;
165
+ },
166
+ };
167
+ //# sourceMappingURL=php.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"php.js","sourceRoot":"","sources":["../../src/detectors/php.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical asymmetric cryptography in PHP — one of the\n * most-deployed backend languages, previously uncovered. Handles the three\n * dominant crypto surfaces:\n *\n * - **ext/openssl** — `openssl_pkey_new()` (classified by its\n * `OPENSSL_KEYTYPE_*` config, defaulting to RSA), `openssl_public_encrypt` /\n * `openssl_private_decrypt` (RSA), and `openssl_sign` / `openssl_verify`.\n * - **phpseclib3** — `RSA::createKey()` / `EC::createKey()` / `DSA::createKey()`.\n * - **libsodium** — `sodium_crypto_box`/`kx` keypairs (X25519) and\n * `sodium_crypto_sign` keypairs (Ed25519).\n *\n * Lexical, like the other packs; the `openssl_*` / `sodium_crypto_*` prefixes and\n * `OPENSSL_KEYTYPE_*` constants are distinctive, so the false-positive rate is low.\n *\n * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later\n * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but\n * forgeable. `openssl_pkey_new` EC and `EC::createKey` are ambiguous (feed BOTH\n * ECDSA and ECDH), classified conservatively as key-exchange-capable (hndl:true).\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst RE_PHP_PKEY_NEW = /\\bopenssl_pkey_new\\s*\\(/g;\nconst RE_PHP_RSA_CRYPT = /\\bopenssl_(?:public_encrypt|private_decrypt)\\s*\\(/g;\nconst RE_PHP_SIGN = /\\bopenssl_(?:sign|verify)\\s*\\(/g;\n// phpseclib3 factory methods. The class names are generic, so require `::createKey`.\nconst RE_PHP_SECLIB = /\\b(RSA|EC|DSA|DH)::createKey\\s*\\(/g;\n// libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.\nconst RE_PHP_SODIUM_X25519 =\n /\\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\\s*\\(|\\bsodium_crypto_scalarmult(?:_base)?\\s*\\(/g;\nconst RE_PHP_SODIUM_ED25519 = /\\bsodium_crypto_sign_(?:seed_)?keypair\\s*\\(/g;\n\n/** Key-type classification for `openssl_pkey_new`, keyed by its config constant. */\ninterface KeyInfo {\n algo: Finding[\"algorithm\"];\n cat: Finding[\"category\"];\n sev: Finding[\"severity\"];\n hndl: boolean;\n label: string;\n remediation?: string;\n}\nconst HYBRID = \"hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign\";\n\nconst RULE_PHP_KEYGEN: RuleMeta = {\n id: \"php-openssl-keygen\",\n title: \"PHP openssl key generation\",\n description: \"openssl_pkey_new (RSA/EC/DSA/DH, by OPENSSL_KEYTYPE_*)\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via PHP openssl_pkey_new — not quantum-safe.\",\n};\nconst RULE_PHP_RSA_CRYPT: RuleMeta = {\n id: \"php-openssl-rsa-crypt\",\n title: \"PHP openssl RSA public-key encryption\",\n description: \"openssl_public_encrypt / openssl_private_decrypt\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"RSA public-key encryption/decryption (PHP openssl) is harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SIGN: RuleMeta = {\n id: \"php-openssl-sign\",\n title: \"PHP openssl signature\",\n description: \"openssl_sign / openssl_verify\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical signature via PHP openssl (RSA/ECDSA/DSA) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_PHP_SECLIB: RuleMeta = {\n id: \"php-phpseclib-keygen\",\n title: \"PHP phpseclib key generation\",\n description: \"phpseclib3 RSA/EC/DSA/DH ::createKey\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"medium\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via phpseclib3 (createKey) — not quantum-safe.\",\n};\nconst RULE_PHP_SODIUM_X25519: RuleMeta = {\n id: \"php-sodium-x25519\",\n title: \"PHP libsodium X25519 key agreement\",\n description: \"sodium_crypto_box/kx keypair + scalarmult\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_box/kx (PHP) uses X25519 key agreement — modern but classical, harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SODIUM_ED25519: RuleMeta = {\n id: \"php-sodium-ed25519\",\n title: \"PHP libsodium Ed25519 signature\",\n description: \"sodium_crypto_sign keypair\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_sign (PHP) uses Ed25519 — classical and forgeable by a quantum attacker.\",\n};\n\n/** phpseclib class name → its classification. */\nconst SECLIB_INFO: Record<string, KeyInfo> = {\n RSA: { algo: \"RSA\", cat: \"kem\", sev: \"high\", hndl: true, label: \"RSA\" },\n EC: {\n algo: \"ECDH\",\n cat: \"key-exchange\",\n sev: \"high\",\n hndl: true,\n label: \"EC (ECDSA/ECDH)\",\n remediation: HYBRID,\n },\n DSA: { algo: \"DSA\", cat: \"signature\", sev: \"high\", hndl: false, label: \"DSA\" },\n DH: { algo: \"DH\", cat: \"key-exchange\", sev: \"high\", hndl: true, label: \"Diffie-Hellman\" },\n};\n\n/** Classify `openssl_pkey_new` by the `OPENSSL_KEYTYPE_*` constant in its config.\n * The window is bounded to the current statement (up to the next `;`, capped at\n * 300 chars) so it can't bleed into a following `openssl_pkey_new` call's key\n * type. Defaults to RSA — openssl_pkey_new's own default private_key_type is RSA\n * when none is given. */\nfunction classifyPkeyNew(content: string, index: number): KeyInfo {\n const semi = content.indexOf(\";\", index);\n const end = Math.min(index + 300, semi === -1 ? content.length : semi);\n const w = content.slice(index, end);\n if (/\\bOPENSSL_KEYTYPE_EC\\b/.test(w)) return SECLIB_INFO.EC;\n if (/\\bOPENSSL_KEYTYPE_DSA\\b/.test(w)) return SECLIB_INFO.DSA;\n if (/\\bOPENSSL_KEYTYPE_DH\\b/.test(w)) return SECLIB_INFO.DH;\n return SECLIB_INFO.RSA;\n}\n\n/** Detects classical asymmetric crypto in PHP (openssl, phpseclib, libsodium). */\nexport const phpDetector: Detector = {\n id: \"php-crypto\",\n description: \"Classical asymmetric crypto in PHP (openssl, phpseclib3, libsodium)\",\n scope: \"source\",\n language: \"php\",\n rules: [\n RULE_PHP_KEYGEN,\n RULE_PHP_RSA_CRYPT,\n RULE_PHP_SIGN,\n RULE_PHP_SECLIB,\n RULE_PHP_SODIUM_X25519,\n RULE_PHP_SODIUM_ED25519,\n ],\n appliesTo: (f) => hasExtension(f, PHP_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta): void =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n\n // openssl_pkey_new — one finding per call, classified by its key-type config.\n eachMatch(RE_PHP_PKEY_NEW, content, (m) => {\n const info = classifyPkeyNew(content, m.index);\n findings.push(\n findingFromRule(\n RULE_PHP_KEYGEN,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP openssl ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via PHP openssl_pkey_new — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_RSA_CRYPT, RULE_PHP_RSA_CRYPT);\n add(RE_PHP_SIGN, RULE_PHP_SIGN);\n\n // phpseclib3 ::createKey — classify by the class (RSA/EC/DSA/DH).\n eachMatch(RE_PHP_SECLIB, content, (m) => {\n const info = SECLIB_INFO[m[1]];\n findings.push(\n findingFromRule(\n RULE_PHP_SECLIB,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP phpseclib ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via phpseclib3 createKey — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_SODIUM_X25519, RULE_PHP_SODIUM_X25519);\n add(RE_PHP_SODIUM_ED25519, RULE_PHP_SODIUM_ED25519);\n return findings;\n },\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/detectors/python.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA6N/D,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,QA8C5B,CAAC"}
1
+ {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/detectors/python.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAsR/D,4DAA4D;AAC5D,eAAO,MAAM,cAAc,EAAE,QA4D5B,CAAC"}
@@ -198,6 +198,63 @@ const RULE_PY_TLS_LEGACY = {
198
198
  message: "TLS 1.0 (ssl.PROTOCOL_TLSv1) is deprecated and insecure; require TLS 1.3.",
199
199
  remediation: "Use ssl.PROTOCOL_TLS_CLIENT with minimum_version = ssl.TLSVersion.TLSv1_3 and prefer PQC-hybrid key exchange.",
200
200
  };
201
+ /**
202
+ * Aliasable `cryptography` / PyCryptodome modules → the (method, rule) pairs
203
+ * reachable through them. Lets an aliased module import
204
+ * (`from ... import rsa as _rsa` → `_rsa.generate_private_key(`) resolve back to
205
+ * the same rule the direct `rsa.generate_private_key(` would fire, since the
206
+ * detector's regexes are module-qualified and miss a renamed prefix.
207
+ */
208
+ const PY_MODULE_RULES = {
209
+ rsa: [{ method: "generate_private_key", rule: RULE_PY_RSA_KEYGEN }],
210
+ ec: [
211
+ { method: "generate_private_key", rule: RULE_PY_EC_KEYGEN },
212
+ { method: "ECDSA", rule: RULE_PY_ECDSA },
213
+ { method: "ECDH", rule: RULE_PY_ECDH },
214
+ ],
215
+ dsa: [{ method: "generate_private_key", rule: RULE_PY_HAZMAT_DSA }],
216
+ dh: [{ method: "generate_parameters", rule: RULE_PY_DH }],
217
+ padding: [{ method: "OAEP", rule: RULE_PY_RSA_ENCRYPT }],
218
+ // PyCryptodome factory modules (`.generate(`).
219
+ RSA: [{ method: "generate", rule: RULE_PY_RSA_KEYGEN }],
220
+ ECC: [{ method: "generate", rule: RULE_PY_EC_KEYGEN }],
221
+ DSA: [{ method: "generate", rule: RULE_PY_DSA }],
222
+ };
223
+ /** Escape a string for interpolation into a dynamically-built RegExp. */
224
+ function escapePyRe(s) {
225
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
226
+ }
227
+ /**
228
+ * Collect module aliases for the aliasable crypto modules, so a later
229
+ * `<alias>.<method>(` call still resolves. Handles both `from <path> import
230
+ * <mod> as <alias>` (including comma-separated specifiers) and the bare
231
+ * `import <path.mod> as <alias>`. Returns Map<module, alias[]>; an alias equal to
232
+ * its own module name is skipped (matched directly already).
233
+ */
234
+ function collectPyModuleAliases(content) {
235
+ const out = new Map();
236
+ const add = (mod, alias) => {
237
+ if (!alias || alias === mod || !(mod in PY_MODULE_RULES))
238
+ return;
239
+ const list = out.get(mod) ?? [];
240
+ if (!list.includes(alias))
241
+ list.push(alias);
242
+ out.set(mod, list);
243
+ };
244
+ // `from <path> import a as b, c as d` — scan the import list for `X as Y`.
245
+ const fromRe = /(?:^|\n)[ \t]*from\s+[\w.]+\s+import\s+([^\n#]+)/g;
246
+ for (let m = fromRe.exec(content); m; m = fromRe.exec(content)) {
247
+ const specRe = /([A-Za-z_]\w*)\s+as\s+([A-Za-z_]\w*)/g;
248
+ for (let s = specRe.exec(m[1]); s; s = specRe.exec(m[1]))
249
+ add(s[1], s[2]);
250
+ }
251
+ // `import <path.mod> as <alias>` — the aliased module is the last dotted segment.
252
+ const impRe = /(?:^|\n)[ \t]*import\s+([\w.]+)\s+as\s+([A-Za-z_]\w*)/g;
253
+ for (let m = impRe.exec(content); m; m = impRe.exec(content)) {
254
+ add(m[1].split(".").pop() ?? m[1], m[2]);
255
+ }
256
+ return out;
257
+ }
201
258
  /** Detects classical asymmetric crypto in Python source. */
202
259
  export const pythonDetector = {
203
260
  id: "python-crypto",
@@ -236,6 +293,19 @@ export const pythonDetector = {
236
293
  add(RE_PY_EDDSA, RULE_PY_EDDSA);
237
294
  add(RE_PY_TLS_REJECT, RULE_PY_TLS_REJECT);
238
295
  add(RE_PY_TLS_LEGACY, RULE_PY_TLS_LEGACY);
296
+ // Module-alias resolution: `from ... import rsa as _rsa` then
297
+ // `_rsa.generate_private_key(` — the direct regexes are module-qualified
298
+ // (`\brsa\.`) and miss a renamed prefix. Runs on the ORIGINAL content so
299
+ // locations stay exact; fires only for an alias explicitly bound to a known
300
+ // crypto module, so precision is unaffected.
301
+ for (const [mod, aliasList] of collectPyModuleAliases(content)) {
302
+ for (const alias of aliasList) {
303
+ const a = escapePyRe(alias);
304
+ for (const { method, rule } of PY_MODULE_RULES[mod]) {
305
+ add(new RegExp(`\\b${a}\\.${method}\\s*\\(`, "g"), rule);
306
+ }
307
+ }
308
+ }
239
309
  return findings;
240
310
  },
241
311
  };
@@ -1 +1 @@
1
- {"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/detectors/python.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical, non-quantum-safe asymmetric cryptography\n * in Python. Same lexical (regex-over-source) strategy as the JS/TS detectors:\n * robust to formatting, dependency-free, and confidence-tagged per pattern.\n *\n * Covers the three libraries that carry ~all real-world Python asymmetric\n * crypto:\n * - `cryptography` (hazmat): rsa/ec/dh/x25519/x448/ed25519/ed448 + OAEP padding\n * - PyCryptodome (`Crypto.PublicKey`): RSA/ECC/DSA generate + PKCS1_OAEP\n * - paramiko (SSH): RSAKey / ECDSAKey / Ed25519Key / DSSKey\n *\n * JWT/JOSE algorithm strings (`algorithm=\"RS256\"`) are NOT handled here — the\n * language-agnostic `jwt-jose` detector now applies to Python too (a quoted\n * `\"RS256\"` is the same evidence in any language).\n *\n * HNDL policy mirrors the JS detectors: key agreement / KEM (RSA-OAEP, ECDH, DH,\n * X25519/X448) is harvest-now-decrypt-later exposed (hndl:true); signatures\n * (ECDSA, DSA, Ed25519/Ed448) are hndl:false but still high/low severity because\n * a quantum attacker can forge them. EC key generation is ambiguous (an EC key\n * feeds both ECDSA and ECDH), so it is classified conservatively as\n * key-exchange-capable (hndl:true), exactly as the Node `ec` keygen rule is.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { PYTHON_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from \"../cwe.js\";\n\n/* -------------------------------------------------------------------------- */\n/* Precompiled regexes (module scope) */\n/* -------------------------------------------------------------------------- */\n\n// RSA key generation: cryptography `rsa.generate_private_key(`, PyCryptodome\n// `RSA.generate(`, paramiko `paramiko.RSAKey` / `RSAKey.generate(`.\nconst RE_PY_RSA_KEYGEN =\n /\\brsa\\.generate_private_key\\s*\\(|\\bRSA\\.generate\\s*\\(|\\bparamiko\\.RSAKey\\b|\\bRSAKey\\.generate\\s*\\(/g;\n// RSA public-key encryption padding (RSA-OAEP): confidentiality → HNDL.\nconst RE_PY_RSA_ENCRYPT = /\\bpadding\\.OAEP\\s*\\(|\\bPKCS1_OAEP\\.new\\s*\\(/g;\n// EC key generation (ambiguous ECDSA/ECDH): cryptography + PyCryptodome.\nconst RE_PY_EC_KEYGEN = /\\bec\\.generate_private_key\\s*\\(|\\bECC\\.generate\\s*\\(/g;\n// ECDSA signatures: cryptography `ec.ECDSA(`, paramiko ECDSAKey.\nconst RE_PY_ECDSA = /\\bec\\.ECDSA\\s*\\(|\\bparamiko\\.ECDSAKey\\b|\\bECDSAKey\\.generate\\s*\\(/g;\n// ECDH key agreement: cryptography `ec.ECDH(` — the actual harvest-now event in\n// `private_key.exchange(ec.ECDH(), peer)`; previously missed entirely (audit F3).\nconst RE_PY_ECDH = /\\bec\\.ECDH\\s*\\(/g;\n// DSA: PyCryptodome `DSA.generate(`, paramiko DSSKey.\nconst RE_PY_DSA = /\\bDSA\\.generate\\s*\\(|\\bparamiko\\.DSSKey\\b|\\bDSSKey\\.generate\\s*\\(/g;\n// hazmat DSA: cryptography `dsa.generate_private_key(` — the `cryptography` DSA\n// keygen path (lowercase module) that RE_PY_DSA's uppercase `DSA.generate` misses\n// (audit F9-python).\nconst RE_PY_HAZMAT_DSA = /\\bdsa\\.generate_private_key\\s*\\(/g;\n// Finite-field Diffie-Hellman (cryptography `dh`).\nconst RE_PY_DH = /\\bdh\\.generate_parameters\\s*\\(|\\bdh\\.DHParameterNumbers\\s*\\(/g;\n// Modern-but-classical curve primitives (cryptography). `.generate(` anchors so\n// a bare import of the class name does not fire.\nconst RE_PY_X25519 = /\\bX25519PrivateKey\\.generate\\s*\\(/g;\nconst RE_PY_X448 = /\\bX448PrivateKey\\.generate\\s*\\(/g;\nconst RE_PY_EDDSA = /\\b(?:Ed25519|Ed448)PrivateKey\\.generate\\s*\\(|\\bparamiko\\.Ed25519Key\\b/g;\n\n// Python TLS misconfiguration. Mirrors the JS `tlsDetector` split of source.ts:\n// certificate-verification bypass (requests `verify=False`, `ssl.CERT_NONE`,\n// `check_hostname=False`, `ssl._create_unverified_context(`) is the high-severity\n// MITM surface (CWE-295); a pinned legacy protocol (`ssl.PROTOCOL_TLSv1`) is the\n// medium-severity weak-strength surface (CWE-326).\nconst RE_PY_TLS_REJECT =\n /\\bverify\\s*=\\s*False\\b|\\bssl\\.CERT_NONE\\b|\\bcheck_hostname\\s*=\\s*False\\b|\\bssl\\._create_unverified_context\\s*\\(/g;\nconst RE_PY_TLS_LEGACY = /\\bPROTOCOL_TLSv1\\b/g;\n\n/* -------------------------------------------------------------------------- */\n/* Rule catalog */\n/* -------------------------------------------------------------------------- */\n\nconst RULE_PY_RSA_KEYGEN: RuleMeta = {\n id: \"python-rsa-keygen\",\n title: \"Python RSA key generation\",\n description:\n \"cryptography rsa.generate_private_key / PyCryptodome RSA.generate / paramiko RSAKey\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical RSA key pair (Python), which is not quantum-safe.\",\n};\nconst RULE_PY_RSA_ENCRYPT: RuleMeta = {\n id: \"python-rsa-encrypt\",\n title: \"Python RSA public-key encryption\",\n description: \"RSA-OAEP / PKCS1_OAEP encryption padding\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"RSA public-key encryption (OAEP) is broken by Shor's algorithm and exposed to harvest-now-decrypt-later.\",\n};\nconst RULE_PY_EC_KEYGEN: RuleMeta = {\n id: \"python-ec-keygen\",\n title: \"Python EC key generation\",\n description: \"cryptography ec.generate_private_key / PyCryptodome ECC.generate\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical EC key pair (Python). EC keys feed BOTH 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_PY_ECDSA: RuleMeta = {\n id: \"python-ecdsa\",\n title: \"Python ECDSA signature\",\n description: \"cryptography ec.ECDSA / paramiko ECDSAKey\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"medium\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical ECDSA signing (Python) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_PY_ECDH: RuleMeta = {\n id: \"python-ecdh\",\n title: \"Python ECDH key agreement\",\n description: \"cryptography ec.ECDH() exchange\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Elliptic-curve Diffie-Hellman key agreement (Python) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_PY_DSA: RuleMeta = {\n id: \"python-dsa\",\n title: \"Python DSA key/usage\",\n description: \"PyCryptodome DSA.generate / paramiko DSSKey\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical DSA (Python) is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_PY_HAZMAT_DSA: RuleMeta = {\n id: \"python-hazmat-dsa\",\n title: \"Python DSA key generation (cryptography)\",\n description: \"cryptography dsa.generate_private_key\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"cryptography dsa.generate_private_key (Python) creates a classical DSA key; DSA is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_PY_DH: RuleMeta = {\n id: \"python-dh\",\n title: \"Python Diffie-Hellman key exchange\",\n description: \"cryptography dh.generate_parameters / DHParameterNumbers\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Finite-field Diffie-Hellman (Python) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_PY_X25519: RuleMeta = {\n id: \"python-x25519\",\n title: \"Python X25519 key exchange\",\n description: \"cryptography X25519PrivateKey.generate\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 (Python) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_PY_X448: RuleMeta = {\n id: \"python-x448\",\n title: \"Python X448 key exchange\",\n description: \"cryptography X448PrivateKey.generate\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X448\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"X448 (Python) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_PY_EDDSA: RuleMeta = {\n id: \"python-eddsa\",\n title: \"Python Ed25519/Ed448 signature\",\n description: \"cryptography Ed25519/Ed448 PrivateKey.generate / paramiko Ed25519Key\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Ed25519/Ed448 (Python) is a modern but still classical signature scheme.\",\n};\nconst RULE_PY_TLS_REJECT: RuleMeta = {\n id: \"python-tls-reject\",\n title: \"Python TLS certificate verification disabled\",\n description:\n \"requests verify=False / ssl.CERT_NONE / check_hostname=False / _create_unverified_context\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message:\n \"TLS certificate verification is disabled (verify=False / CERT_NONE / check_hostname=False / _create_unverified_context), which allows man-in-the-middle attacks.\",\n remediation:\n \"Enable certificate verification (verify=True, ssl.CERT_REQUIRED, check_hostname=True) and verify certificates properly.\",\n};\nconst RULE_PY_TLS_LEGACY: RuleMeta = {\n id: \"python-tls-legacy-version\",\n title: \"Python legacy TLS version pinned\",\n description: \"ssl.PROTOCOL_TLSv1 (TLS 1.0)\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_WEAK_STRENGTH,\n message: \"TLS 1.0 (ssl.PROTOCOL_TLSv1) is deprecated and insecure; require TLS 1.3.\",\n remediation:\n \"Use ssl.PROTOCOL_TLS_CLIENT with minimum_version = ssl.TLSVersion.TLSv1_3 and prefer PQC-hybrid key exchange.\",\n};\n\n/** Detects classical asymmetric crypto in Python source. */\nexport const pythonDetector: Detector = {\n id: \"python-crypto\",\n description: \"Classical asymmetric crypto in Python (cryptography, PyCryptodome, paramiko)\",\n scope: \"source\",\n language: \"python\",\n rules: [\n RULE_PY_RSA_KEYGEN,\n RULE_PY_RSA_ENCRYPT,\n RULE_PY_EC_KEYGEN,\n RULE_PY_ECDSA,\n RULE_PY_ECDH,\n RULE_PY_DSA,\n RULE_PY_HAZMAT_DSA,\n RULE_PY_DH,\n RULE_PY_X25519,\n RULE_PY_X448,\n RULE_PY_EDDSA,\n RULE_PY_TLS_REJECT,\n RULE_PY_TLS_LEGACY,\n ],\n appliesTo: (f) => hasExtension(f, PYTHON_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n\n add(RE_PY_RSA_KEYGEN, RULE_PY_RSA_KEYGEN);\n add(RE_PY_RSA_ENCRYPT, RULE_PY_RSA_ENCRYPT);\n add(RE_PY_EC_KEYGEN, RULE_PY_EC_KEYGEN);\n add(RE_PY_ECDSA, RULE_PY_ECDSA);\n add(RE_PY_ECDH, RULE_PY_ECDH);\n add(RE_PY_DSA, RULE_PY_DSA);\n add(RE_PY_HAZMAT_DSA, RULE_PY_HAZMAT_DSA);\n add(RE_PY_DH, RULE_PY_DH);\n add(RE_PY_X25519, RULE_PY_X25519);\n add(RE_PY_X448, RULE_PY_X448);\n add(RE_PY_EDDSA, RULE_PY_EDDSA);\n add(RE_PY_TLS_REJECT, RULE_PY_TLS_REJECT);\n add(RE_PY_TLS_LEGACY, RULE_PY_TLS_LEGACY);\n\n return findings;\n },\n};\n"]}
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/detectors/python.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical, non-quantum-safe asymmetric cryptography\n * in Python. Same lexical (regex-over-source) strategy as the JS/TS detectors:\n * robust to formatting, dependency-free, and confidence-tagged per pattern.\n *\n * Covers the three libraries that carry ~all real-world Python asymmetric\n * crypto:\n * - `cryptography` (hazmat): rsa/ec/dh/x25519/x448/ed25519/ed448 + OAEP padding\n * - PyCryptodome (`Crypto.PublicKey`): RSA/ECC/DSA generate + PKCS1_OAEP\n * - paramiko (SSH): RSAKey / ECDSAKey / Ed25519Key / DSSKey\n *\n * JWT/JOSE algorithm strings (`algorithm=\"RS256\"`) are NOT handled here — the\n * language-agnostic `jwt-jose` detector now applies to Python too (a quoted\n * `\"RS256\"` is the same evidence in any language).\n *\n * HNDL policy mirrors the JS detectors: key agreement / KEM (RSA-OAEP, ECDH, DH,\n * X25519/X448) is harvest-now-decrypt-later exposed (hndl:true); signatures\n * (ECDSA, DSA, Ed25519/Ed448) are hndl:false but still high/low severity because\n * a quantum attacker can forge them. EC key generation is ambiguous (an EC key\n * feeds both ECDSA and ECDH), so it is classified conservatively as\n * key-exchange-capable (hndl:true), exactly as the Node `ec` keygen rule is.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { PYTHON_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from \"../cwe.js\";\n\n/* -------------------------------------------------------------------------- */\n/* Precompiled regexes (module scope) */\n/* -------------------------------------------------------------------------- */\n\n// RSA key generation: cryptography `rsa.generate_private_key(`, PyCryptodome\n// `RSA.generate(`, paramiko `paramiko.RSAKey` / `RSAKey.generate(`.\nconst RE_PY_RSA_KEYGEN =\n /\\brsa\\.generate_private_key\\s*\\(|\\bRSA\\.generate\\s*\\(|\\bparamiko\\.RSAKey\\b|\\bRSAKey\\.generate\\s*\\(/g;\n// RSA public-key encryption padding (RSA-OAEP): confidentiality → HNDL.\nconst RE_PY_RSA_ENCRYPT = /\\bpadding\\.OAEP\\s*\\(|\\bPKCS1_OAEP\\.new\\s*\\(/g;\n// EC key generation (ambiguous ECDSA/ECDH): cryptography + PyCryptodome.\nconst RE_PY_EC_KEYGEN = /\\bec\\.generate_private_key\\s*\\(|\\bECC\\.generate\\s*\\(/g;\n// ECDSA signatures: cryptography `ec.ECDSA(`, paramiko ECDSAKey.\nconst RE_PY_ECDSA = /\\bec\\.ECDSA\\s*\\(|\\bparamiko\\.ECDSAKey\\b|\\bECDSAKey\\.generate\\s*\\(/g;\n// ECDH key agreement: cryptography `ec.ECDH(` — the actual harvest-now event in\n// `private_key.exchange(ec.ECDH(), peer)`; previously missed entirely (audit F3).\nconst RE_PY_ECDH = /\\bec\\.ECDH\\s*\\(/g;\n// DSA: PyCryptodome `DSA.generate(`, paramiko DSSKey.\nconst RE_PY_DSA = /\\bDSA\\.generate\\s*\\(|\\bparamiko\\.DSSKey\\b|\\bDSSKey\\.generate\\s*\\(/g;\n// hazmat DSA: cryptography `dsa.generate_private_key(` — the `cryptography` DSA\n// keygen path (lowercase module) that RE_PY_DSA's uppercase `DSA.generate` misses\n// (audit F9-python).\nconst RE_PY_HAZMAT_DSA = /\\bdsa\\.generate_private_key\\s*\\(/g;\n// Finite-field Diffie-Hellman (cryptography `dh`).\nconst RE_PY_DH = /\\bdh\\.generate_parameters\\s*\\(|\\bdh\\.DHParameterNumbers\\s*\\(/g;\n// Modern-but-classical curve primitives (cryptography). `.generate(` anchors so\n// a bare import of the class name does not fire.\nconst RE_PY_X25519 = /\\bX25519PrivateKey\\.generate\\s*\\(/g;\nconst RE_PY_X448 = /\\bX448PrivateKey\\.generate\\s*\\(/g;\nconst RE_PY_EDDSA = /\\b(?:Ed25519|Ed448)PrivateKey\\.generate\\s*\\(|\\bparamiko\\.Ed25519Key\\b/g;\n\n// Python TLS misconfiguration. Mirrors the JS `tlsDetector` split of source.ts:\n// certificate-verification bypass (requests `verify=False`, `ssl.CERT_NONE`,\n// `check_hostname=False`, `ssl._create_unverified_context(`) is the high-severity\n// MITM surface (CWE-295); a pinned legacy protocol (`ssl.PROTOCOL_TLSv1`) is the\n// medium-severity weak-strength surface (CWE-326).\nconst RE_PY_TLS_REJECT =\n /\\bverify\\s*=\\s*False\\b|\\bssl\\.CERT_NONE\\b|\\bcheck_hostname\\s*=\\s*False\\b|\\bssl\\._create_unverified_context\\s*\\(/g;\nconst RE_PY_TLS_LEGACY = /\\bPROTOCOL_TLSv1\\b/g;\n\n/* -------------------------------------------------------------------------- */\n/* Rule catalog */\n/* -------------------------------------------------------------------------- */\n\nconst RULE_PY_RSA_KEYGEN: RuleMeta = {\n id: \"python-rsa-keygen\",\n title: \"Python RSA key generation\",\n description:\n \"cryptography rsa.generate_private_key / PyCryptodome RSA.generate / paramiko RSAKey\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical RSA key pair (Python), which is not quantum-safe.\",\n};\nconst RULE_PY_RSA_ENCRYPT: RuleMeta = {\n id: \"python-rsa-encrypt\",\n title: \"Python RSA public-key encryption\",\n description: \"RSA-OAEP / PKCS1_OAEP encryption padding\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"RSA public-key encryption (OAEP) is broken by Shor's algorithm and exposed to harvest-now-decrypt-later.\",\n};\nconst RULE_PY_EC_KEYGEN: RuleMeta = {\n id: \"python-ec-keygen\",\n title: \"Python EC key generation\",\n description: \"cryptography ec.generate_private_key / PyCryptodome ECC.generate\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical EC key pair (Python). EC keys feed BOTH 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_PY_ECDSA: RuleMeta = {\n id: \"python-ecdsa\",\n title: \"Python ECDSA signature\",\n description: \"cryptography ec.ECDSA / paramiko ECDSAKey\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"medium\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical ECDSA signing (Python) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_PY_ECDH: RuleMeta = {\n id: \"python-ecdh\",\n title: \"Python ECDH key agreement\",\n description: \"cryptography ec.ECDH() exchange\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Elliptic-curve Diffie-Hellman key agreement (Python) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_PY_DSA: RuleMeta = {\n id: \"python-dsa\",\n title: \"Python DSA key/usage\",\n description: \"PyCryptodome DSA.generate / paramiko DSSKey\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical DSA (Python) is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_PY_HAZMAT_DSA: RuleMeta = {\n id: \"python-hazmat-dsa\",\n title: \"Python DSA key generation (cryptography)\",\n description: \"cryptography dsa.generate_private_key\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"cryptography dsa.generate_private_key (Python) creates a classical DSA key; DSA is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_PY_DH: RuleMeta = {\n id: \"python-dh\",\n title: \"Python Diffie-Hellman key exchange\",\n description: \"cryptography dh.generate_parameters / DHParameterNumbers\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Finite-field Diffie-Hellman (Python) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_PY_X25519: RuleMeta = {\n id: \"python-x25519\",\n title: \"Python X25519 key exchange\",\n description: \"cryptography X25519PrivateKey.generate\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 (Python) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_PY_X448: RuleMeta = {\n id: \"python-x448\",\n title: \"Python X448 key exchange\",\n description: \"cryptography X448PrivateKey.generate\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X448\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"X448 (Python) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_PY_EDDSA: RuleMeta = {\n id: \"python-eddsa\",\n title: \"Python Ed25519/Ed448 signature\",\n description: \"cryptography Ed25519/Ed448 PrivateKey.generate / paramiko Ed25519Key\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Ed25519/Ed448 (Python) is a modern but still classical signature scheme.\",\n};\nconst RULE_PY_TLS_REJECT: RuleMeta = {\n id: \"python-tls-reject\",\n title: \"Python TLS certificate verification disabled\",\n description:\n \"requests verify=False / ssl.CERT_NONE / check_hostname=False / _create_unverified_context\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message:\n \"TLS certificate verification is disabled (verify=False / CERT_NONE / check_hostname=False / _create_unverified_context), which allows man-in-the-middle attacks.\",\n remediation:\n \"Enable certificate verification (verify=True, ssl.CERT_REQUIRED, check_hostname=True) and verify certificates properly.\",\n};\nconst RULE_PY_TLS_LEGACY: RuleMeta = {\n id: \"python-tls-legacy-version\",\n title: \"Python legacy TLS version pinned\",\n description: \"ssl.PROTOCOL_TLSv1 (TLS 1.0)\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_WEAK_STRENGTH,\n message: \"TLS 1.0 (ssl.PROTOCOL_TLSv1) is deprecated and insecure; require TLS 1.3.\",\n remediation:\n \"Use ssl.PROTOCOL_TLS_CLIENT with minimum_version = ssl.TLSVersion.TLSv1_3 and prefer PQC-hybrid key exchange.\",\n};\n\n/**\n * Aliasable `cryptography` / PyCryptodome modules → the (method, rule) pairs\n * reachable through them. Lets an aliased module import\n * (`from ... import rsa as _rsa` → `_rsa.generate_private_key(`) resolve back to\n * the same rule the direct `rsa.generate_private_key(` would fire, since the\n * detector's regexes are module-qualified and miss a renamed prefix.\n */\nconst PY_MODULE_RULES: Record<string, ReadonlyArray<{ method: string; rule: RuleMeta }>> = {\n rsa: [{ method: \"generate_private_key\", rule: RULE_PY_RSA_KEYGEN }],\n ec: [\n { method: \"generate_private_key\", rule: RULE_PY_EC_KEYGEN },\n { method: \"ECDSA\", rule: RULE_PY_ECDSA },\n { method: \"ECDH\", rule: RULE_PY_ECDH },\n ],\n dsa: [{ method: \"generate_private_key\", rule: RULE_PY_HAZMAT_DSA }],\n dh: [{ method: \"generate_parameters\", rule: RULE_PY_DH }],\n padding: [{ method: \"OAEP\", rule: RULE_PY_RSA_ENCRYPT }],\n // PyCryptodome factory modules (`.generate(`).\n RSA: [{ method: \"generate\", rule: RULE_PY_RSA_KEYGEN }],\n ECC: [{ method: \"generate\", rule: RULE_PY_EC_KEYGEN }],\n DSA: [{ method: \"generate\", rule: RULE_PY_DSA }],\n};\n\n/** Escape a string for interpolation into a dynamically-built RegExp. */\nfunction escapePyRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Collect module aliases for the aliasable crypto modules, so a later\n * `<alias>.<method>(` call still resolves. Handles both `from <path> import\n * <mod> as <alias>` (including comma-separated specifiers) and the bare\n * `import <path.mod> as <alias>`. Returns Map<module, alias[]>; an alias equal to\n * its own module name is skipped (matched directly already).\n */\nfunction collectPyModuleAliases(content: string): Map<string, string[]> {\n const out = new Map<string, string[]>();\n const add = (mod: string, alias: string): void => {\n if (!alias || alias === mod || !(mod in PY_MODULE_RULES)) return;\n const list = out.get(mod) ?? [];\n if (!list.includes(alias)) list.push(alias);\n out.set(mod, list);\n };\n // `from <path> import a as b, c as d` — scan the import list for `X as Y`.\n const fromRe = /(?:^|\\n)[ \\t]*from\\s+[\\w.]+\\s+import\\s+([^\\n#]+)/g;\n for (let m = fromRe.exec(content); m; m = fromRe.exec(content)) {\n const specRe = /([A-Za-z_]\\w*)\\s+as\\s+([A-Za-z_]\\w*)/g;\n for (let s = specRe.exec(m[1]); s; s = specRe.exec(m[1])) add(s[1], s[2]);\n }\n // `import <path.mod> as <alias>` — the aliased module is the last dotted segment.\n const impRe = /(?:^|\\n)[ \\t]*import\\s+([\\w.]+)\\s+as\\s+([A-Za-z_]\\w*)/g;\n for (let m = impRe.exec(content); m; m = impRe.exec(content)) {\n add(m[1].split(\".\").pop() ?? m[1], m[2]);\n }\n return out;\n}\n\n/** Detects classical asymmetric crypto in Python source. */\nexport const pythonDetector: Detector = {\n id: \"python-crypto\",\n description: \"Classical asymmetric crypto in Python (cryptography, PyCryptodome, paramiko)\",\n scope: \"source\",\n language: \"python\",\n rules: [\n RULE_PY_RSA_KEYGEN,\n RULE_PY_RSA_ENCRYPT,\n RULE_PY_EC_KEYGEN,\n RULE_PY_ECDSA,\n RULE_PY_ECDH,\n RULE_PY_DSA,\n RULE_PY_HAZMAT_DSA,\n RULE_PY_DH,\n RULE_PY_X25519,\n RULE_PY_X448,\n RULE_PY_EDDSA,\n RULE_PY_TLS_REJECT,\n RULE_PY_TLS_LEGACY,\n ],\n appliesTo: (f) => hasExtension(f, PYTHON_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n\n add(RE_PY_RSA_KEYGEN, RULE_PY_RSA_KEYGEN);\n add(RE_PY_RSA_ENCRYPT, RULE_PY_RSA_ENCRYPT);\n add(RE_PY_EC_KEYGEN, RULE_PY_EC_KEYGEN);\n add(RE_PY_ECDSA, RULE_PY_ECDSA);\n add(RE_PY_ECDH, RULE_PY_ECDH);\n add(RE_PY_DSA, RULE_PY_DSA);\n add(RE_PY_HAZMAT_DSA, RULE_PY_HAZMAT_DSA);\n add(RE_PY_DH, RULE_PY_DH);\n add(RE_PY_X25519, RULE_PY_X25519);\n add(RE_PY_X448, RULE_PY_X448);\n add(RE_PY_EDDSA, RULE_PY_EDDSA);\n add(RE_PY_TLS_REJECT, RULE_PY_TLS_REJECT);\n add(RE_PY_TLS_LEGACY, RULE_PY_TLS_LEGACY);\n\n // Module-alias resolution: `from ... import rsa as _rsa` then\n // `_rsa.generate_private_key(` — the direct regexes are module-qualified\n // (`\\brsa\\.`) and miss a renamed prefix. Runs on the ORIGINAL content so\n // locations stay exact; fires only for an alias explicitly bound to a known\n // crypto module, so precision is unaffected.\n for (const [mod, aliasList] of collectPyModuleAliases(content)) {\n for (const alias of aliasList) {\n const a = escapePyRe(alias);\n for (const { method, rule } of PY_MODULE_RULES[mod]) {\n add(new RegExp(`\\\\b${a}\\\\.${method}\\\\s*\\\\(`, \"g\"), rule);\n }\n }\n }\n\n return findings;\n },\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA+OhF,iFAAiF;AACjF,eAAO,MAAM,YAAY,EAAE,QA6D1B,CAAC"}
1
+ {"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAwShF,iFAAiF;AACjF,eAAO,MAAM,YAAY,EAAE,QA2E1B,CAAC"}
@@ -97,6 +97,18 @@ const RULE_RUST_X25519 = {
97
97
  cwe: CWE_BROKEN_CRYPTO,
98
98
  message: "X25519 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.",
99
99
  };
100
+ const RULE_RUST_X448 = {
101
+ id: "rust-x448",
102
+ title: "Rust X448 key agreement",
103
+ description: "the `x448` crate Secret key agreement",
104
+ category: "key-exchange",
105
+ severity: "medium",
106
+ confidence: "high",
107
+ algorithm: "X448",
108
+ hndl: true,
109
+ cwe: CWE_BROKEN_CRYPTO,
110
+ message: "X448 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.",
111
+ };
100
112
  const RULE_RUST_OPENSSL_RSA = {
101
113
  id: "rust-openssl-rsa",
102
114
  title: "Rust openssl RSA key generation",
@@ -222,6 +234,52 @@ const RULE_RUST_TLS_DANGEROUS = {
222
234
  message: "rustls `.dangerous()` opts into disabling certificate verification (MITM risk).",
223
235
  remediation: "Avoid the dangerous() escape hatch; keep the default certificate verifier.",
224
236
  };
237
+ /**
238
+ * Aliasable Rust crypto types (`<crate>::<OrigType>` → rule). A renamed `use`
239
+ * (`use x25519_dalek::{EphemeralSecret as MontgomerySecret}`) binds the alias to a
240
+ * classical-key type; a later `MontgomerySecret::<ctor>(` is that key's
241
+ * construction. The `::`-qualified and braced rules above miss this because the
242
+ * call site uses the renamed identifier, not the original path.
243
+ */
244
+ const RUST_ALIASABLE = {
245
+ "x25519_dalek::EphemeralSecret": RULE_RUST_X25519,
246
+ "x25519_dalek::StaticSecret": RULE_RUST_X25519,
247
+ "x448::Secret": RULE_RUST_X448,
248
+ "ed25519_dalek::SigningKey": RULE_RUST_ED25519,
249
+ "ed25519_dalek::Keypair": RULE_RUST_ED25519,
250
+ "ed25519_dalek::SecretKey": RULE_RUST_ED25519,
251
+ };
252
+ /** Escape a string for interpolation into a dynamically-built RegExp. */
253
+ function escapeRustRe(s) {
254
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
255
+ }
256
+ /**
257
+ * Collect `use <crate>::<OrigType> as <Alias>` bindings (braced or single) that
258
+ * rename an aliasable crypto type, returning {alias, rule} pairs. An alias equal
259
+ * to its own type name is skipped (the qualified/bare rules already catch it).
260
+ */
261
+ function collectRustTypeAliases(content) {
262
+ const out = [];
263
+ const push = (crate, orig, alias) => {
264
+ if (!alias || alias === orig)
265
+ return;
266
+ const rule = RUST_ALIASABLE[`${crate}::${orig}`];
267
+ if (rule)
268
+ out.push({ alias, rule });
269
+ };
270
+ // Braced: `use <crate>::{ Orig as Alias, ... }`.
271
+ const braced = /\buse\s+([\w:]+)::\{([^}]*)\}/g;
272
+ for (let m = braced.exec(content); m; m = braced.exec(content)) {
273
+ const specRe = /([A-Za-z_]\w*)\s+as\s+([A-Za-z_]\w*)/g;
274
+ for (let s = specRe.exec(m[2]); s; s = specRe.exec(m[2]))
275
+ push(m[1], s[1], s[2]);
276
+ }
277
+ // Single: `use <crate>::Orig as Alias;`.
278
+ const single = /\buse\s+([\w:]+)::([A-Za-z_]\w*)\s+as\s+([A-Za-z_]\w*)/g;
279
+ for (let m = single.exec(content); m; m = single.exec(content))
280
+ push(m[1], m[2], m[3]);
281
+ return out;
282
+ }
225
283
  /** Detects classical asymmetric crypto in Rust (rsa, ring, dalek, p256/k256). */
226
284
  export const rustDetector = {
227
285
  id: "rust-crypto",
@@ -234,6 +292,7 @@ export const rustDetector = {
234
292
  RULE_RUST_ECDH,
235
293
  RULE_RUST_ED25519,
236
294
  RULE_RUST_X25519,
295
+ RULE_RUST_X448,
237
296
  RULE_RUST_OPENSSL_RSA,
238
297
  RULE_RUST_OPENSSL_EC,
239
298
  RULE_RUST_OPENSSL_DSA,
@@ -270,6 +329,15 @@ export const rustDetector = {
270
329
  });
271
330
  add(RE_RUST_TLS_ACCEPT_INVALID, RULE_RUST_TLS_ACCEPT_INVALID);
272
331
  add(RE_RUST_TLS_DANGEROUS, RULE_RUST_TLS_DANGEROUS);
332
+ // Type-alias resolution: `use x25519_dalek::{EphemeralSecret as MontgomerySecret}`
333
+ // then `MontgomerySecret::random_from_rng(` — the braced+renamed `use` defeats
334
+ // the `::`-qualified rules. Match the aliased type's construction calls; runs
335
+ // on the ORIGINAL content so locations stay exact, and fires only for an alias
336
+ // explicitly bound to a known crypto type (precision-safe).
337
+ for (const { alias, rule } of collectRustTypeAliases(content)) {
338
+ const a = escapeRustRe(alias);
339
+ add(new RegExp(`\\b${a}::(?:new|random|random_from_rng|generate|from_bytes)\\s*\\(`, "g"), rule);
340
+ }
273
341
  return findings;
274
342
  },
275
343
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rust.js","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical asymmetric cryptography in Rust. Covers the\n * common crypto crates: `rsa`, `ring`, the `*-dalek` family (ed25519/x25519),\n * the RustCrypto elliptic-curve crates (`p256`/`p384`/`k256`), and the `openssl`\n * crate bindings. Lexical, same strategy as the other packs; the `::` path\n * syntax makes the signals precise.\n *\n * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later\n * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but\n * forgeable. `openssl::EcKey` keygen is ambiguous (feeds ECDSA + ECDH),\n * classified conservatively as key-exchange-capable (hndl:true). Also flags\n * Rust TLS certificate-validation bypasses (reqwest / rustls) — not quantum but\n * mirrors the JS tlsDetector's cert-validation category.\n */\nimport type { AlgorithmFamily, Detector, Finding, RuleMeta } from \"../types.js\";\nimport { RUST_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from \"../cwe.js\";\n\n// `rsa` crate + ring RsaKeyPair. `::` after the type marks an associated-fn call.\nconst RE_RUST_RSA = /\\b(?:RsaPrivateKey|RsaPublicKey|RsaKeyPair)::/g;\n// ECDSA: RustCrypto `p256::ecdsa::SigningKey` (contains `ecdsa::SigningKey`),\n// ring `EcdsaKeyPair::`, or a bare `ecdsa::SigningKey`.\nconst RE_RUST_ECDSA = /\\becdsa::SigningKey\\b|\\bEcdsaKeyPair::/g;\n// ECDH: RustCrypto `p256::ecdh::EphemeralSecret`, ring agreement / ECDH_P*.\nconst RE_RUST_ECDH = /\\becdh::EphemeralSecret\\b|\\bagreement::ECDH_P(?:256|384)\\b/g;\n// Ed25519 signatures (dalek + ring).\nconst RE_RUST_ED25519 = /\\bed25519_dalek::(?:SigningKey|Keypair|SecretKey)\\b|\\bEd25519KeyPair::/g;\n// X25519 key agreement (dalek).\nconst RE_RUST_X25519 = /\\bx25519_dalek::(?:EphemeralSecret|StaticSecret)\\b/g;\n// openssl crate bindings — the `Type::generate(` / `Dh::` call forms, distinct\n// from the ring / RustCrypto path syntax above (audit F5/F9 false-negatives).\nconst RE_RUST_OPENSSL_RSA = /\\bRsa::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_EC = /\\bEcKey::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_DSA = /\\bDsa::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_DH = /\\bDh::/g;\n// ring X25519 agreement (the ECDH_P* forms are handled by RE_RUST_ECDH above).\nconst RE_RUST_RING_X25519 = /\\bagreement::X25519\\b/g;\n// Bare (unqualified) constructors: a braced `use x25519_dalek::{EphemeralSecret}`\n// / `use ed25519_dalek::{SigningKey}` strips the `::` path prefix, defeating the\n// path-qualified rules above. The `(?<![:\\w])` lookbehind keeps these from\n// double-matching the qualified forms (e.g. `p256::ecdsa::SigningKey::random`).\nconst RE_RUST_BARE_X25519 = /(?<![:\\w])EphemeralSecret::new\\s*\\(/g;\nconst RE_RUST_BARE_SIGNINGKEY = /(?<![:\\w])SigningKey::(?:generate|random)\\s*\\(/g;\n// jsonwebtoken crate `Algorithm` enum variants selecting a classical JWT signer:\n// RSASSA-PKCS1 (RS*), RSASSA-PSS (PS*), ECDSA (ES*), and Ed25519 (EdDSA). The\n// `\\s*` between `Algorithm` and `::` catches the variant split across lines /\n// routed through a helper (audit token_policy false-negative); the required\n// suffix keeps it off unrelated `Algorithm::` uses (e.g. `HS256` HMAC, which is\n// symmetric and not quantum-vulnerable, or `Algorithm::new`).\nconst RE_RUST_JWT_ALG = /\\bAlgorithm\\s*::(RS|PS|ES)(?:256|384|512)\\b|\\bAlgorithm\\s*::EdDSA\\b/g;\n// Rust TLS certificate-validation bypass: reqwest `danger_accept_invalid_certs`\n// and the rustls `.dangerous()` escape hatch. Mirrors the JS tlsDetector.\nconst RE_RUST_TLS_ACCEPT_INVALID = /\\bdanger_accept_invalid_certs\\s*\\(\\s*true/g;\nconst RE_RUST_TLS_DANGEROUS = /\\.dangerous\\s*\\(\\s*\\)/g;\n\nconst RULE_RUST_RSA: RuleMeta = {\n id: \"rust-rsa\",\n title: \"Rust RSA key/usage\",\n description: \"the `rsa` crate / ring RsaKeyPair\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical RSA (Rust) is not quantum-safe and RSA encryption is HNDL-exposed.\",\n};\nconst RULE_RUST_ECDSA: RuleMeta = {\n id: \"rust-ecdsa\",\n title: \"Rust ECDSA signature\",\n description: \"p256/p384/k256 ecdsa::SigningKey / ring EcdsaKeyPair\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical ECDSA signing (Rust) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_ECDH: RuleMeta = {\n id: \"rust-ecdh\",\n title: \"Rust ECDH key agreement\",\n description: \"p256/p384 ecdh::EphemeralSecret / ring agreement\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Elliptic-curve Diffie-Hellman (Rust) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_RUST_ED25519: RuleMeta = {\n id: \"rust-ed25519\",\n title: \"Rust Ed25519 signature\",\n description: \"ed25519-dalek SigningKey/Keypair / ring Ed25519KeyPair\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Ed25519 (Rust) is a modern but still classical signature scheme.\",\n};\nconst RULE_RUST_X25519: RuleMeta = {\n id: \"rust-x25519\",\n title: \"Rust X25519 key agreement\",\n description: \"x25519-dalek EphemeralSecret/StaticSecret\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"X25519 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_OPENSSL_RSA: RuleMeta = {\n id: \"rust-openssl-rsa\",\n title: \"Rust openssl RSA key generation\",\n description: \"openssl crate Rsa::generate\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical RSA key pair via the Rust `openssl` crate — not quantum-safe and RSA encryption is HNDL-exposed.\",\n};\nconst RULE_RUST_OPENSSL_EC: RuleMeta = {\n id: \"rust-openssl-ec\",\n title: \"Rust openssl EC key generation\",\n description: \"openssl crate EcKey::generate\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical EC key pair via the Rust `openssl` crate. EC keys feed BOTH 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_RUST_OPENSSL_DSA: RuleMeta = {\n id: \"rust-openssl-dsa\",\n title: \"Rust openssl DSA key/usage\",\n description: \"openssl crate Dsa::generate\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical DSA via the Rust `openssl` crate is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_RUST_OPENSSL_DH: RuleMeta = {\n id: \"rust-openssl-dh\",\n title: \"Rust openssl Diffie-Hellman key exchange\",\n description: \"openssl crate Dh params / key generation\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Finite-field Diffie-Hellman via the Rust `openssl` crate is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_RUST_RING_X25519: RuleMeta = {\n id: \"rust-ring-x25519\",\n title: \"Rust ring X25519 key agreement\",\n description: \"ring agreement::X25519\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 key agreement via ring (Rust) is modern but still classical — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_BARE_X25519: RuleMeta = {\n id: \"rust-x25519-bare\",\n title: \"Rust X25519 key agreement (unqualified)\",\n description: \"bare EphemeralSecret::new (x25519-dalek imported via `use`)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 key agreement (x25519-dalek, imported unqualified) is modern but still classical — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_BARE_SIGNINGKEY: RuleMeta = {\n id: \"rust-signingkey-bare\",\n title: \"Rust signature key (unqualified)\",\n description: \"bare SigningKey::generate/random (ed25519-dalek / k256 via `use`)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical signature key from an unqualified `SigningKey` (ed25519-dalek Ed25519 / k256 ECDSA) — forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_JWT_ALGORITHM: RuleMeta = {\n id: \"rust-jwt-algorithm\",\n title: \"Rust jsonwebtoken classical signature algorithm\",\n description: \"jsonwebtoken Algorithm::{RS,PS,ES}* / Algorithm::EdDSA enum variant\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n // Representative family; refined per-finding (RS*/PS* → RSA, ES* → ECDSA, EdDSA).\n algorithm: \"RSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Selects a classical JWT signature algorithm (jsonwebtoken RS*/PS*/ES*/EdDSA), forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_TLS_ACCEPT_INVALID: RuleMeta = {\n id: \"rust-tls-accept-invalid-certs\",\n title: \"Rust TLS certificate verification disabled\",\n description: \"reqwest danger_accept_invalid_certs(true)\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message:\n \"danger_accept_invalid_certs(true) disables TLS certificate verification in reqwest (MITM risk).\",\n remediation: \"Remove danger_accept_invalid_certs(true); verify certificates properly.\",\n};\nconst RULE_RUST_TLS_DANGEROUS: RuleMeta = {\n id: \"rust-tls-rustls-dangerous\",\n title: \"Rust rustls dangerous certificate config\",\n description: \"rustls ClientConfig .dangerous() escape hatch\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"medium\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message: \"rustls `.dangerous()` opts into disabling certificate verification (MITM risk).\",\n remediation: \"Avoid the dangerous() escape hatch; keep the default certificate verifier.\",\n};\n\n/** Detects classical asymmetric crypto in Rust (rsa, ring, dalek, p256/k256). */\nexport const rustDetector: Detector = {\n id: \"rust-crypto\",\n description: \"Classical asymmetric crypto in Rust (rsa, ring, *-dalek, p256/k256)\",\n scope: \"source\",\n language: \"rust\",\n rules: [\n RULE_RUST_RSA,\n RULE_RUST_ECDSA,\n RULE_RUST_ECDH,\n RULE_RUST_ED25519,\n RULE_RUST_X25519,\n RULE_RUST_OPENSSL_RSA,\n RULE_RUST_OPENSSL_EC,\n RULE_RUST_OPENSSL_DSA,\n RULE_RUST_OPENSSL_DH,\n RULE_RUST_RING_X25519,\n RULE_RUST_BARE_X25519,\n RULE_RUST_BARE_SIGNINGKEY,\n RULE_RUST_JWT_ALGORITHM,\n RULE_RUST_TLS_ACCEPT_INVALID,\n RULE_RUST_TLS_DANGEROUS,\n ],\n appliesTo: (f) => hasExtension(f, RUST_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_RUST_RSA, RULE_RUST_RSA);\n add(RE_RUST_ECDSA, RULE_RUST_ECDSA);\n add(RE_RUST_ECDH, RULE_RUST_ECDH);\n add(RE_RUST_ED25519, RULE_RUST_ED25519);\n add(RE_RUST_X25519, RULE_RUST_X25519);\n add(RE_RUST_OPENSSL_RSA, RULE_RUST_OPENSSL_RSA);\n add(RE_RUST_OPENSSL_EC, RULE_RUST_OPENSSL_EC);\n add(RE_RUST_OPENSSL_DSA, RULE_RUST_OPENSSL_DSA);\n add(RE_RUST_OPENSSL_DH, RULE_RUST_OPENSSL_DH);\n add(RE_RUST_RING_X25519, RULE_RUST_RING_X25519);\n add(RE_RUST_BARE_X25519, RULE_RUST_BARE_X25519);\n add(RE_RUST_BARE_SIGNINGKEY, RULE_RUST_BARE_SIGNINGKEY);\n // jsonwebtoken Algorithm enum variants — resolve the family per match: the\n // RS*/PS* variants are RSASSA (RSA), ES* is ECDSA, EdDSA is Ed25519.\n eachMatch(RE_RUST_JWT_ALG, content, (m) => {\n const prefix = m[1];\n const algorithm: AlgorithmFamily =\n prefix === undefined ? \"EdDSA\" : prefix === \"ES\" ? \"ECDSA\" : \"RSA\";\n findings.push(\n findingFromRule(\n RULE_RUST_JWT_ALGORITHM,\n { file, content, index: m.index, matchLength: m[0].length },\n { algorithm },\n ),\n );\n });\n add(RE_RUST_TLS_ACCEPT_INVALID, RULE_RUST_TLS_ACCEPT_INVALID);\n add(RE_RUST_TLS_DANGEROUS, RULE_RUST_TLS_DANGEROUS);\n return findings;\n },\n};\n"]}
1
+ {"version":3,"file":"rust.js","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical asymmetric cryptography in Rust. Covers the\n * common crypto crates: `rsa`, `ring`, the `*-dalek` family (ed25519/x25519),\n * the RustCrypto elliptic-curve crates (`p256`/`p384`/`k256`), and the `openssl`\n * crate bindings. Lexical, same strategy as the other packs; the `::` path\n * syntax makes the signals precise.\n *\n * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later\n * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but\n * forgeable. `openssl::EcKey` keygen is ambiguous (feeds ECDSA + ECDH),\n * classified conservatively as key-exchange-capable (hndl:true). Also flags\n * Rust TLS certificate-validation bypasses (reqwest / rustls) — not quantum but\n * mirrors the JS tlsDetector's cert-validation category.\n */\nimport type { AlgorithmFamily, Detector, Finding, RuleMeta } from \"../types.js\";\nimport { RUST_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from \"../cwe.js\";\n\n// `rsa` crate + ring RsaKeyPair. `::` after the type marks an associated-fn call.\nconst RE_RUST_RSA = /\\b(?:RsaPrivateKey|RsaPublicKey|RsaKeyPair)::/g;\n// ECDSA: RustCrypto `p256::ecdsa::SigningKey` (contains `ecdsa::SigningKey`),\n// ring `EcdsaKeyPair::`, or a bare `ecdsa::SigningKey`.\nconst RE_RUST_ECDSA = /\\becdsa::SigningKey\\b|\\bEcdsaKeyPair::/g;\n// ECDH: RustCrypto `p256::ecdh::EphemeralSecret`, ring agreement / ECDH_P*.\nconst RE_RUST_ECDH = /\\becdh::EphemeralSecret\\b|\\bagreement::ECDH_P(?:256|384)\\b/g;\n// Ed25519 signatures (dalek + ring).\nconst RE_RUST_ED25519 = /\\bed25519_dalek::(?:SigningKey|Keypair|SecretKey)\\b|\\bEd25519KeyPair::/g;\n// X25519 key agreement (dalek).\nconst RE_RUST_X25519 = /\\bx25519_dalek::(?:EphemeralSecret|StaticSecret)\\b/g;\n// openssl crate bindings — the `Type::generate(` / `Dh::` call forms, distinct\n// from the ring / RustCrypto path syntax above (audit F5/F9 false-negatives).\nconst RE_RUST_OPENSSL_RSA = /\\bRsa::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_EC = /\\bEcKey::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_DSA = /\\bDsa::generate\\s*\\(/g;\nconst RE_RUST_OPENSSL_DH = /\\bDh::/g;\n// ring X25519 agreement (the ECDH_P* forms are handled by RE_RUST_ECDH above).\nconst RE_RUST_RING_X25519 = /\\bagreement::X25519\\b/g;\n// Bare (unqualified) constructors: a braced `use x25519_dalek::{EphemeralSecret}`\n// / `use ed25519_dalek::{SigningKey}` strips the `::` path prefix, defeating the\n// path-qualified rules above. The `(?<![:\\w])` lookbehind keeps these from\n// double-matching the qualified forms (e.g. `p256::ecdsa::SigningKey::random`).\nconst RE_RUST_BARE_X25519 = /(?<![:\\w])EphemeralSecret::new\\s*\\(/g;\nconst RE_RUST_BARE_SIGNINGKEY = /(?<![:\\w])SigningKey::(?:generate|random)\\s*\\(/g;\n// jsonwebtoken crate `Algorithm` enum variants selecting a classical JWT signer:\n// RSASSA-PKCS1 (RS*), RSASSA-PSS (PS*), ECDSA (ES*), and Ed25519 (EdDSA). The\n// `\\s*` between `Algorithm` and `::` catches the variant split across lines /\n// routed through a helper (audit token_policy false-negative); the required\n// suffix keeps it off unrelated `Algorithm::` uses (e.g. `HS256` HMAC, which is\n// symmetric and not quantum-vulnerable, or `Algorithm::new`).\nconst RE_RUST_JWT_ALG = /\\bAlgorithm\\s*::(RS|PS|ES)(?:256|384|512)\\b|\\bAlgorithm\\s*::EdDSA\\b/g;\n// Rust TLS certificate-validation bypass: reqwest `danger_accept_invalid_certs`\n// and the rustls `.dangerous()` escape hatch. Mirrors the JS tlsDetector.\nconst RE_RUST_TLS_ACCEPT_INVALID = /\\bdanger_accept_invalid_certs\\s*\\(\\s*true/g;\nconst RE_RUST_TLS_DANGEROUS = /\\.dangerous\\s*\\(\\s*\\)/g;\n\nconst RULE_RUST_RSA: RuleMeta = {\n id: \"rust-rsa\",\n title: \"Rust RSA key/usage\",\n description: \"the `rsa` crate / ring RsaKeyPair\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical RSA (Rust) is not quantum-safe and RSA encryption is HNDL-exposed.\",\n};\nconst RULE_RUST_ECDSA: RuleMeta = {\n id: \"rust-ecdsa\",\n title: \"Rust ECDSA signature\",\n description: \"p256/p384/k256 ecdsa::SigningKey / ring EcdsaKeyPair\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Classical ECDSA signing (Rust) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_ECDH: RuleMeta = {\n id: \"rust-ecdh\",\n title: \"Rust ECDH key agreement\",\n description: \"p256/p384 ecdh::EphemeralSecret / ring agreement\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Elliptic-curve Diffie-Hellman (Rust) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_RUST_ED25519: RuleMeta = {\n id: \"rust-ed25519\",\n title: \"Rust Ed25519 signature\",\n description: \"ed25519-dalek SigningKey/Keypair / ring Ed25519KeyPair\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Ed25519 (Rust) is a modern but still classical signature scheme.\",\n};\nconst RULE_RUST_X25519: RuleMeta = {\n id: \"rust-x25519\",\n title: \"Rust X25519 key agreement\",\n description: \"x25519-dalek EphemeralSecret/StaticSecret\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"X25519 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_X448: RuleMeta = {\n id: \"rust-x448\",\n title: \"Rust X448 key agreement\",\n description: \"the `x448` crate Secret key agreement\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X448\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"X448 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_OPENSSL_RSA: RuleMeta = {\n id: \"rust-openssl-rsa\",\n title: \"Rust openssl RSA key generation\",\n description: \"openssl crate Rsa::generate\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical RSA key pair via the Rust `openssl` crate — not quantum-safe and RSA encryption is HNDL-exposed.\",\n};\nconst RULE_RUST_OPENSSL_EC: RuleMeta = {\n id: \"rust-openssl-ec\",\n title: \"Rust openssl EC key generation\",\n description: \"openssl crate EcKey::generate\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Generates a classical EC key pair via the Rust `openssl` crate. EC keys feed BOTH 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_RUST_OPENSSL_DSA: RuleMeta = {\n id: \"rust-openssl-dsa\",\n title: \"Rust openssl DSA key/usage\",\n description: \"openssl crate Dsa::generate\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical DSA via the Rust `openssl` crate is deprecated and forgeable by a quantum attacker.\",\n remediation: \"Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_RUST_OPENSSL_DH: RuleMeta = {\n id: \"rust-openssl-dh\",\n title: \"Rust openssl Diffie-Hellman key exchange\",\n description: \"openssl crate Dh params / key generation\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Finite-field Diffie-Hellman via the Rust `openssl` crate is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n};\nconst RULE_RUST_RING_X25519: RuleMeta = {\n id: \"rust-ring-x25519\",\n title: \"Rust ring X25519 key agreement\",\n description: \"ring agreement::X25519\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 key agreement via ring (Rust) is modern but still classical — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_BARE_X25519: RuleMeta = {\n id: \"rust-x25519-bare\",\n title: \"Rust X25519 key agreement (unqualified)\",\n description: \"bare EphemeralSecret::new (x25519-dalek imported via `use`)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"X25519 key agreement (x25519-dalek, imported unqualified) is modern but still classical — harvest-now-decrypt-later.\",\n};\nconst RULE_RUST_BARE_SIGNINGKEY: RuleMeta = {\n id: \"rust-signingkey-bare\",\n title: \"Rust signature key (unqualified)\",\n description: \"bare SigningKey::generate/random (ed25519-dalek / k256 via `use`)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical signature key from an unqualified `SigningKey` (ed25519-dalek Ed25519 / k256 ECDSA) — forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_JWT_ALGORITHM: RuleMeta = {\n id: \"rust-jwt-algorithm\",\n title: \"Rust jsonwebtoken classical signature algorithm\",\n description: \"jsonwebtoken Algorithm::{RS,PS,ES}* / Algorithm::EdDSA enum variant\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n // Representative family; refined per-finding (RS*/PS* → RSA, ES* → ECDSA, EdDSA).\n algorithm: \"RSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Selects a classical JWT signature algorithm (jsonwebtoken RS*/PS*/ES*/EdDSA), forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_RUST_TLS_ACCEPT_INVALID: RuleMeta = {\n id: \"rust-tls-accept-invalid-certs\",\n title: \"Rust TLS certificate verification disabled\",\n description: \"reqwest danger_accept_invalid_certs(true)\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message:\n \"danger_accept_invalid_certs(true) disables TLS certificate verification in reqwest (MITM risk).\",\n remediation: \"Remove danger_accept_invalid_certs(true); verify certificates properly.\",\n};\nconst RULE_RUST_TLS_DANGEROUS: RuleMeta = {\n id: \"rust-tls-rustls-dangerous\",\n title: \"Rust rustls dangerous certificate config\",\n description: \"rustls ClientConfig .dangerous() escape hatch\",\n category: \"tls\",\n severity: \"high\",\n confidence: \"medium\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message: \"rustls `.dangerous()` opts into disabling certificate verification (MITM risk).\",\n remediation: \"Avoid the dangerous() escape hatch; keep the default certificate verifier.\",\n};\n\n/**\n * Aliasable Rust crypto types (`<crate>::<OrigType>` → rule). A renamed `use`\n * (`use x25519_dalek::{EphemeralSecret as MontgomerySecret}`) binds the alias to a\n * classical-key type; a later `MontgomerySecret::<ctor>(` is that key's\n * construction. The `::`-qualified and braced rules above miss this because the\n * call site uses the renamed identifier, not the original path.\n */\nconst RUST_ALIASABLE: Record<string, RuleMeta> = {\n \"x25519_dalek::EphemeralSecret\": RULE_RUST_X25519,\n \"x25519_dalek::StaticSecret\": RULE_RUST_X25519,\n \"x448::Secret\": RULE_RUST_X448,\n \"ed25519_dalek::SigningKey\": RULE_RUST_ED25519,\n \"ed25519_dalek::Keypair\": RULE_RUST_ED25519,\n \"ed25519_dalek::SecretKey\": RULE_RUST_ED25519,\n};\n\n/** Escape a string for interpolation into a dynamically-built RegExp. */\nfunction escapeRustRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Collect `use <crate>::<OrigType> as <Alias>` bindings (braced or single) that\n * rename an aliasable crypto type, returning {alias, rule} pairs. An alias equal\n * to its own type name is skipped (the qualified/bare rules already catch it).\n */\nfunction collectRustTypeAliases(content: string): Array<{ alias: string; rule: RuleMeta }> {\n const out: Array<{ alias: string; rule: RuleMeta }> = [];\n const push = (crate: string, orig: string, alias: string): void => {\n if (!alias || alias === orig) return;\n const rule = RUST_ALIASABLE[`${crate}::${orig}`];\n if (rule) out.push({ alias, rule });\n };\n // Braced: `use <crate>::{ Orig as Alias, ... }`.\n const braced = /\\buse\\s+([\\w:]+)::\\{([^}]*)\\}/g;\n for (let m = braced.exec(content); m; m = braced.exec(content)) {\n const specRe = /([A-Za-z_]\\w*)\\s+as\\s+([A-Za-z_]\\w*)/g;\n for (let s = specRe.exec(m[2]); s; s = specRe.exec(m[2])) push(m[1], s[1], s[2]);\n }\n // Single: `use <crate>::Orig as Alias;`.\n const single = /\\buse\\s+([\\w:]+)::([A-Za-z_]\\w*)\\s+as\\s+([A-Za-z_]\\w*)/g;\n for (let m = single.exec(content); m; m = single.exec(content)) push(m[1], m[2], m[3]);\n return out;\n}\n\n/** Detects classical asymmetric crypto in Rust (rsa, ring, dalek, p256/k256). */\nexport const rustDetector: Detector = {\n id: \"rust-crypto\",\n description: \"Classical asymmetric crypto in Rust (rsa, ring, *-dalek, p256/k256)\",\n scope: \"source\",\n language: \"rust\",\n rules: [\n RULE_RUST_RSA,\n RULE_RUST_ECDSA,\n RULE_RUST_ECDH,\n RULE_RUST_ED25519,\n RULE_RUST_X25519,\n RULE_RUST_X448,\n RULE_RUST_OPENSSL_RSA,\n RULE_RUST_OPENSSL_EC,\n RULE_RUST_OPENSSL_DSA,\n RULE_RUST_OPENSSL_DH,\n RULE_RUST_RING_X25519,\n RULE_RUST_BARE_X25519,\n RULE_RUST_BARE_SIGNINGKEY,\n RULE_RUST_JWT_ALGORITHM,\n RULE_RUST_TLS_ACCEPT_INVALID,\n RULE_RUST_TLS_DANGEROUS,\n ],\n appliesTo: (f) => hasExtension(f, RUST_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_RUST_RSA, RULE_RUST_RSA);\n add(RE_RUST_ECDSA, RULE_RUST_ECDSA);\n add(RE_RUST_ECDH, RULE_RUST_ECDH);\n add(RE_RUST_ED25519, RULE_RUST_ED25519);\n add(RE_RUST_X25519, RULE_RUST_X25519);\n add(RE_RUST_OPENSSL_RSA, RULE_RUST_OPENSSL_RSA);\n add(RE_RUST_OPENSSL_EC, RULE_RUST_OPENSSL_EC);\n add(RE_RUST_OPENSSL_DSA, RULE_RUST_OPENSSL_DSA);\n add(RE_RUST_OPENSSL_DH, RULE_RUST_OPENSSL_DH);\n add(RE_RUST_RING_X25519, RULE_RUST_RING_X25519);\n add(RE_RUST_BARE_X25519, RULE_RUST_BARE_X25519);\n add(RE_RUST_BARE_SIGNINGKEY, RULE_RUST_BARE_SIGNINGKEY);\n // jsonwebtoken Algorithm enum variants — resolve the family per match: the\n // RS*/PS* variants are RSASSA (RSA), ES* is ECDSA, EdDSA is Ed25519.\n eachMatch(RE_RUST_JWT_ALG, content, (m) => {\n const prefix = m[1];\n const algorithm: AlgorithmFamily =\n prefix === undefined ? \"EdDSA\" : prefix === \"ES\" ? \"ECDSA\" : \"RSA\";\n findings.push(\n findingFromRule(\n RULE_RUST_JWT_ALGORITHM,\n { file, content, index: m.index, matchLength: m[0].length },\n { algorithm },\n ),\n );\n });\n add(RE_RUST_TLS_ACCEPT_INVALID, RULE_RUST_TLS_ACCEPT_INVALID);\n add(RE_RUST_TLS_DANGEROUS, RULE_RUST_TLS_DANGEROUS);\n\n // Type-alias resolution: `use x25519_dalek::{EphemeralSecret as MontgomerySecret}`\n // then `MontgomerySecret::random_from_rng(` — the braced+renamed `use` defeats\n // the `::`-qualified rules. Match the aliased type's construction calls; runs\n // on the ORIGINAL content so locations stay exact, and fires only for an alias\n // explicitly bound to a known crypto type (precision-safe).\n for (const { alias, rule } of collectRustTypeAliases(content)) {\n const a = escapeRustRe(alias);\n add(\n new RegExp(`\\\\b${a}::(?:new|random|random_from_rng|generate|from_bytes)\\\\s*\\\\(`, \"g\"),\n rule,\n );\n }\n return findings;\n },\n};\n"]}