@quantakrypto/core 0.4.2 → 0.4.3

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 (132) 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.js.map +1 -1
  7. package/dist/changed.js.map +1 -1
  8. package/dist/codemods/config-toggle.js.map +1 -1
  9. package/dist/codemods/registry.js.map +1 -1
  10. package/dist/comments.d.ts +26 -0
  11. package/dist/comments.d.ts.map +1 -1
  12. package/dist/comments.js +141 -1
  13. package/dist/comments.js.map +1 -1
  14. package/dist/config.js.map +1 -1
  15. package/dist/cwe.js.map +1 -1
  16. package/dist/dependencies.d.ts +3 -2
  17. package/dist/dependencies.d.ts.map +1 -1
  18. package/dist/dependencies.js +138 -2
  19. package/dist/dependencies.js.map +1 -1
  20. package/dist/detect-utils.d.ts +14 -3
  21. package/dist/detect-utils.d.ts.map +1 -1
  22. package/dist/detect-utils.js +32 -4
  23. package/dist/detect-utils.js.map +1 -1
  24. package/dist/detectors/c.d.ts.map +1 -1
  25. package/dist/detectors/c.js +190 -2
  26. package/dist/detectors/c.js.map +1 -1
  27. package/dist/detectors/csharp.d.ts.map +1 -1
  28. package/dist/detectors/csharp.js +141 -3
  29. package/dist/detectors/csharp.js.map +1 -1
  30. package/dist/detectors/go.d.ts +5 -3
  31. package/dist/detectors/go.d.ts.map +1 -1
  32. package/dist/detectors/go.js +187 -3
  33. package/dist/detectors/go.js.map +1 -1
  34. package/dist/detectors/java.d.ts.map +1 -1
  35. package/dist/detectors/java.js +168 -3
  36. package/dist/detectors/java.js.map +1 -1
  37. package/dist/detectors/pem.d.ts.map +1 -1
  38. package/dist/detectors/pem.js +49 -1
  39. package/dist/detectors/pem.js.map +1 -1
  40. package/dist/detectors/python.d.ts.map +1 -1
  41. package/dist/detectors/python.js +74 -3
  42. package/dist/detectors/python.js.map +1 -1
  43. package/dist/detectors/ruby.d.ts +9 -5
  44. package/dist/detectors/ruby.d.ts.map +1 -1
  45. package/dist/detectors/ruby.js +122 -2
  46. package/dist/detectors/ruby.js.map +1 -1
  47. package/dist/detectors/rust.d.ts +8 -3
  48. package/dist/detectors/rust.d.ts.map +1 -1
  49. package/dist/detectors/rust.js +185 -3
  50. package/dist/detectors/rust.js.map +1 -1
  51. package/dist/detectors/source.d.ts.map +1 -1
  52. package/dist/detectors/source.js +117 -7
  53. package/dist/detectors/source.js.map +1 -1
  54. package/dist/detectors/stateful-hbs.d.ts +22 -0
  55. package/dist/detectors/stateful-hbs.d.ts.map +1 -0
  56. package/dist/detectors/stateful-hbs.js +139 -0
  57. package/dist/detectors/stateful-hbs.js.map +1 -0
  58. package/dist/errors.js.map +1 -1
  59. package/dist/evidence.d.ts +48 -0
  60. package/dist/evidence.d.ts.map +1 -0
  61. package/dist/evidence.js +76 -0
  62. package/dist/evidence.js.map +1 -0
  63. package/dist/index.d.ts +4 -2
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +4 -2
  66. package/dist/index.js.map +1 -1
  67. package/dist/inventory.d.ts +2 -0
  68. package/dist/inventory.d.ts.map +1 -1
  69. package/dist/inventory.js +27 -1
  70. package/dist/inventory.js.map +1 -1
  71. package/dist/parallel.js.map +1 -1
  72. package/dist/patch-policy.js.map +1 -1
  73. package/dist/redact.d.ts.map +1 -1
  74. package/dist/redact.js +39 -0
  75. package/dist/redact.js.map +1 -1
  76. package/dist/registry.d.ts +2 -2
  77. package/dist/registry.d.ts.map +1 -1
  78. package/dist/registry.js +4 -2
  79. package/dist/registry.js.map +1 -1
  80. package/dist/remediate-pipeline.d.ts +3 -0
  81. package/dist/remediate-pipeline.d.ts.map +1 -1
  82. package/dist/remediate-pipeline.js +50 -0
  83. package/dist/remediate-pipeline.js.map +1 -1
  84. package/dist/remediate-request.js.map +1 -1
  85. package/dist/remediation.d.ts +9 -3
  86. package/dist/remediation.d.ts.map +1 -1
  87. package/dist/remediation.js +17 -5
  88. package/dist/remediation.js.map +1 -1
  89. package/dist/report.d.ts +9 -0
  90. package/dist/report.d.ts.map +1 -1
  91. package/dist/report.js +31 -0
  92. package/dist/report.js.map +1 -1
  93. package/dist/scan-worker.js.map +1 -1
  94. package/dist/scan.d.ts.map +1 -1
  95. package/dist/scan.js +8 -1
  96. package/dist/scan.js.map +1 -1
  97. package/dist/severity.js.map +1 -1
  98. package/dist/triage.js.map +1 -1
  99. package/dist/types.d.ts +1 -1
  100. package/dist/types.d.ts.map +1 -1
  101. package/dist/types.js.map +1 -1
  102. package/dist/verify.js.map +1 -1
  103. package/dist/version.d.ts +1 -1
  104. package/dist/version.js +1 -1
  105. package/dist/version.js.map +1 -1
  106. package/dist/walk.js.map +1 -1
  107. package/dist/worktree.js.map +1 -1
  108. package/package.json +1 -1
  109. package/src/comments.ts +140 -1
  110. package/src/dependencies.ts +143 -4
  111. package/src/detect-utils.ts +33 -4
  112. package/src/detectors/c.ts +202 -2
  113. package/src/detectors/csharp.ts +154 -3
  114. package/src/detectors/go.ts +209 -6
  115. package/src/detectors/java.ts +189 -3
  116. package/src/detectors/pem.ts +53 -1
  117. package/src/detectors/python.ts +82 -3
  118. package/src/detectors/ruby.ts +138 -7
  119. package/src/detectors/rust.ts +211 -7
  120. package/src/detectors/source.ts +149 -6
  121. package/src/detectors/stateful-hbs.ts +177 -0
  122. package/src/evidence.ts +124 -0
  123. package/src/index.ts +5 -1
  124. package/src/inventory.ts +31 -1
  125. package/src/redact.ts +41 -0
  126. package/src/registry.ts +4 -2
  127. package/src/remediate-pipeline.ts +55 -0
  128. package/src/remediation.ts +23 -8
  129. package/src/report.ts +41 -2
  130. package/src/scan.ts +13 -1
  131. package/src/types.ts +1 -1
  132. package/src/version.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import { RUST_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
2
- import { CWE_BROKEN_CRYPTO } from "../cwe.js";
2
+ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from "../cwe.js";
3
3
  // `rsa` crate + ring RsaKeyPair. `::` after the type marks an associated-fn call.
4
4
  const RE_RUST_RSA = /\b(?:RsaPrivateKey|RsaPublicKey|RsaKeyPair)::/g;
5
5
  // ECDSA: RustCrypto `p256::ecdsa::SigningKey` (contains `ecdsa::SigningKey`),
@@ -11,6 +11,31 @@ const RE_RUST_ECDH = /\becdh::EphemeralSecret\b|\bagreement::ECDH_P(?:256|384)\b
11
11
  const RE_RUST_ED25519 = /\bed25519_dalek::(?:SigningKey|Keypair|SecretKey)\b|\bEd25519KeyPair::/g;
12
12
  // X25519 key agreement (dalek).
13
13
  const RE_RUST_X25519 = /\bx25519_dalek::(?:EphemeralSecret|StaticSecret)\b/g;
14
+ // openssl crate bindings — the `Type::generate(` / `Dh::` call forms, distinct
15
+ // from the ring / RustCrypto path syntax above (audit F5/F9 false-negatives).
16
+ const RE_RUST_OPENSSL_RSA = /\bRsa::generate\s*\(/g;
17
+ const RE_RUST_OPENSSL_EC = /\bEcKey::generate\s*\(/g;
18
+ const RE_RUST_OPENSSL_DSA = /\bDsa::generate\s*\(/g;
19
+ const RE_RUST_OPENSSL_DH = /\bDh::/g;
20
+ // ring X25519 agreement (the ECDH_P* forms are handled by RE_RUST_ECDH above).
21
+ const RE_RUST_RING_X25519 = /\bagreement::X25519\b/g;
22
+ // Bare (unqualified) constructors: a braced `use x25519_dalek::{EphemeralSecret}`
23
+ // / `use ed25519_dalek::{SigningKey}` strips the `::` path prefix, defeating the
24
+ // path-qualified rules above. The `(?<![:\w])` lookbehind keeps these from
25
+ // double-matching the qualified forms (e.g. `p256::ecdsa::SigningKey::random`).
26
+ const RE_RUST_BARE_X25519 = /(?<![:\w])EphemeralSecret::new\s*\(/g;
27
+ const RE_RUST_BARE_SIGNINGKEY = /(?<![:\w])SigningKey::(?:generate|random)\s*\(/g;
28
+ // jsonwebtoken crate `Algorithm` enum variants selecting a classical JWT signer:
29
+ // RSASSA-PKCS1 (RS*), RSASSA-PSS (PS*), ECDSA (ES*), and Ed25519 (EdDSA). The
30
+ // `\s*` between `Algorithm` and `::` catches the variant split across lines /
31
+ // routed through a helper (audit token_policy false-negative); the required
32
+ // suffix keeps it off unrelated `Algorithm::` uses (e.g. `HS256` HMAC, which is
33
+ // symmetric and not quantum-vulnerable, or `Algorithm::new`).
34
+ const RE_RUST_JWT_ALG = /\bAlgorithm\s*::(RS|PS|ES)(?:256|384|512)\b|\bAlgorithm\s*::EdDSA\b/g;
35
+ // Rust TLS certificate-validation bypass: reqwest `danger_accept_invalid_certs`
36
+ // and the rustls `.dangerous()` escape hatch. Mirrors the JS tlsDetector.
37
+ const RE_RUST_TLS_ACCEPT_INVALID = /\bdanger_accept_invalid_certs\s*\(\s*true/g;
38
+ const RE_RUST_TLS_DANGEROUS = /\.dangerous\s*\(\s*\)/g;
14
39
  const RULE_RUST_RSA = {
15
40
  id: "rust-rsa",
16
41
  title: "Rust RSA key/usage",
@@ -65,20 +90,161 @@ const RULE_RUST_X25519 = {
65
90
  title: "Rust X25519 key agreement",
66
91
  description: "x25519-dalek EphemeralSecret/StaticSecret",
67
92
  category: "key-exchange",
68
- severity: "low",
93
+ severity: "medium",
69
94
  confidence: "high",
70
95
  algorithm: "X25519",
71
96
  hndl: true,
72
97
  cwe: CWE_BROKEN_CRYPTO,
73
98
  message: "X25519 (Rust) is modern but still classical key agreement — harvest-now-decrypt-later.",
74
99
  };
100
+ const RULE_RUST_OPENSSL_RSA = {
101
+ id: "rust-openssl-rsa",
102
+ title: "Rust openssl RSA key generation",
103
+ description: "openssl crate Rsa::generate",
104
+ category: "kem",
105
+ severity: "high",
106
+ confidence: "high",
107
+ algorithm: "RSA",
108
+ hndl: true,
109
+ cwe: CWE_BROKEN_CRYPTO,
110
+ message: "Generates a classical RSA key pair via the Rust `openssl` crate — not quantum-safe and RSA encryption is HNDL-exposed.",
111
+ };
112
+ const RULE_RUST_OPENSSL_EC = {
113
+ id: "rust-openssl-ec",
114
+ title: "Rust openssl EC key generation",
115
+ description: "openssl crate EcKey::generate",
116
+ category: "key-exchange",
117
+ severity: "high",
118
+ confidence: "high",
119
+ algorithm: "ECDH",
120
+ hndl: true,
121
+ cwe: CWE_BROKEN_CRYPTO,
122
+ message: "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.",
123
+ remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
124
+ };
125
+ const RULE_RUST_OPENSSL_DSA = {
126
+ id: "rust-openssl-dsa",
127
+ title: "Rust openssl DSA key/usage",
128
+ description: "openssl crate Dsa::generate",
129
+ category: "signature",
130
+ severity: "high",
131
+ confidence: "high",
132
+ algorithm: "DSA",
133
+ hndl: false,
134
+ cwe: CWE_BROKEN_CRYPTO,
135
+ message: "Classical DSA via the Rust `openssl` crate is deprecated and forgeable by a quantum attacker.",
136
+ remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
137
+ };
138
+ const RULE_RUST_OPENSSL_DH = {
139
+ id: "rust-openssl-dh",
140
+ title: "Rust openssl Diffie-Hellman key exchange",
141
+ description: "openssl crate Dh params / key generation",
142
+ category: "key-exchange",
143
+ severity: "high",
144
+ confidence: "high",
145
+ algorithm: "DH",
146
+ hndl: true,
147
+ cwe: CWE_BROKEN_CRYPTO,
148
+ message: "Finite-field Diffie-Hellman via the Rust `openssl` crate is broken by Shor's algorithm (harvest-now-decrypt-later).",
149
+ };
150
+ const RULE_RUST_RING_X25519 = {
151
+ id: "rust-ring-x25519",
152
+ title: "Rust ring X25519 key agreement",
153
+ description: "ring agreement::X25519",
154
+ category: "key-exchange",
155
+ severity: "medium",
156
+ confidence: "high",
157
+ algorithm: "X25519",
158
+ hndl: true,
159
+ cwe: CWE_BROKEN_CRYPTO,
160
+ message: "X25519 key agreement via ring (Rust) is modern but still classical — harvest-now-decrypt-later.",
161
+ };
162
+ const RULE_RUST_BARE_X25519 = {
163
+ id: "rust-x25519-bare",
164
+ title: "Rust X25519 key agreement (unqualified)",
165
+ description: "bare EphemeralSecret::new (x25519-dalek imported via `use`)",
166
+ category: "key-exchange",
167
+ severity: "medium",
168
+ confidence: "medium",
169
+ algorithm: "X25519",
170
+ hndl: true,
171
+ cwe: CWE_BROKEN_CRYPTO,
172
+ message: "X25519 key agreement (x25519-dalek, imported unqualified) is modern but still classical — harvest-now-decrypt-later.",
173
+ };
174
+ const RULE_RUST_BARE_SIGNINGKEY = {
175
+ id: "rust-signingkey-bare",
176
+ title: "Rust signature key (unqualified)",
177
+ description: "bare SigningKey::generate/random (ed25519-dalek / k256 via `use`)",
178
+ category: "signature",
179
+ severity: "medium",
180
+ confidence: "medium",
181
+ algorithm: "unknown",
182
+ hndl: false,
183
+ cwe: CWE_BROKEN_CRYPTO,
184
+ message: "Classical signature key from an unqualified `SigningKey` (ed25519-dalek Ed25519 / k256 ECDSA) — forgeable by a quantum attacker.",
185
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
186
+ };
187
+ const RULE_RUST_JWT_ALGORITHM = {
188
+ id: "rust-jwt-algorithm",
189
+ title: "Rust jsonwebtoken classical signature algorithm",
190
+ description: "jsonwebtoken Algorithm::{RS,PS,ES}* / Algorithm::EdDSA enum variant",
191
+ category: "signature",
192
+ severity: "high",
193
+ confidence: "high",
194
+ // Representative family; refined per-finding (RS*/PS* → RSA, ES* → ECDSA, EdDSA).
195
+ algorithm: "RSA",
196
+ hndl: false,
197
+ cwe: CWE_BROKEN_CRYPTO,
198
+ message: "Selects a classical JWT signature algorithm (jsonwebtoken RS*/PS*/ES*/EdDSA), forgeable by a quantum attacker.",
199
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
200
+ };
201
+ const RULE_RUST_TLS_ACCEPT_INVALID = {
202
+ id: "rust-tls-accept-invalid-certs",
203
+ title: "Rust TLS certificate verification disabled",
204
+ description: "reqwest danger_accept_invalid_certs(true)",
205
+ category: "tls",
206
+ severity: "high",
207
+ confidence: "high",
208
+ hndl: false,
209
+ cwe: CWE_CERT_VALIDATION,
210
+ message: "danger_accept_invalid_certs(true) disables TLS certificate verification in reqwest (MITM risk).",
211
+ remediation: "Remove danger_accept_invalid_certs(true); verify certificates properly.",
212
+ };
213
+ const RULE_RUST_TLS_DANGEROUS = {
214
+ id: "rust-tls-rustls-dangerous",
215
+ title: "Rust rustls dangerous certificate config",
216
+ description: "rustls ClientConfig .dangerous() escape hatch",
217
+ category: "tls",
218
+ severity: "high",
219
+ confidence: "medium",
220
+ hndl: false,
221
+ cwe: CWE_CERT_VALIDATION,
222
+ message: "rustls `.dangerous()` opts into disabling certificate verification (MITM risk).",
223
+ remediation: "Avoid the dangerous() escape hatch; keep the default certificate verifier.",
224
+ };
75
225
  /** Detects classical asymmetric crypto in Rust (rsa, ring, dalek, p256/k256). */
76
226
  export const rustDetector = {
77
227
  id: "rust-crypto",
78
228
  description: "Classical asymmetric crypto in Rust (rsa, ring, *-dalek, p256/k256)",
79
229
  scope: "source",
80
230
  language: "rust",
81
- rules: [RULE_RUST_RSA, RULE_RUST_ECDSA, RULE_RUST_ECDH, RULE_RUST_ED25519, RULE_RUST_X25519],
231
+ rules: [
232
+ RULE_RUST_RSA,
233
+ RULE_RUST_ECDSA,
234
+ RULE_RUST_ECDH,
235
+ RULE_RUST_ED25519,
236
+ RULE_RUST_X25519,
237
+ RULE_RUST_OPENSSL_RSA,
238
+ RULE_RUST_OPENSSL_EC,
239
+ RULE_RUST_OPENSSL_DSA,
240
+ RULE_RUST_OPENSSL_DH,
241
+ RULE_RUST_RING_X25519,
242
+ RULE_RUST_BARE_X25519,
243
+ RULE_RUST_BARE_SIGNINGKEY,
244
+ RULE_RUST_JWT_ALGORITHM,
245
+ RULE_RUST_TLS_ACCEPT_INVALID,
246
+ RULE_RUST_TLS_DANGEROUS,
247
+ ],
82
248
  appliesTo: (f) => hasExtension(f, RUST_EXTENSIONS),
83
249
  detect({ file, content }) {
84
250
  const findings = [];
@@ -88,6 +254,22 @@ export const rustDetector = {
88
254
  add(RE_RUST_ECDH, RULE_RUST_ECDH);
89
255
  add(RE_RUST_ED25519, RULE_RUST_ED25519);
90
256
  add(RE_RUST_X25519, RULE_RUST_X25519);
257
+ add(RE_RUST_OPENSSL_RSA, RULE_RUST_OPENSSL_RSA);
258
+ add(RE_RUST_OPENSSL_EC, RULE_RUST_OPENSSL_EC);
259
+ add(RE_RUST_OPENSSL_DSA, RULE_RUST_OPENSSL_DSA);
260
+ add(RE_RUST_OPENSSL_DH, RULE_RUST_OPENSSL_DH);
261
+ add(RE_RUST_RING_X25519, RULE_RUST_RING_X25519);
262
+ add(RE_RUST_BARE_X25519, RULE_RUST_BARE_X25519);
263
+ add(RE_RUST_BARE_SIGNINGKEY, RULE_RUST_BARE_SIGNINGKEY);
264
+ // jsonwebtoken Algorithm enum variants — resolve the family per match: the
265
+ // RS*/PS* variants are RSASSA (RSA), ES* is ECDSA, EdDSA is Ed25519.
266
+ eachMatch(RE_RUST_JWT_ALG, content, (m) => {
267
+ const prefix = m[1];
268
+ const algorithm = prefix === undefined ? "EdDSA" : prefix === "ES" ? "ECDSA" : "RSA";
269
+ findings.push(findingFromRule(RULE_RUST_JWT_ALGORITHM, { file, content, index: m.index, matchLength: m[0].length }, { algorithm }));
270
+ });
271
+ add(RE_RUST_TLS_ACCEPT_INVALID, RULE_RUST_TLS_ACCEPT_INVALID);
272
+ add(RE_RUST_TLS_DANGEROUS, RULE_RUST_TLS_DANGEROUS);
91
273
  return findings;
92
274
  },
93
275
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rust.js","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,kFAAkF;AAClF,MAAM,WAAW,GAAG,gDAAgD,CAAC;AACrE,8EAA8E;AAC9E,wDAAwD;AACxD,MAAM,aAAa,GAAG,yCAAyC,CAAC;AAChE,4EAA4E;AAC5E,MAAM,YAAY,GAAG,6DAA6D,CAAC;AACnF,qCAAqC;AACrC,MAAM,eAAe,GAAG,yEAAyE,CAAC;AAClG,gCAAgC;AAChC,MAAM,cAAc,GAAG,qDAAqD,CAAC;AAE7E,MAAM,aAAa,GAAa;IAC9B,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,oBAAoB;IAC3B,WAAW,EAAE,mCAAmC;IAChD,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,KAAK;IAChB,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,8EAA8E;CACxF,CAAC;AACF,MAAM,eAAe,GAAa;IAChC,EAAE,EAAE,YAAY;IAChB,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE,sDAAsD;IACnE,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,OAAO;IAClB,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,oEAAoE;IAC7E,WAAW,EAAE,4CAA4C;CAC1D,CAAC;AACF,MAAM,cAAc,GAAa;IAC/B,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,yBAAyB;IAChC,WAAW,EAAE,kDAAkD;IAC/D,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,iBAAiB;IACtB,OAAO,EACL,iGAAiG;CACpG,CAAC;AACF,MAAM,iBAAiB,GAAa;IAClC,EAAE,EAAE,cAAc;IAClB,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,wDAAwD;IACrE,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,OAAO;IAClB,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,kEAAkE;CAC5E,CAAC;AACF,MAAM,gBAAgB,GAAa;IACjC,EAAE,EAAE,aAAa;IACjB,KAAK,EAAE,2BAA2B;IAClC,WAAW,EAAE,2CAA2C;IACxD,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,QAAQ;IACnB,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,iBAAiB;IACtB,OAAO,EAAE,wFAAwF;CAClG,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,EAAE,EAAE,aAAa;IACjB,WAAW,EAAE,qEAAqE;IAClF,KAAK,EAAE,QAAQ;IACf,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,CAAC,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,CAAC;IAC5F,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,eAAe,CAAC;IAClD,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,CAAC,EAAU,EAAE,IAAc,EAAE,EAAE,CACzC,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAC3B,QAAQ,CAAC,IAAI,CACX,eAAe,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CACnF,CACF,CAAC;QACJ,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QAChC,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QACpC,GAAG,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QAClC,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QACxC,GAAG,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;QACtC,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
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 +1 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/detectors/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA82B/D,0DAA0D;AAC1D,eAAO,MAAM,eAAe,EAAE,QAAQ,EAOrC,CAAC"}
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/detectors/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA4/B/D,0DAA0D;AAC1D,eAAO,MAAM,eAAe,EAAE,QAAQ,EAQrC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { JS_TS_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
1
+ import { JS_TS_EXTENSIONS, DOC_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
2
2
  import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
3
3
  /* -------------------------------------------------------------------------- */
4
4
  /* Precompiled regexes (module scope — never recreated per file) */
@@ -206,8 +206,8 @@ const nodeCryptoDetector = {
206
206
  },
207
207
  dsa: { algo: "DSA", cat: "signature", sev: "high", hndl: false, label: "DSA" },
208
208
  dh: { algo: "DH", cat: "key-exchange", sev: "high", hndl: true, label: "Diffie-Hellman" },
209
- x25519: { algo: "X25519", cat: "key-exchange", sev: "low", hndl: true, label: "X25519" },
210
- x448: { algo: "X448", cat: "key-exchange", sev: "low", hndl: true, label: "X448" },
209
+ x25519: { algo: "X25519", cat: "key-exchange", sev: "medium", hndl: true, label: "X25519" },
210
+ x448: { algo: "X448", cat: "key-exchange", sev: "medium", hndl: true, label: "X448" },
211
211
  ed25519: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed25519" },
212
212
  ed448: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed448" },
213
213
  };
@@ -495,6 +495,10 @@ const libraryDetector = {
495
495
  /* -------------------------------------------------------------------------- */
496
496
  /* JWT / JOSE / COSE algorithm strings */
497
497
  /* -------------------------------------------------------------------------- */
498
+ // JOSE RSA key-transport algorithms (JWE `alg`): quoted RSA-OAEP / RSA-OAEP-256
499
+ // /384/512 / RSA1_5. Classical RSA *encryption* (key transport) — distinct from
500
+ // the RS*/PS* *signature* tokens above, and a harvest-now-decrypt-later surface.
501
+ const RE_JOSE_KEM = /['"`](RSA-OAEP(?:-(?:256|384|512))?|RSA1_5)['"`]/g;
498
502
  const RULE_JWT_ALG = {
499
503
  id: "jwt-classical-alg",
500
504
  title: "Classical JWT/JOSE algorithm",
@@ -521,6 +525,19 @@ const RULE_JOSE_ECDH = {
521
525
  message: "JOSE ECDH-ES performs classical ECDH key agreement — harvest-now-decrypt-later exposed.",
522
526
  remediation: "Track IETF PQC JOSE/COSE; adopt hybrid X25519MLKEM768 KEM-based encryption.",
523
527
  };
528
+ const RULE_JOSE_RSA_OAEP = {
529
+ id: "jose-rsa-oaep",
530
+ title: "JOSE RSA key-transport algorithm",
531
+ description: "JWE RSA-OAEP / RSA-OAEP-256/384/512 / RSA1_5 key encryption",
532
+ category: "key-exchange",
533
+ severity: "high",
534
+ confidence: "medium",
535
+ algorithm: "RSA",
536
+ hndl: true,
537
+ cwe: CWE_BROKEN_CRYPTO,
538
+ message: "JOSE RSA key transport (RSA-OAEP / RSA1_5) is classical RSA encryption — harvest-now-decrypt-later exposed.",
539
+ remediation: "Track IETF PQC JOSE/COSE; adopt hybrid X25519MLKEM768 KEM-based encryption.",
540
+ };
524
541
  /**
525
542
  * Detects classical signature algorithm identifiers used by JWT/JOSE, plus
526
543
  * ECDH-ES key-agreement identifiers (HNDL-exposed). These appear as string
@@ -534,7 +551,7 @@ const jwtDetector = {
534
551
  // signal in JS/TS or Python (e.g. PyJWT `algorithm="RS256"`), so this detector
535
552
  // is un-gated from JS-only to the JWT host surfaces.
536
553
  language: "any",
537
- rules: [RULE_JWT_ALG, RULE_JOSE_ECDH],
554
+ rules: [RULE_JWT_ALG, RULE_JOSE_ECDH, RULE_JOSE_RSA_OAEP],
538
555
  appliesTo: (f) => hasExtension(f, JWT_HOST_EXTENSIONS),
539
556
  detect({ file, content }) {
540
557
  const findings = [];
@@ -561,6 +578,13 @@ const jwtDetector = {
561
578
  message: `JOSE "${m[1]}" performs classical ECDH key agreement — harvest-now-decrypt-later exposed.`,
562
579
  }));
563
580
  });
581
+ // JOSE RSA key transport (RSA-OAEP / RSA1_5) — classical RSA encryption, HNDL.
582
+ eachMatch(RE_JOSE_KEM, content, (m) => {
583
+ findings.push(findingFromRule(RULE_JOSE_RSA_OAEP, { file, content, index: m.index, matchLength: m[0].length }, {
584
+ title: `JOSE RSA key transport ${m[1]}`,
585
+ message: `JOSE "${m[1]}" is classical RSA key transport — harvest-now-decrypt-later exposed.`,
586
+ }));
587
+ });
564
588
  return findings;
565
589
  },
566
590
  };
@@ -648,6 +672,12 @@ const tlsDetector = {
648
672
  /* -------------------------------------------------------------------------- */
649
673
  const RE_SSH_PUBKEY = /\b(ssh-rsa|ssh-ed25519|ssh-dss|ecdsa-sha2-nistp(?:256|384|521))\b/g;
650
674
  const RE_CERT_SIG_ALG = /\b(sha(?:1|256|384|512)WithRSAEncryption|ecdsa-with-SHA(?:1|256|384|512)|rsassaPss|dsaWithSHA(?:1|256))\b/g;
675
+ // Classical SSH key-exchange algorithm identifiers: finite-field DH groups
676
+ // (diffie-hellman-group{1,14,15,16,17,18} + group-exchange), ECDH over NIST
677
+ // curves (ecdh-sha2-nistp*), and Curve25519 (curve25519-sha256). All are
678
+ // Shor-broken key agreement — the harvest-now-decrypt-later surface a lexical
679
+ // scan misses when only the `crypto/*` API is matched, not the negotiated kex.
680
+ const RE_SSH_KEX = /\b(diffie-hellman-group(?:1|14|15|16|17|18)(?:-sha1|-sha256|-sha512)?|diffie-hellman-group-exchange-sha(?:1|256)|ecdh-sha2-nistp(?:256|384|521)|curve25519-sha256)\b/g;
651
681
  const RULE_SSH_PUBKEY = {
652
682
  id: "ssh-public-key",
653
683
  title: "Classical SSH public key",
@@ -660,7 +690,7 @@ const RULE_SSH_PUBKEY = {
660
690
  cwe: CWE_BROKEN_CRYPTO,
661
691
  sensitive: true,
662
692
  message: "A classical SSH public key is forgeable by a quantum attacker.",
663
- remediation: "Plan migration to PQC-capable SSH (e.g. sntrup761x25519 KEX, PQC host keys).",
693
+ remediation: "Plan migration to PQC-capable SSH: prefer the mlkem768x25519-sha256 KEX (ML-KEM-768 hybrid, OpenSSH 10's default since Apr 2025); sntrup761x25519 is an acceptable interim. Rotate to PQC host keys as they land.",
664
694
  };
665
695
  const RULE_CERT_SIG_ALG = {
666
696
  id: "cert-signature-algorithm",
@@ -675,6 +705,19 @@ const RULE_CERT_SIG_ALG = {
675
705
  message: "A classical certificate signature algorithm (RSA/ECDSA/DSA) is a quantum forgery surface.",
676
706
  remediation: "Plan re-issuance with PQC-capable CAs as ML-DSA certificate profiles mature.",
677
707
  };
708
+ const RULE_SSH_KEX = {
709
+ id: "ssh-kex-classical",
710
+ title: "Classical SSH key exchange",
711
+ description: "diffie-hellman-group* / group-exchange / ecdh-sha2-* / curve25519-sha256 kex",
712
+ category: "key-exchange",
713
+ severity: "medium",
714
+ confidence: "medium",
715
+ algorithm: "unknown",
716
+ hndl: true,
717
+ cwe: CWE_BROKEN_CRYPTO,
718
+ message: "A classical SSH key-exchange algorithm (finite-field DH / ECDH / X25519) is harvest-now-decrypt-later exposed.",
719
+ remediation: "Prefer the mlkem768x25519-sha256 KEX (ML-KEM-768 hybrid, OpenSSH 10 default); sntrup761x25519 is an acceptable interim.",
720
+ };
678
721
  /**
679
722
  * Detects classical SSH public keys (`authorized_keys` / `known_hosts` lines)
680
723
  * and X.509 certificate signature-algorithm identifiers in any text file. These
@@ -686,8 +729,10 @@ const sshCertDetector = {
686
729
  description: "SSH public keys and TLS/X.509 certificate signature algorithms in config",
687
730
  scope: "config",
688
731
  language: "any",
689
- rules: [RULE_SSH_PUBKEY, RULE_CERT_SIG_ALG],
690
- appliesTo: () => true,
732
+ rules: [RULE_SSH_PUBKEY, RULE_CERT_SIG_ALG, RULE_SSH_KEX],
733
+ // Skip prose/docs: a changelog or README that merely mentions `ssh-rsa` in a
734
+ // sentence is not crypto config. PEM material is caught by its own detector.
735
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
691
736
  detect({ file, content }) {
692
737
  const findings = [];
693
738
  // SSH public keys: ssh-rsa AAAA…, ecdsa-sha2-nistp256 …, ssh-ed25519 …
@@ -720,6 +765,70 @@ const sshCertDetector = {
720
765
  message: `Certificate signature algorithm "${tok}" is classical (RSA/ECDSA/DSA) — a quantum forgery surface.`,
721
766
  }));
722
767
  });
768
+ // SSH key-exchange algorithm identifiers (finite-field DH / ECDH / X25519).
769
+ eachMatch(RE_SSH_KEX, content, (m) => {
770
+ const tok = m[1];
771
+ const algorithm = tok.startsWith("diffie-hellman")
772
+ ? "DH"
773
+ : tok.startsWith("ecdh")
774
+ ? "ECDH"
775
+ : "X25519";
776
+ findings.push(findingFromRule(RULE_SSH_KEX, { file, content, index: m.index, matchLength: m[0].length }, {
777
+ title: `Classical SSH key exchange (${tok})`,
778
+ algorithm,
779
+ message: `SSH key-exchange "${tok}" is classical (${algorithm}) — harvest-now-decrypt-later exposed.`,
780
+ }));
781
+ });
782
+ return findings;
783
+ },
784
+ };
785
+ /* -------------------------------------------------------------------------- */
786
+ /* Classical TLS key-exchange cipher suites (language-agnostic config) */
787
+ /* -------------------------------------------------------------------------- */
788
+ // Classical KEX cipher-suite identifiers, in both the OpenSSL (`ECDHE-RSA-…`) and
789
+ // IANA (`TLS_ECDHE_RSA_WITH_…`) spellings, with `-` or `_` separators. These name
790
+ // a Shor-broken key exchange (ECDHE/DHE), so they are the harvest-now surface a
791
+ // PQC scanner should flag — distinct from the legacy-*version* / weak-cipher rules
792
+ // above. Case-sensitive on purpose: cipher-suite tokens are upper-case constants,
793
+ // so prose like "ecdhe rsa" does not misfire. No trailing \b so the IANA
794
+ // `TLS_ECDHE_RSA_WITH_…` form (a `_` follows `RSA`) still matches.
795
+ const RE_TLS_CLASSICAL_KEX = /\b(?:TLS_)?(?:ECDHE|ECDH|DHE)[-_](?:RSA|ECDSA|DSS)/g;
796
+ const RULE_TLS_CLASSICAL_KEX = {
797
+ id: "tls-classical-kex",
798
+ title: "Classical TLS key-exchange cipher suite",
799
+ description: "ECDHE / DHE cipher suites negotiate Shor-broken key exchange",
800
+ category: "tls",
801
+ severity: "medium",
802
+ confidence: "medium",
803
+ algorithm: "unknown",
804
+ hndl: true,
805
+ cwe: CWE_BROKEN_CRYPTO,
806
+ message: "Classical TLS key exchange (ECDHE/DHE) is harvest-now-decrypt-later exposed — the session key can be recorded now and recovered by a quantum attacker.",
807
+ remediation: "Adopt a PQC-hybrid TLS 1.3 key exchange (e.g. X25519MLKEM768) as your stack and peers support it; keep classical suites only as a transitional fallback.",
808
+ };
809
+ /**
810
+ * Detects classical TLS key-exchange cipher suites in any text/config file
811
+ * (OpenSSL cipher lists, IANA suite constants in Go/Java/C#/Rust, `.properties`,
812
+ * etc.). Language-agnostic because the suite identifiers are the same tokens
813
+ * everywhere — this is the cross-language TLS gap the legacy-version rule misses.
814
+ */
815
+ const tlsClassicalKexDetector = {
816
+ id: "tls-cipher-suite",
817
+ description: "Classical TLS key-exchange cipher suites (ECDHE/DHE) in any config",
818
+ scope: "config",
819
+ language: "any",
820
+ rules: [RULE_TLS_CLASSICAL_KEX],
821
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
822
+ detect({ file, content }) {
823
+ const findings = [];
824
+ eachMatch(RE_TLS_CLASSICAL_KEX, content, (m) => {
825
+ const tok = m[0];
826
+ const algorithm = tok.includes("ECDH") ? "ECDH" : "DH";
827
+ findings.push(findingFromRule(RULE_TLS_CLASSICAL_KEX, { file, content, index: m.index, matchLength: m[0].length }, {
828
+ algorithm,
829
+ message: `Classical TLS key-exchange suite "${tok}…" (${algorithm}) is harvest-now-decrypt-later exposed.`,
830
+ }));
831
+ });
723
832
  return findings;
724
833
  },
725
834
  };
@@ -731,5 +840,6 @@ export const sourceDetectors = [
731
840
  jwtDetector,
732
841
  tlsDetector,
733
842
  sshCertDetector,
843
+ tlsClassicalKexDetector,
734
844
  ];
735
845
  //# sourceMappingURL=source.js.map