@quantakrypto/core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/agent-types.d.ts +48 -0
  2. package/dist/agent-types.d.ts.map +1 -0
  3. package/dist/agent-types.js +2 -0
  4. package/dist/agent-types.js.map +1 -0
  5. package/dist/cache.d.ts +27 -0
  6. package/dist/cache.d.ts.map +1 -0
  7. package/dist/cache.js +93 -0
  8. package/dist/cache.js.map +1 -0
  9. package/dist/cbom.d.ts.map +1 -1
  10. package/dist/cbom.js +26 -1
  11. package/dist/cbom.js.map +1 -1
  12. package/dist/changed.d.ts.map +1 -1
  13. package/dist/changed.js +14 -2
  14. package/dist/changed.js.map +1 -1
  15. package/dist/codemods/config-toggle.d.ts +3 -0
  16. package/dist/codemods/config-toggle.d.ts.map +1 -0
  17. package/dist/codemods/config-toggle.js +26 -0
  18. package/dist/codemods/config-toggle.js.map +1 -0
  19. package/dist/codemods/registry.d.ts +26 -0
  20. package/dist/codemods/registry.d.ts.map +1 -0
  21. package/dist/codemods/registry.js +8 -0
  22. package/dist/codemods/registry.js.map +1 -0
  23. package/dist/comments.d.ts +45 -0
  24. package/dist/comments.d.ts.map +1 -0
  25. package/dist/comments.js +164 -0
  26. package/dist/comments.js.map +1 -0
  27. package/dist/config.d.ts +1 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +7 -0
  30. package/dist/config.js.map +1 -1
  31. package/dist/dependencies.d.ts +31 -13
  32. package/dist/dependencies.d.ts.map +1 -1
  33. package/dist/dependencies.js +460 -40
  34. package/dist/dependencies.js.map +1 -1
  35. package/dist/detect-utils.d.ts +44 -1
  36. package/dist/detect-utils.d.ts.map +1 -1
  37. package/dist/detect-utils.js +113 -17
  38. package/dist/detect-utils.js.map +1 -1
  39. package/dist/detectors/c.d.ts +20 -0
  40. package/dist/detectors/c.d.ts.map +1 -0
  41. package/dist/detectors/c.js +104 -0
  42. package/dist/detectors/c.js.map +1 -0
  43. package/dist/detectors/csharp.d.ts +15 -0
  44. package/dist/detectors/csharp.d.ts.map +1 -0
  45. package/dist/detectors/csharp.js +76 -0
  46. package/dist/detectors/csharp.js.map +1 -0
  47. package/dist/detectors/go.d.ts +25 -0
  48. package/dist/detectors/go.d.ts.map +1 -0
  49. package/dist/detectors/go.js +132 -0
  50. package/dist/detectors/go.js.map +1 -0
  51. package/dist/detectors/java.d.ts +25 -0
  52. package/dist/detectors/java.d.ts.map +1 -0
  53. package/dist/detectors/java.js +200 -0
  54. package/dist/detectors/java.js.map +1 -0
  55. package/dist/detectors/python.d.ts +26 -0
  56. package/dist/detectors/python.d.ts.map +1 -0
  57. package/dist/detectors/python.js +171 -0
  58. package/dist/detectors/python.js.map +1 -0
  59. package/dist/detectors/ruby.d.ts +15 -0
  60. package/dist/detectors/ruby.d.ts.map +1 -0
  61. package/dist/detectors/ruby.js +75 -0
  62. package/dist/detectors/ruby.js.map +1 -0
  63. package/dist/detectors/rust.d.ts +13 -0
  64. package/dist/detectors/rust.d.ts.map +1 -0
  65. package/dist/detectors/rust.js +94 -0
  66. package/dist/detectors/rust.js.map +1 -0
  67. package/dist/detectors/source.d.ts.map +1 -1
  68. package/dist/detectors/source.js +70 -22
  69. package/dist/detectors/source.js.map +1 -1
  70. package/dist/index.d.ts +18 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +15 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/parallel.d.ts +4 -0
  75. package/dist/parallel.d.ts.map +1 -1
  76. package/dist/parallel.js +59 -8
  77. package/dist/parallel.js.map +1 -1
  78. package/dist/patch-policy.d.ts +22 -0
  79. package/dist/patch-policy.d.ts.map +1 -0
  80. package/dist/patch-policy.js +21 -0
  81. package/dist/patch-policy.js.map +1 -0
  82. package/dist/redact.d.ts +19 -0
  83. package/dist/redact.d.ts.map +1 -0
  84. package/dist/redact.js +77 -0
  85. package/dist/redact.js.map +1 -0
  86. package/dist/registry.d.ts +13 -3
  87. package/dist/registry.d.ts.map +1 -1
  88. package/dist/registry.js +31 -4
  89. package/dist/registry.js.map +1 -1
  90. package/dist/remediate-pipeline.d.ts +41 -0
  91. package/dist/remediate-pipeline.d.ts.map +1 -0
  92. package/dist/remediate-pipeline.js +38 -0
  93. package/dist/remediate-pipeline.js.map +1 -0
  94. package/dist/remediate-request.d.ts +21 -0
  95. package/dist/remediate-request.d.ts.map +1 -0
  96. package/dist/remediate-request.js +28 -0
  97. package/dist/remediate-request.js.map +1 -0
  98. package/dist/remediation.d.ts +10 -0
  99. package/dist/remediation.d.ts.map +1 -1
  100. package/dist/remediation.js +21 -8
  101. package/dist/remediation.js.map +1 -1
  102. package/dist/report.d.ts.map +1 -1
  103. package/dist/report.js +16 -1
  104. package/dist/report.js.map +1 -1
  105. package/dist/scan-worker.js +6 -2
  106. package/dist/scan-worker.js.map +1 -1
  107. package/dist/scan.d.ts +5 -4
  108. package/dist/scan.d.ts.map +1 -1
  109. package/dist/scan.js +52 -29
  110. package/dist/scan.js.map +1 -1
  111. package/dist/triage.d.ts +27 -0
  112. package/dist/triage.d.ts.map +1 -0
  113. package/dist/triage.js +33 -0
  114. package/dist/triage.js.map +1 -0
  115. package/dist/types.d.ts +53 -3
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/verify.d.ts +30 -0
  118. package/dist/verify.d.ts.map +1 -0
  119. package/dist/verify.js +65 -0
  120. package/dist/verify.js.map +1 -0
  121. package/dist/version.d.ts +1 -1
  122. package/dist/version.js +1 -1
  123. package/dist/walk.d.ts +7 -0
  124. package/dist/walk.d.ts.map +1 -1
  125. package/dist/walk.js +85 -11
  126. package/dist/walk.js.map +1 -1
  127. package/dist/worktree.d.ts +6 -0
  128. package/dist/worktree.d.ts.map +1 -0
  129. package/dist/worktree.js +43 -0
  130. package/dist/worktree.js.map +1 -0
  131. package/package.json +1 -1
  132. package/src/agent-types.ts +52 -0
  133. package/src/cache.ts +120 -0
  134. package/src/cbom.ts +27 -1
  135. package/src/changed.ts +13 -4
  136. package/src/codemods/config-toggle.ts +35 -0
  137. package/src/codemods/registry.ts +31 -0
  138. package/src/comments.ts +180 -0
  139. package/src/config.ts +8 -0
  140. package/src/dependencies.ts +483 -51
  141. package/src/detect-utils.ts +123 -15
  142. package/src/detectors/c.ts +132 -0
  143. package/src/detectors/csharp.ts +98 -0
  144. package/src/detectors/go.ts +167 -0
  145. package/src/detectors/java.ts +235 -0
  146. package/src/detectors/python.ts +212 -0
  147. package/src/detectors/ruby.ts +97 -0
  148. package/src/detectors/rust.ts +112 -0
  149. package/src/detectors/source.ts +69 -21
  150. package/src/index.ts +45 -1
  151. package/src/parallel.ts +63 -6
  152. package/src/patch-policy.ts +42 -0
  153. package/src/redact.ts +92 -0
  154. package/src/registry.ts +32 -4
  155. package/src/remediate-pipeline.ts +81 -0
  156. package/src/remediate-request.ts +52 -0
  157. package/src/remediation.ts +27 -11
  158. package/src/report.ts +27 -1
  159. package/src/scan-worker.ts +6 -2
  160. package/src/scan.ts +62 -31
  161. package/src/triage.ts +58 -0
  162. package/src/types.ts +66 -3
  163. package/src/verify.ts +82 -0
  164. package/src/version.ts +1 -1
  165. package/src/walk.ts +81 -12
  166. package/src/worktree.ts +45 -0
@@ -19,29 +19,63 @@ export interface LineCol {
19
19
  column: number;
20
20
  }
21
21
 
22
+ /**
23
+ * Memoized line-start offsets for the CURRENT content string. A scan processes
24
+ * all of one file's findings consecutively against the same `content` reference,
25
+ * so a single-entry cache turns the previously O(offset)-per-finding line/column
26
+ * math into O(log n) — the whole file is O(n log n) instead of O(n²). Without
27
+ * this, a large file with many findings scaled quadratically (a real perf cliff,
28
+ * caught by the ReDoS/time-budget test).
29
+ */
30
+ let cachedContent: string | null = null;
31
+ let cachedLineStarts: number[] = [];
32
+ function lineStartsFor(content: string): number[] {
33
+ if (content === cachedContent) return cachedLineStarts;
34
+ const starts = [0];
35
+ for (let i = 0; i < content.length; i++) {
36
+ if (content.charCodeAt(i) === 10 /* \n */) starts.push(i + 1);
37
+ }
38
+ cachedContent = content;
39
+ cachedLineStarts = starts;
40
+ return starts;
41
+ }
42
+
43
+ /** Index of the line containing `offset` (0-based into the line-starts array). */
44
+ function lineIndexFor(starts: readonly number[], offset: number): number {
45
+ let lo = 0;
46
+ let hi = starts.length - 1;
47
+ let best = 0;
48
+ while (lo <= hi) {
49
+ const mid = (lo + hi) >>> 1;
50
+ if (starts[mid] <= offset) {
51
+ best = mid;
52
+ lo = mid + 1;
53
+ } else {
54
+ hi = mid - 1;
55
+ }
56
+ }
57
+ return best;
58
+ }
59
+
22
60
  /**
23
61
  * Convert a 0-based character offset within `content` into a 1-based
24
62
  * line/column. Newlines are LF; CR is treated as an ordinary character, so on
25
63
  * CRLF files the column includes the trailing CR offset harmlessly.
26
64
  */
27
65
  export function offsetToLineCol(content: string, offset: number): LineCol {
28
- let line = 1;
29
- let lastNewline = -1;
30
- for (let i = 0; i < offset && i < content.length; i++) {
31
- if (content.charCodeAt(i) === 10 /* \n */) {
32
- line++;
33
- lastNewline = i;
34
- }
35
- }
36
- return { line, column: offset - lastNewline };
66
+ const starts = lineStartsFor(content);
67
+ const idx = lineIndexFor(starts, offset);
68
+ return { line: idx + 1, column: offset - starts[idx] + 1 };
37
69
  }
38
70
 
39
71
  /** Extract the (trimmed) single source line containing `offset`. */
40
72
  export function lineAt(content: string, offset: number): string {
41
- let start = offset;
42
- while (start > 0 && content.charCodeAt(start - 1) !== 10) start--;
43
- let end = offset;
44
- while (end < content.length && content.charCodeAt(end) !== 10) end++;
73
+ const starts = lineStartsFor(content);
74
+ const idx = lineIndexFor(starts, offset);
75
+ const start = starts[idx];
76
+ const nextStart = idx + 1 < starts.length ? starts[idx + 1] : content.length + 1;
77
+ // nextStart points just past the '\n'; the line content ends before it.
78
+ const end = Math.min(nextStart - 1, content.length);
45
79
  return content.slice(start, end).replace(/\r$/, "").trim();
46
80
  }
47
81
 
@@ -166,8 +200,82 @@ export function hasExtension(filePath: string, exts: readonly string[]): boolean
166
200
  return exts.some((e) => lower.endsWith(e));
167
201
  }
168
202
 
169
- /** JavaScript / TypeScript source extensions handled by the source detectors. */
170
- export const JS_TS_EXTENSIONS: readonly string[] = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"];
203
+ /**
204
+ * JavaScript / TypeScript source extensions handled by the source detectors.
205
+ * `.vue` / `.svelte` single-file components embed a `<script>` block, so the
206
+ * lexical detectors catch crypto usage inside them.
207
+ */
208
+ export const JS_TS_EXTENSIONS: readonly string[] = [
209
+ ".js",
210
+ ".jsx",
211
+ ".ts",
212
+ ".tsx",
213
+ ".mjs",
214
+ ".cjs",
215
+ ".vue",
216
+ ".svelte",
217
+ ];
218
+
219
+ /** Python source extensions handled by the Python detector. */
220
+ export const PYTHON_EXTENSIONS: readonly string[] = [".py", ".pyi", ".pyw"];
221
+
222
+ /** Go source extensions handled by the Go detector. */
223
+ export const GO_EXTENSIONS: readonly string[] = [".go"];
224
+
225
+ /** Java / Kotlin source extensions handled by the JCA detector. */
226
+ export const JAVA_EXTENSIONS: readonly string[] = [".java", ".kt", ".kts"];
227
+
228
+ /** C# source extensions handled by the .NET detector. */
229
+ export const CSHARP_EXTENSIONS: readonly string[] = [".cs"];
230
+
231
+ /** Rust source extensions handled by the Rust detector. */
232
+ export const RUST_EXTENSIONS: readonly string[] = [".rs"];
233
+
234
+ /** Ruby source extensions handled by the Ruby detector. */
235
+ export const RUBY_EXTENSIONS: readonly string[] = [".rb"];
236
+
237
+ /** C / C++ source extensions handled by the OpenSSL detector. */
238
+ export const C_EXTENSIONS: readonly string[] = [".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hh"];
239
+
240
+ /**
241
+ * File-literal surfaces where a JWT/JOSE algorithm string (`"RS256"`, `"ES256"`)
242
+ * is the same evidence regardless of language. Used to un-gate the JWT detector
243
+ * from JS-only. Kept to JS/TS + Python for now (both quote the alg token, so the
244
+ * regex stays precise); YAML/JSON config carry unquoted tokens and higher FP
245
+ * risk, so they wait for a config-aware pass.
246
+ */
247
+ export const JWT_HOST_EXTENSIONS: readonly string[] = [...JS_TS_EXTENSIONS, ...PYTHON_EXTENSIONS];
248
+
249
+ /**
250
+ * Extensions the scanner can actually analyze for inline crypto usage today
251
+ * (the language-specific source detectors). A scan that walked files but found
252
+ * none of these has NOT meaningfully assessed the codebase — reporters surface
253
+ * that ({@link CryptoInventory}/coverage), so a bare 100/100 can't masquerade as
254
+ * "safe" on, say, a Go or Rust repo. PEM / SSH / dependency detectors run on any
255
+ * file and are intentionally excluded here.
256
+ */
257
+ export const ANALYZABLE_SOURCE_EXTENSIONS: readonly string[] = [
258
+ ...JS_TS_EXTENSIONS,
259
+ ...PYTHON_EXTENSIONS,
260
+ ...GO_EXTENSIONS,
261
+ ...JAVA_EXTENSIONS,
262
+ ...CSHARP_EXTENSIONS,
263
+ ...RUST_EXTENSIONS,
264
+ ...RUBY_EXTENSIONS,
265
+ ...C_EXTENSIONS,
266
+ ];
267
+
268
+ /**
269
+ * Human label for the source languages the scanner can analyze for inline
270
+ * crypto, shown in coverage output. Kept next to {@link
271
+ * ANALYZABLE_SOURCE_EXTENSIONS} so a new language pack updates one place.
272
+ */
273
+ export const ANALYZABLE_LANGUAGES_LABEL = "JS/TS, Python, Go, Java, C#, Rust, Ruby, C/C++";
274
+
275
+ /** True when a path is in a source language the scanner can analyze for crypto. */
276
+ export function isAnalyzableSource(filePath: string): boolean {
277
+ return hasExtension(filePath, ANALYZABLE_SOURCE_EXTENSIONS);
278
+ }
171
279
 
172
280
  /**
173
281
  * Given a SORTED ascending array of call offsets, return true when `idx` is at
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Source-code detector for classical asymmetric cryptography in C / C++ using
3
+ * OpenSSL (the dominant crypto library for the ecosystem). Lexical, same
4
+ * strategy as the other packs; the OpenSSL function names are distinctive.
5
+ *
6
+ * NOTE: scanning OpenSSL's OWN source/headers will naturally light up (the
7
+ * library declares these symbols) — that is inherent to lexical scanning of a
8
+ * crypto library, not a false positive in application code. Matches the classic
9
+ * `*_generate_key` / `ECDSA_sign` / `ECDH_compute_key` call forms plus the
10
+ * `EVP_RSA_gen` helper.
11
+ *
12
+ * HNDL: RSA encryption and (EC)DH key agreement are harvest-now-decrypt-later
13
+ * exposed (hndl:true); ECDSA / DSA signatures are hndl:false but forgeable.
14
+ * `EC_KEY_generate_key` is ambiguous (feeds ECDSA + ECDH), classified
15
+ * conservatively as key-exchange-capable (hndl:true).
16
+ */
17
+ import type { Detector, Finding, RuleMeta } from "../types.js";
18
+ import { C_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
19
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
20
+
21
+ const RE_C_RSA = /\bRSA_generate_key(?:_ex)?\s*\(|\bEVP_RSA_gen\s*\(/g;
22
+ const RE_C_EC = /\bEC_KEY_generate_key\s*\(|\bEC_KEY_new_by_curve_name\s*\(/g;
23
+ const RE_C_ECDSA = /\bECDSA_do_sign\s*\(|\bECDSA_sign\s*\(/g;
24
+ const RE_C_ECDH = /\bECDH_compute_key\s*\(/g;
25
+ const RE_C_DSA = /\bDSA_generate_key\s*\(|\bDSA_generate_parameters(?:_ex)?\s*\(/g;
26
+ const RE_C_DH = /\bDH_generate_key\s*\(|\bDH_generate_parameters(?:_ex)?\s*\(/g;
27
+
28
+ const RULE_C_RSA: RuleMeta = {
29
+ id: "c-rsa-keygen",
30
+ title: "C/OpenSSL RSA key generation",
31
+ description: "OpenSSL RSA_generate_key(_ex) / EVP_RSA_gen",
32
+ category: "kem",
33
+ severity: "high",
34
+ confidence: "high",
35
+ algorithm: "RSA",
36
+ hndl: true,
37
+ cwe: CWE_BROKEN_CRYPTO,
38
+ message: "Generates a classical RSA key pair (C/OpenSSL), which is not quantum-safe.",
39
+ };
40
+ const RULE_C_EC: RuleMeta = {
41
+ id: "c-ec-keygen",
42
+ title: "C/OpenSSL EC key generation",
43
+ description: "OpenSSL EC_KEY_generate_key / EC_KEY_new_by_curve_name",
44
+ category: "key-exchange",
45
+ severity: "high",
46
+ confidence: "high",
47
+ algorithm: "ECDH",
48
+ hndl: true,
49
+ cwe: CWE_BROKEN_CRYPTO,
50
+ message:
51
+ "Generates a classical EC key pair (C/OpenSSL). EC keys feed BOTH ECDSA signatures and ECDH key agreement; the ECDH path is harvest-now-decrypt-later exposed.",
52
+ remediation:
53
+ "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
54
+ };
55
+ const RULE_C_ECDSA: RuleMeta = {
56
+ id: "c-ecdsa",
57
+ title: "C/OpenSSL ECDSA signature",
58
+ description: "OpenSSL ECDSA_sign / ECDSA_do_sign",
59
+ category: "signature",
60
+ severity: "high",
61
+ confidence: "high",
62
+ algorithm: "ECDSA",
63
+ hndl: false,
64
+ cwe: CWE_BROKEN_CRYPTO,
65
+ message: "Classical ECDSA signing (C/OpenSSL) is forgeable by a quantum attacker.",
66
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
67
+ };
68
+ const RULE_C_ECDH: RuleMeta = {
69
+ id: "c-ecdh",
70
+ title: "C/OpenSSL ECDH key agreement",
71
+ description: "OpenSSL ECDH_compute_key",
72
+ category: "key-exchange",
73
+ severity: "high",
74
+ confidence: "high",
75
+ algorithm: "ECDH",
76
+ hndl: true,
77
+ cwe: CWE_BROKEN_CRYPTO,
78
+ message:
79
+ "Elliptic-curve Diffie-Hellman (C/OpenSSL) is broken by Shor's algorithm (harvest-now-decrypt-later).",
80
+ };
81
+ const RULE_C_DSA: RuleMeta = {
82
+ id: "c-dsa",
83
+ title: "C/OpenSSL DSA key/usage",
84
+ description: "OpenSSL DSA_generate_key / DSA_generate_parameters",
85
+ category: "signature",
86
+ severity: "high",
87
+ confidence: "high",
88
+ algorithm: "DSA",
89
+ hndl: false,
90
+ cwe: CWE_BROKEN_CRYPTO,
91
+ message: "Classical DSA (C/OpenSSL) is deprecated and forgeable by a quantum attacker.",
92
+ remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
93
+ };
94
+ const RULE_C_DH: RuleMeta = {
95
+ id: "c-dh",
96
+ title: "C/OpenSSL Diffie-Hellman key exchange",
97
+ description: "OpenSSL DH_generate_key / DH_generate_parameters",
98
+ category: "key-exchange",
99
+ severity: "high",
100
+ confidence: "high",
101
+ algorithm: "DH",
102
+ hndl: true,
103
+ cwe: CWE_BROKEN_CRYPTO,
104
+ message:
105
+ "Finite-field Diffie-Hellman (C/OpenSSL) is broken by Shor's algorithm (harvest-now-decrypt-later).",
106
+ };
107
+
108
+ /** Detects classical asymmetric crypto in C/C++ (OpenSSL). */
109
+ export const cDetector: Detector = {
110
+ id: "c-crypto",
111
+ description: "Classical asymmetric crypto in C/C++ (OpenSSL)",
112
+ scope: "source",
113
+ language: "c",
114
+ rules: [RULE_C_RSA, RULE_C_EC, RULE_C_ECDSA, RULE_C_ECDH, RULE_C_DSA, RULE_C_DH],
115
+ appliesTo: (f) => hasExtension(f, C_EXTENSIONS),
116
+ detect({ file, content }): Finding[] {
117
+ const findings: Finding[] = [];
118
+ const add = (re: RegExp, rule: RuleMeta) =>
119
+ eachMatch(re, content, (m) =>
120
+ findings.push(
121
+ findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
122
+ ),
123
+ );
124
+ add(RE_C_RSA, RULE_C_RSA);
125
+ add(RE_C_EC, RULE_C_EC);
126
+ add(RE_C_ECDSA, RULE_C_ECDSA);
127
+ add(RE_C_ECDH, RULE_C_ECDH);
128
+ add(RE_C_DSA, RULE_C_DSA);
129
+ add(RE_C_DH, RULE_C_DH);
130
+ return findings;
131
+ },
132
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Source-code detector for classical asymmetric cryptography in C# / .NET
3
+ * (`System.Security.Cryptography`). Same lexical strategy as the other packs.
4
+ * .NET exposes each algorithm through a factory (`RSA.Create()`,
5
+ * `ECDsa.Create()`, `ECDiffieHellman.Create()`) or a concrete CSP/CNG class
6
+ * (`RSACryptoServiceProvider`, `ECDsaCng`, …), all of which are precise signals.
7
+ *
8
+ * HNDL: RSA (keygen/encryption) and (EC)DH key agreement are harvest-now-
9
+ * decrypt-later exposed (hndl:true); ECDSA/DSA signatures are hndl:false but
10
+ * forgeable by a quantum attacker.
11
+ */
12
+ import type { Detector, Finding, RuleMeta } from "../types.js";
13
+ import { CSHARP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
14
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
15
+
16
+ const RE_CS_RSA =
17
+ /\bRSA\.Create\s*\(|\bnew\s+RSACryptoServiceProvider\s*\(|\bnew\s+RSACng\s*\(|\bnew\s+RSAOpenSsl\s*\(/g;
18
+ const RE_CS_ECDSA = /\bECDsa\.Create\s*\(|\bnew\s+ECDsaCng\s*\(|\bnew\s+ECDsaOpenSsl\s*\(/g;
19
+ const RE_CS_ECDH =
20
+ /\bECDiffieHellman\.Create\s*\(|\bnew\s+ECDiffieHellmanCng\s*\(|\bnew\s+ECDiffieHellmanOpenSsl\s*\(/g;
21
+ const RE_CS_DSA = /\bDSA\.Create\s*\(|\bnew\s+DSACryptoServiceProvider\s*\(|\bnew\s+DSACng\s*\(/g;
22
+
23
+ const RULE_CS_RSA: RuleMeta = {
24
+ id: "csharp-rsa",
25
+ title: "C# RSA key/usage",
26
+ description: "System.Security.Cryptography RSA.Create / RSACryptoServiceProvider / RSACng",
27
+ category: "kem",
28
+ severity: "high",
29
+ confidence: "high",
30
+ algorithm: "RSA",
31
+ hndl: true,
32
+ cwe: CWE_BROKEN_CRYPTO,
33
+ message: "Classical RSA (.NET) is not quantum-safe and RSA encryption is HNDL-exposed.",
34
+ };
35
+ const RULE_CS_ECDSA: RuleMeta = {
36
+ id: "csharp-ecdsa",
37
+ title: "C# ECDSA signature",
38
+ description: "System.Security.Cryptography ECDsa.Create / ECDsaCng",
39
+ category: "signature",
40
+ severity: "high",
41
+ confidence: "high",
42
+ algorithm: "ECDSA",
43
+ hndl: false,
44
+ cwe: CWE_BROKEN_CRYPTO,
45
+ message: "Classical ECDSA signing (.NET) is forgeable by a quantum attacker.",
46
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
47
+ };
48
+ const RULE_CS_ECDH: RuleMeta = {
49
+ id: "csharp-ecdh",
50
+ title: "C# ECDH key agreement",
51
+ description: "System.Security.Cryptography ECDiffieHellman.Create / ECDiffieHellmanCng",
52
+ category: "key-exchange",
53
+ severity: "high",
54
+ confidence: "high",
55
+ algorithm: "ECDH",
56
+ hndl: true,
57
+ cwe: CWE_BROKEN_CRYPTO,
58
+ message:
59
+ "Elliptic-curve Diffie-Hellman (.NET) is broken by Shor's algorithm (harvest-now-decrypt-later).",
60
+ };
61
+ const RULE_CS_DSA: RuleMeta = {
62
+ id: "csharp-dsa",
63
+ title: "C# DSA key/signature",
64
+ description: "System.Security.Cryptography DSA.Create / DSACryptoServiceProvider",
65
+ category: "signature",
66
+ severity: "high",
67
+ confidence: "high",
68
+ algorithm: "DSA",
69
+ hndl: false,
70
+ cwe: CWE_BROKEN_CRYPTO,
71
+ message: "Classical DSA (.NET) is deprecated and forgeable by a quantum attacker.",
72
+ remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
73
+ };
74
+
75
+ /** Detects classical asymmetric crypto in C# (System.Security.Cryptography). */
76
+ export const csharpDetector: Detector = {
77
+ id: "csharp-crypto",
78
+ description: "Classical asymmetric crypto in C#/.NET (System.Security.Cryptography)",
79
+ scope: "source",
80
+ language: "csharp",
81
+ rules: [RULE_CS_RSA, RULE_CS_ECDSA, RULE_CS_ECDH, RULE_CS_DSA],
82
+ appliesTo: (f) => hasExtension(f, CSHARP_EXTENSIONS),
83
+ detect({ file, content }): Finding[] {
84
+ const findings: Finding[] = [];
85
+ const add = (re: RegExp, rule: RuleMeta) =>
86
+ eachMatch(re, content, (m) =>
87
+ findings.push(
88
+ findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
89
+ ),
90
+ );
91
+ // ECDSA / ECDH before RSA/DSA so the specific EC factories win.
92
+ add(RE_CS_ECDSA, RULE_CS_ECDSA);
93
+ add(RE_CS_ECDH, RULE_CS_ECDH);
94
+ add(RE_CS_RSA, RULE_CS_RSA);
95
+ add(RE_CS_DSA, RULE_CS_DSA);
96
+ return findings;
97
+ },
98
+ };
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Source-code detector for classical, non-quantum-safe asymmetric cryptography
3
+ * in Go. Same lexical (regex-over-source) strategy as the JS/TS and Python
4
+ * detectors. Go's crypto lives almost entirely in the highly-standardized
5
+ * `crypto/*` standard library, so package-qualified calls (`rsa.GenerateKey`,
6
+ * `ecdsa.SignASN1`, `ecdh.X25519`, …) are precise, low-false-positive signals.
7
+ *
8
+ * Covered:
9
+ * - crypto/rsa — GenerateKey, EncryptOAEP/PKCS1v15, SignPKCS1v15/PSS
10
+ * - crypto/ecdsa — GenerateKey, Sign/SignASN1
11
+ * - crypto/ecdh — P256/P384/P521/X25519 curve construction (key agreement)
12
+ * - crypto/ed25519 — GenerateKey, Sign
13
+ * - crypto/dsa — GenerateKey, GenerateParameters (deprecated)
14
+ *
15
+ * HNDL policy mirrors the other detectors: KEM / key agreement (RSA encryption,
16
+ * ECDH) is harvest-now-decrypt-later exposed (hndl:true); signatures (RSA-sign,
17
+ * ECDSA, Ed25519, DSA) are hndl:false but still forgeable by a quantum attacker.
18
+ * Unlike Node's ambiguous `'ec'` keygen, Go's `ecdsa.GenerateKey` is
19
+ * signature-specific and Go's key agreement lives in the separate `crypto/ecdh`
20
+ * package, so the two are classified precisely rather than conservatively merged.
21
+ */
22
+ import type { Detector, Finding, RuleMeta } from "../types.js";
23
+ import { GO_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
24
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
25
+
26
+ /* -------------------------------------------------------------------------- */
27
+ /* Precompiled regexes (module scope) */
28
+ /* -------------------------------------------------------------------------- */
29
+
30
+ const RE_GO_RSA_KEYGEN = /\brsa\.GenerateKey\s*\(|\brsa\.GenerateMultiPrimeKey\s*\(/g;
31
+ const RE_GO_RSA_ENCRYPT = /\brsa\.EncryptOAEP\s*\(|\brsa\.EncryptPKCS1v15\s*\(/g;
32
+ const RE_GO_RSA_SIGN = /\brsa\.SignPKCS1v15\s*\(|\brsa\.SignPSS\s*\(/g;
33
+ const RE_GO_ECDSA = /\becdsa\.GenerateKey\s*\(|\becdsa\.SignASN1\s*\(|\becdsa\.Sign\s*\(/g;
34
+ const RE_GO_ECDH = /\becdh\.(?:P256|P384|P521|X25519)\s*\(/g;
35
+ const RE_GO_ED25519 = /\bed25519\.GenerateKey\s*\(|\bed25519\.Sign\s*\(/g;
36
+ const RE_GO_DSA = /\bdsa\.GenerateKey\s*\(|\bdsa\.GenerateParameters\s*\(/g;
37
+
38
+ /* -------------------------------------------------------------------------- */
39
+ /* Rule catalog */
40
+ /* -------------------------------------------------------------------------- */
41
+
42
+ const RULE_GO_RSA_KEYGEN: RuleMeta = {
43
+ id: "go-rsa-keygen",
44
+ title: "Go RSA key generation",
45
+ description: "crypto/rsa GenerateKey / GenerateMultiPrimeKey",
46
+ category: "kem",
47
+ severity: "high",
48
+ confidence: "high",
49
+ algorithm: "RSA",
50
+ hndl: true,
51
+ cwe: CWE_BROKEN_CRYPTO,
52
+ message: "Generates a classical RSA key pair (Go), which is not quantum-safe.",
53
+ };
54
+ const RULE_GO_RSA_ENCRYPT: RuleMeta = {
55
+ id: "go-rsa-encrypt",
56
+ title: "Go RSA public-key encryption",
57
+ description: "crypto/rsa EncryptOAEP / EncryptPKCS1v15",
58
+ category: "kem",
59
+ severity: "high",
60
+ confidence: "high",
61
+ algorithm: "RSA",
62
+ hndl: true,
63
+ cwe: CWE_BROKEN_CRYPTO,
64
+ message:
65
+ "RSA public-key encryption (Go) is broken by Shor's algorithm and exposed to harvest-now-decrypt-later.",
66
+ };
67
+ const RULE_GO_RSA_SIGN: RuleMeta = {
68
+ id: "go-rsa-sign",
69
+ title: "Go RSA signature",
70
+ description: "crypto/rsa SignPKCS1v15 / SignPSS",
71
+ category: "signature",
72
+ severity: "high",
73
+ confidence: "high",
74
+ algorithm: "RSA",
75
+ hndl: false,
76
+ cwe: CWE_BROKEN_CRYPTO,
77
+ message: "Classical RSA signing (Go) is forgeable by a quantum attacker.",
78
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
79
+ };
80
+ const RULE_GO_ECDSA: RuleMeta = {
81
+ id: "go-ecdsa",
82
+ title: "Go ECDSA key/signature",
83
+ description: "crypto/ecdsa GenerateKey / Sign / SignASN1",
84
+ category: "signature",
85
+ severity: "high",
86
+ confidence: "high",
87
+ algorithm: "ECDSA",
88
+ hndl: false,
89
+ cwe: CWE_BROKEN_CRYPTO,
90
+ message: "Classical ECDSA (Go) is forgeable by a quantum attacker.",
91
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
92
+ };
93
+ const RULE_GO_ECDH: RuleMeta = {
94
+ id: "go-ecdh",
95
+ title: "Go ECDH key exchange",
96
+ description: "crypto/ecdh P256/P384/P521/X25519 key agreement",
97
+ category: "key-exchange",
98
+ severity: "high",
99
+ confidence: "high",
100
+ algorithm: "ECDH",
101
+ hndl: true,
102
+ cwe: CWE_BROKEN_CRYPTO,
103
+ message:
104
+ "Elliptic-curve Diffie-Hellman (Go crypto/ecdh) is broken by Shor's algorithm (harvest-now-decrypt-later).",
105
+ };
106
+ const RULE_GO_ED25519: RuleMeta = {
107
+ id: "go-ed25519",
108
+ title: "Go Ed25519 signature",
109
+ description: "crypto/ed25519 GenerateKey / Sign",
110
+ category: "signature",
111
+ severity: "low",
112
+ confidence: "high",
113
+ algorithm: "EdDSA",
114
+ hndl: false,
115
+ cwe: CWE_BROKEN_CRYPTO,
116
+ message: "Ed25519 (Go) is a modern but still classical signature scheme.",
117
+ };
118
+ const RULE_GO_DSA: RuleMeta = {
119
+ id: "go-dsa",
120
+ title: "Go DSA key/usage",
121
+ description: "crypto/dsa GenerateKey / GenerateParameters",
122
+ category: "signature",
123
+ severity: "high",
124
+ confidence: "high",
125
+ algorithm: "DSA",
126
+ hndl: false,
127
+ cwe: CWE_BROKEN_CRYPTO,
128
+ message: "Classical DSA (Go) is deprecated and forgeable by a quantum attacker.",
129
+ remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
130
+ };
131
+
132
+ /** Detects classical asymmetric crypto in Go source (crypto/* standard library). */
133
+ export const goDetector: Detector = {
134
+ id: "go-crypto",
135
+ description: "Classical asymmetric crypto in Go (crypto/rsa, ecdsa, ecdh, ed25519, dsa)",
136
+ scope: "source",
137
+ language: "go",
138
+ rules: [
139
+ RULE_GO_RSA_KEYGEN,
140
+ RULE_GO_RSA_ENCRYPT,
141
+ RULE_GO_RSA_SIGN,
142
+ RULE_GO_ECDSA,
143
+ RULE_GO_ECDH,
144
+ RULE_GO_ED25519,
145
+ RULE_GO_DSA,
146
+ ],
147
+ appliesTo: (f) => hasExtension(f, GO_EXTENSIONS),
148
+ detect({ file, content }): Finding[] {
149
+ const findings: Finding[] = [];
150
+ const add = (re: RegExp, rule: RuleMeta) =>
151
+ eachMatch(re, content, (m) =>
152
+ findings.push(
153
+ findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
154
+ ),
155
+ );
156
+
157
+ add(RE_GO_RSA_KEYGEN, RULE_GO_RSA_KEYGEN);
158
+ add(RE_GO_RSA_ENCRYPT, RULE_GO_RSA_ENCRYPT);
159
+ add(RE_GO_RSA_SIGN, RULE_GO_RSA_SIGN);
160
+ add(RE_GO_ECDSA, RULE_GO_ECDSA);
161
+ add(RE_GO_ECDH, RULE_GO_ECDH);
162
+ add(RE_GO_ED25519, RULE_GO_ED25519);
163
+ add(RE_GO_DSA, RULE_GO_DSA);
164
+
165
+ return findings;
166
+ },
167
+ };