@quantakrypto/core 0.4.1 → 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 (140) 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.d.ts.map +1 -1
  7. package/dist/cbom.js +13 -4
  8. package/dist/cbom.js.map +1 -1
  9. package/dist/changed.js.map +1 -1
  10. package/dist/codemods/config-toggle.js.map +1 -1
  11. package/dist/codemods/registry.js.map +1 -1
  12. package/dist/comments.d.ts +26 -0
  13. package/dist/comments.d.ts.map +1 -1
  14. package/dist/comments.js +141 -1
  15. package/dist/comments.js.map +1 -1
  16. package/dist/config.js.map +1 -1
  17. package/dist/cwe.js.map +1 -1
  18. package/dist/dependencies.d.ts +3 -2
  19. package/dist/dependencies.d.ts.map +1 -1
  20. package/dist/dependencies.js +151 -4
  21. package/dist/dependencies.js.map +1 -1
  22. package/dist/detect-utils.d.ts +14 -3
  23. package/dist/detect-utils.d.ts.map +1 -1
  24. package/dist/detect-utils.js +32 -4
  25. package/dist/detect-utils.js.map +1 -1
  26. package/dist/detectors/c.d.ts.map +1 -1
  27. package/dist/detectors/c.js +190 -2
  28. package/dist/detectors/c.js.map +1 -1
  29. package/dist/detectors/csharp.d.ts.map +1 -1
  30. package/dist/detectors/csharp.js +141 -3
  31. package/dist/detectors/csharp.js.map +1 -1
  32. package/dist/detectors/go.d.ts +5 -3
  33. package/dist/detectors/go.d.ts.map +1 -1
  34. package/dist/detectors/go.js +187 -3
  35. package/dist/detectors/go.js.map +1 -1
  36. package/dist/detectors/java.d.ts.map +1 -1
  37. package/dist/detectors/java.js +168 -3
  38. package/dist/detectors/java.js.map +1 -1
  39. package/dist/detectors/pem.d.ts.map +1 -1
  40. package/dist/detectors/pem.js +49 -1
  41. package/dist/detectors/pem.js.map +1 -1
  42. package/dist/detectors/python.d.ts.map +1 -1
  43. package/dist/detectors/python.js +74 -3
  44. package/dist/detectors/python.js.map +1 -1
  45. package/dist/detectors/ruby.d.ts +9 -5
  46. package/dist/detectors/ruby.d.ts.map +1 -1
  47. package/dist/detectors/ruby.js +122 -2
  48. package/dist/detectors/ruby.js.map +1 -1
  49. package/dist/detectors/rust.d.ts +8 -3
  50. package/dist/detectors/rust.d.ts.map +1 -1
  51. package/dist/detectors/rust.js +185 -3
  52. package/dist/detectors/rust.js.map +1 -1
  53. package/dist/detectors/source.d.ts.map +1 -1
  54. package/dist/detectors/source.js +131 -13
  55. package/dist/detectors/source.js.map +1 -1
  56. package/dist/detectors/stateful-hbs.d.ts +22 -0
  57. package/dist/detectors/stateful-hbs.d.ts.map +1 -0
  58. package/dist/detectors/stateful-hbs.js +139 -0
  59. package/dist/detectors/stateful-hbs.js.map +1 -0
  60. package/dist/errors.js.map +1 -1
  61. package/dist/evidence.d.ts +48 -0
  62. package/dist/evidence.d.ts.map +1 -0
  63. package/dist/evidence.js +76 -0
  64. package/dist/evidence.js.map +1 -0
  65. package/dist/index.d.ts +4 -2
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +4 -2
  68. package/dist/index.js.map +1 -1
  69. package/dist/inventory.d.ts +2 -0
  70. package/dist/inventory.d.ts.map +1 -1
  71. package/dist/inventory.js +27 -1
  72. package/dist/inventory.js.map +1 -1
  73. package/dist/parallel.js +15 -0
  74. package/dist/parallel.js.map +1 -1
  75. package/dist/patch-policy.js.map +1 -1
  76. package/dist/redact.d.ts.map +1 -1
  77. package/dist/redact.js +122 -7
  78. package/dist/redact.js.map +1 -1
  79. package/dist/registry.d.ts +2 -2
  80. package/dist/registry.d.ts.map +1 -1
  81. package/dist/registry.js +4 -2
  82. package/dist/registry.js.map +1 -1
  83. package/dist/remediate-pipeline.d.ts +3 -0
  84. package/dist/remediate-pipeline.d.ts.map +1 -1
  85. package/dist/remediate-pipeline.js +50 -0
  86. package/dist/remediate-pipeline.js.map +1 -1
  87. package/dist/remediate-request.js.map +1 -1
  88. package/dist/remediation.d.ts +9 -3
  89. package/dist/remediation.d.ts.map +1 -1
  90. package/dist/remediation.js +33 -6
  91. package/dist/remediation.js.map +1 -1
  92. package/dist/report.d.ts +9 -0
  93. package/dist/report.d.ts.map +1 -1
  94. package/dist/report.js +31 -0
  95. package/dist/report.js.map +1 -1
  96. package/dist/scan-worker.js.map +1 -1
  97. package/dist/scan.d.ts.map +1 -1
  98. package/dist/scan.js +16 -2
  99. package/dist/scan.js.map +1 -1
  100. package/dist/severity.js.map +1 -1
  101. package/dist/triage.js.map +1 -1
  102. package/dist/types.d.ts +8 -1
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js.map +1 -1
  105. package/dist/verify.js.map +1 -1
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/dist/version.js.map +1 -1
  109. package/dist/walk.js.map +1 -1
  110. package/dist/worktree.d.ts.map +1 -1
  111. package/dist/worktree.js +10 -1
  112. package/dist/worktree.js.map +1 -1
  113. package/package.json +1 -1
  114. package/src/cbom.ts +13 -4
  115. package/src/comments.ts +140 -1
  116. package/src/dependencies.ts +156 -6
  117. package/src/detect-utils.ts +33 -4
  118. package/src/detectors/c.ts +202 -2
  119. package/src/detectors/csharp.ts +154 -3
  120. package/src/detectors/go.ts +209 -6
  121. package/src/detectors/java.ts +189 -3
  122. package/src/detectors/pem.ts +53 -1
  123. package/src/detectors/python.ts +82 -3
  124. package/src/detectors/ruby.ts +138 -7
  125. package/src/detectors/rust.ts +211 -7
  126. package/src/detectors/source.ts +163 -12
  127. package/src/detectors/stateful-hbs.ts +177 -0
  128. package/src/evidence.ts +124 -0
  129. package/src/index.ts +5 -1
  130. package/src/inventory.ts +31 -1
  131. package/src/parallel.ts +15 -0
  132. package/src/redact.ts +125 -8
  133. package/src/registry.ts +4 -2
  134. package/src/remediate-pipeline.ts +55 -0
  135. package/src/remediation.ts +41 -9
  136. package/src/report.ts +41 -2
  137. package/src/scan.ts +21 -2
  138. package/src/types.ts +8 -1
  139. package/src/version.ts +1 -1
  140. package/src/worktree.ts +9 -1
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Curated database of packages that primarily expose classical asymmetric
3
3
  * cryptography (and are therefore quantum-vulnerable), across multiple
4
- * ecosystems (npm, PyPI, crates.io, Go modules, Maven, RubyGems), plus a
4
+ * ecosystems (npm, PyPI, crates.io, Go modules, Maven, RubyGems, NuGet), plus a
5
5
  * manifest scanner that flags any of them found in the corresponding manifest
6
- * (package.json, requirements.txt, Cargo.toml, go.mod, pom.xml, Gemfile, …).
6
+ * (package.json, requirements.txt, Cargo.toml, go.mod, pom.xml, Gemfile,
7
+ * packages.config / *.csproj, …).
7
8
  *
8
9
  * The list is intentionally focused on libraries whose *purpose* is classical
9
10
  * public-key crypto. General-purpose packages that merely call the language's
@@ -101,6 +102,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
101
102
  reason: "JWTs commonly signed with RS256/ES256 (classical RSA/ECDSA).",
102
103
  algorithms: ["RSA", "ECDSA"],
103
104
  severity: "high",
105
+ hndl: false,
104
106
  },
105
107
  {
106
108
  name: "jose",
@@ -115,6 +117,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
115
117
  reason: "JSON Web Signatures using classical RS/ES algorithms.",
116
118
  algorithms: ["RSA", "ECDSA"],
117
119
  severity: "high",
120
+ hndl: false,
118
121
  },
119
122
  {
120
123
  name: "eccrypto",
@@ -171,6 +174,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
171
174
  reason: "PASETO public tokens signed with classical Ed25519 (v2/v4) or RSA.",
172
175
  algorithms: ["EdDSA", "RSA"],
173
176
  severity: "medium",
177
+ hndl: false,
174
178
  },
175
179
  {
176
180
  name: "bcrypto",
@@ -241,6 +245,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
241
245
  reason: "JSON Web Algorithms: RSA (RS/PS) and EC (ES) signatures.",
242
246
  algorithms: ["RSA", "ECDSA"],
243
247
  severity: "medium",
248
+ hndl: false,
244
249
  },
245
250
  {
246
251
  name: "jwk-to-pem",
@@ -255,6 +260,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
255
260
  reason: "JWT signing/verification with classical RS/PS/ES algorithms.",
256
261
  algorithms: ["RSA", "ECDSA"],
257
262
  severity: "medium",
263
+ hndl: false,
258
264
  },
259
265
  {
260
266
  name: "ssh2",
@@ -283,6 +289,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
283
289
  reason: "HTTP request signing with classical RSA/ECDSA keys.",
284
290
  algorithms: ["RSA", "ECDSA"],
285
291
  severity: "medium",
292
+ hndl: false,
286
293
  },
287
294
  {
288
295
  name: "libsodium-wrappers",
@@ -307,6 +314,28 @@ export const vulnerableDependencies: VulnerableDependency[] = [
307
314
  algorithms: ["RSA", "ECDSA", "DSA"],
308
315
  severity: "high",
309
316
  },
317
+ {
318
+ name: "pycrypto",
319
+ ecosystem: "pypi",
320
+ reason: "Abandoned/unmaintained crypto library — classical RSA/DSA/ElGamal.",
321
+ algorithms: ["RSA", "DSA"],
322
+ severity: "high",
323
+ },
324
+ {
325
+ name: "jwcrypto",
326
+ ecosystem: "pypi",
327
+ reason: "JWK / JWS / JWE with classical RS*/ES* and ECDH-ES key agreement.",
328
+ algorithms: ["RSA", "ECDSA", "ECDH"],
329
+ severity: "medium",
330
+ },
331
+ {
332
+ name: "authlib",
333
+ ecosystem: "pypi",
334
+ reason: "OAuth / OIDC / JOSE stack using classical RS*/ES* JWT signing.",
335
+ algorithms: ["RSA", "ECDSA"],
336
+ severity: "medium",
337
+ hndl: false,
338
+ },
310
339
  {
311
340
  name: "pycryptodomex",
312
341
  ecosystem: "pypi",
@@ -341,6 +370,7 @@ export const vulnerableDependencies: VulnerableDependency[] = [
341
370
  reason: "JWT signing with classical RS*/ES* algorithms.",
342
371
  algorithms: ["RSA", "ECDSA"],
343
372
  severity: "medium",
373
+ hndl: false,
344
374
  },
345
375
  {
346
376
  name: "python-jose",
@@ -415,6 +445,13 @@ export const vulnerableDependencies: VulnerableDependency[] = [
415
445
  algorithms: ["ECDSA", "ECDH"],
416
446
  severity: "high",
417
447
  },
448
+ {
449
+ name: "secp256k1",
450
+ ecosystem: "cargo",
451
+ reason: "libsecp256k1 bindings (blockchain) — classical ECDSA + ECDH.",
452
+ algorithms: ["ECDSA", "ECDH"],
453
+ severity: "high",
454
+ },
418
455
  {
419
456
  name: "ed25519-dalek",
420
457
  ecosystem: "cargo",
@@ -438,6 +475,43 @@ export const vulnerableDependencies: VulnerableDependency[] = [
438
475
  algorithms: ["RSA", "ECDSA", "EdDSA", "ECDH"],
439
476
  severity: "medium",
440
477
  },
478
+ {
479
+ name: "github.com/golang-jwt/jwt/v5",
480
+ ecosystem: "go",
481
+ reason: "golang-jwt — classical RS*/PS*/ES* JWT algorithms.",
482
+ algorithms: ["RSA", "ECDSA"],
483
+ severity: "medium",
484
+ hndl: false,
485
+ },
486
+ {
487
+ name: "github.com/golang-jwt/jwt/v4",
488
+ ecosystem: "go",
489
+ reason: "golang-jwt (v4) — classical RS*/PS*/ES* JWT algorithms.",
490
+ algorithms: ["RSA", "ECDSA"],
491
+ severity: "medium",
492
+ hndl: false,
493
+ },
494
+ {
495
+ name: "github.com/go-jose/go-jose/v4",
496
+ ecosystem: "go",
497
+ reason: "go-jose — classical JOSE (RSA/ECDSA signatures, ECDH-ES key agreement).",
498
+ algorithms: ["RSA", "ECDSA", "ECDH"],
499
+ severity: "medium",
500
+ },
501
+ {
502
+ name: "github.com/cloudflare/circl",
503
+ ecosystem: "go",
504
+ reason: "Cloudflare CIRCL — classical ECDH/EdDSA curves (X25519, X448, Ed25519, P-256).",
505
+ algorithms: ["ECDH", "EdDSA"],
506
+ severity: "medium",
507
+ },
508
+ {
509
+ name: "github.com/decred/dcrd/dcrec/secp256k1/v4",
510
+ ecosystem: "go",
511
+ reason: "decred secp256k1 — classical ECDSA/ECDH on the secp256k1 curve (blockchain keys).",
512
+ algorithms: ["ECDSA", "ECDH"],
513
+ severity: "medium",
514
+ },
441
515
 
442
516
  // --- Maven (Java) ---
443
517
  {
@@ -467,6 +541,22 @@ export const vulnerableDependencies: VulnerableDependency[] = [
467
541
  reason: "Auth0 JWT with classical RS*/ES* algorithms.",
468
542
  algorithms: ["RSA", "ECDSA"],
469
543
  severity: "medium",
544
+ hndl: false,
545
+ },
546
+ {
547
+ name: "nimbus-jose-jwt",
548
+ ecosystem: "maven",
549
+ reason: "Nimbus JOSE+JWT — classical RS*/PS*/ES* JWS and RSA/ECDH-ES JWE.",
550
+ algorithms: ["RSA", "ECDSA", "ECDH"],
551
+ severity: "medium",
552
+ },
553
+ {
554
+ name: "jjwt-api",
555
+ ecosystem: "maven",
556
+ reason: "JJWT (io.jsonwebtoken) — classical RS*/ES* JWT signing.",
557
+ algorithms: ["RSA", "ECDSA"],
558
+ severity: "medium",
559
+ hndl: false,
470
560
  },
471
561
 
472
562
  // --- RubyGems ---
@@ -476,6 +566,15 @@ export const vulnerableDependencies: VulnerableDependency[] = [
476
566
  reason: "Ruby JWT with classical RS*/ES* algorithms.",
477
567
  algorithms: ["RSA", "ECDSA"],
478
568
  severity: "medium",
569
+ hndl: false,
570
+ },
571
+ {
572
+ name: "net-ssh",
573
+ ecosystem: "rubygems",
574
+ reason:
575
+ "Ruby SSH client — classical host/user keys (RSA/ECDSA/Ed25519) and ECDH/DH key exchange.",
576
+ algorithms: ["RSA", "ECDSA", "ECDH"],
577
+ severity: "medium",
479
578
  },
480
579
  {
481
580
  name: "rbnacl",
@@ -491,13 +590,46 @@ export const vulnerableDependencies: VulnerableDependency[] = [
491
590
  algorithms: ["EdDSA"],
492
591
  severity: "low",
493
592
  },
593
+
594
+ // --- NuGet (.NET) ---
595
+ {
596
+ name: "BouncyCastle.Cryptography",
597
+ ecosystem: "nuget",
598
+ reason: "BouncyCastle for .NET — full classical RSA/ECDSA/ECDH/DSA suite.",
599
+ algorithms: ["RSA", "ECDSA", "ECDH", "DSA"],
600
+ severity: "high",
601
+ },
602
+ {
603
+ name: "Portable.BouncyCastle",
604
+ ecosystem: "nuget",
605
+ reason: "Portable BouncyCastle for .NET — classical RSA/ECDSA/ECDH/DSA.",
606
+ algorithms: ["RSA", "ECDSA", "ECDH", "DSA"],
607
+ severity: "high",
608
+ },
609
+ {
610
+ name: "System.IdentityModel.Tokens.Jwt",
611
+ ecosystem: "nuget",
612
+ reason: "Microsoft JWT handler — classical RS*/ES* (RSA/ECDSA) signatures.",
613
+ algorithms: ["RSA", "ECDSA"],
614
+ severity: "medium",
615
+ hndl: false,
616
+ },
617
+ {
618
+ name: "Microsoft.IdentityModel.Tokens",
619
+ ecosystem: "nuget",
620
+ reason: "Microsoft IdentityModel token crypto — classical RSA/ECDSA keys.",
621
+ algorithms: ["RSA", "ECDSA"],
622
+ severity: "medium",
623
+ hndl: false,
624
+ },
494
625
  ];
495
626
 
496
627
  /**
497
628
  * Normalise a package name for matching within its ecosystem. PyPI is
498
629
  * case-insensitive and folds runs of `-_.` to a single `-` (PEP 503); cargo /
499
- * maven / rubygems are effectively lower-case; npm and go module paths are
500
- * matched verbatim (npm scopes and go paths are case-sensitive).
630
+ * maven / rubygems / nuget are effectively lower-case (NuGet ids are matched
631
+ * case-insensitively); npm and go module paths are matched verbatim (npm scopes
632
+ * and go paths are case-sensitive).
501
633
  */
502
634
  function normalizeName(ecosystem: DependencyEcosystem, name: string): string {
503
635
  const n = name.trim();
@@ -583,6 +715,8 @@ export function manifestEcosystem(file: string): DependencyEcosystem | null {
583
715
  if (base === "go.mod") return "go";
584
716
  if (base === "pom.xml" || base === "build.gradle" || base === "build.gradle.kts") return "maven";
585
717
  if (base === "gemfile" || base.endsWith(".gemspec")) return "rubygems";
718
+ if (base === "packages.config" || base === "directory.packages.props" || base.endsWith(".csproj"))
719
+ return "nuget";
586
720
  return null;
587
721
  }
588
722
 
@@ -648,6 +782,20 @@ function candidateNames(ecosystem: DependencyEcosystem, content: string): string
648
782
  }
649
783
  break;
650
784
  }
785
+ case "nuget": {
786
+ // NuGet manifests are XML. `.csproj` / `Directory.Packages.props` declare
787
+ // `<PackageReference Include="Name" … />`; `packages.config` uses
788
+ // `<package id="Name" … />`. Pull the Include= / id= attribute value from
789
+ // each. The `[^>]` guard keeps every match inside a single tag, so the
790
+ // scan is linear (no nested quantifiers → no catastrophic backtracking).
791
+ for (const m of content.matchAll(/<PackageReference\b[^>]*\bInclude\s*=\s*"([^"]+)"/gi)) {
792
+ names.push(m[1]);
793
+ }
794
+ for (const m of content.matchAll(/<package\b[^>]*\bid\s*=\s*"([^"]+)"/gi)) {
795
+ names.push(m[1]);
796
+ }
797
+ break;
798
+ }
651
799
  case "npm":
652
800
  break; // npm uses the JSON path, not this extractor.
653
801
  }
@@ -682,8 +830,10 @@ function dependencyFinding(
682
830
  severity: dep.severity,
683
831
  confidence: "high",
684
832
  algorithm,
685
- // Confidentiality libs are HNDL-exposed; signature-only ones are not.
686
- hndl: dep.algorithms.some(isConfidentialityFamily),
833
+ // Confidentiality libs are HNDL-exposed; signature-only ones are not. An
834
+ // explicit `dep.hndl` wins (some packages list RSA/EC as a family but only
835
+ // ever sign — e.g. JWS/JWT libs — and signatures are not HNDL-exposed).
836
+ hndl: dep.hndl ?? dep.algorithms.some(isConfidentialityFamily),
687
837
  cwe: CWE_BROKEN_CRYPTO,
688
838
  message: `${dep.name} — ${dep.reason}`,
689
839
  remediation: multiFamilyRemediation(dep.algorithms),
@@ -237,14 +237,43 @@ export const RUBY_EXTENSIONS: readonly string[] = [".rb"];
237
237
  /** C / C++ source extensions handled by the OpenSSL detector. */
238
238
  export const C_EXTENSIONS: readonly string[] = [".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hh"];
239
239
 
240
+ /**
241
+ * Prose/documentation extensions. The language-agnostic *token* detectors (SSH
242
+ * public keys, TLS cipher suites, certificate signature algorithms) must not run
243
+ * on these: a changelog or README that mentions `ssh-rsa` or `ECDHE-RSA` in a
244
+ * sentence is prose, not crypto config. (PEM/`-----BEGIN` material stays in scope
245
+ * everywhere, so a key pasted into docs is still caught.)
246
+ */
247
+ export const DOC_EXTENSIONS: readonly string[] = [
248
+ ".md",
249
+ ".markdown",
250
+ ".mdown",
251
+ ".mkd",
252
+ ".rst",
253
+ ".adoc",
254
+ ".asciidoc",
255
+ ".textile",
256
+ ".org",
257
+ ".rdoc",
258
+ ".pod",
259
+ ];
260
+
240
261
  /**
241
262
  * File-literal surfaces where a JWT/JOSE algorithm string (`"RS256"`, `"ES256"`)
242
263
  * 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.
264
+ * from JS-only. Covers the languages whose JWT libraries pass the alg as a quoted
265
+ * token JS/TS, Python, Go (`jwt.GetSigningMethod("RS256")`) and Ruby
266
+ * (`JWT.encode(payload, key, 'RS256')`) — so the regex stays precise (`HS*` HMAC
267
+ * tokens are excluded by `RE_JWT_ALG`). Java/C# pass the alg as an *identifier*
268
+ * (`SignatureAlgorithm.RS256`), which needs its own pattern — a later pass.
269
+ * YAML/JSON config carry unquoted tokens and higher FP risk, so they wait too.
246
270
  */
247
- export const JWT_HOST_EXTENSIONS: readonly string[] = [...JS_TS_EXTENSIONS, ...PYTHON_EXTENSIONS];
271
+ export const JWT_HOST_EXTENSIONS: readonly string[] = [
272
+ ...JS_TS_EXTENSIONS,
273
+ ...PYTHON_EXTENSIONS,
274
+ ...GO_EXTENSIONS,
275
+ ...RUBY_EXTENSIONS,
276
+ ];
248
277
 
249
278
  /**
250
279
  * Extensions the scanner can actually analyze for inline crypto usage today
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import type { Detector, Finding, RuleMeta } from "../types.js";
18
18
  import { C_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
19
- import { CWE_BROKEN_CRYPTO } from "../cwe.js";
19
+ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
20
20
 
21
21
  const RE_C_RSA = /\bRSA_generate_key(?:_ex)?\s*\(|\bEVP_RSA_gen\s*\(/g;
22
22
  const RE_C_EC = /\bEC_KEY_generate_key\s*\(|\bEC_KEY_new_by_curve_name\s*\(/g;
@@ -24,6 +24,30 @@ const RE_C_ECDSA = /\bECDSA_do_sign\s*\(|\bECDSA_sign\s*\(/g;
24
24
  const RE_C_ECDH = /\bECDH_compute_key\s*\(/g;
25
25
  const RE_C_DSA = /\bDSA_generate_key\s*\(|\bDSA_generate_parameters(?:_ex)?\s*\(/g;
26
26
  const RE_C_DH = /\bDH_generate_key\s*\(|\bDH_generate_parameters(?:_ex)?\s*\(/g;
27
+ // Modern OpenSSL 3.x EVP API (the legacy *_generate_key forms above are
28
+ // deprecated) + libsodium — the biggest C false-negative surface (audit F1).
29
+ const RE_C_EVP_KEYGEN = /\bEVP_PKEY_(?:Q_)?keygen\s*\(|\bEVP_PKEY_paramgen\s*\(/g;
30
+ const RE_C_EVP_DERIVE = /\bEVP_PKEY_derive\s*\(/g;
31
+ const RE_C_EVP_CRYPT = /\bEVP_PKEY_(?:encrypt|decrypt)\s*\(/g;
32
+ const RE_C_EVP_SIGN = /\bEVP_DigestSign(?:Init)?\s*\(|\bEVP_DigestVerify(?:Init)?\s*\(/g;
33
+ // X25519: crypto_box / crypto_kx key pairs (incl. the fully-qualified
34
+ // curve25519xsalsa20poly1305 form) plus the low-level crypto_scalarmult
35
+ // primitive — all classical Curve25519 key agreement (audit recall gap).
36
+ const RE_C_SODIUM_BOX =
37
+ /\bcrypto_box_(?:curve25519xsalsa20poly1305_)?(?:seed_)?keypair\s*\(|\bcrypto_kx_keypair\s*\(|\bcrypto_scalarmult_(?:curve25519|base)\s*\(/g;
38
+ // EdDSA: crypto_sign key pairs, including the explicit _ed25519 forms and the
39
+ // deterministic seed_keypair variants (audit recall gap: _ed25519_keypair).
40
+ const RE_C_SODIUM_SIGN = /\bcrypto_sign_(?:ed25519_)?(?:seed_)?keypair\s*\(/g;
41
+ // Legacy verify / decrypt counterparts to the *_sign / *_encrypt rules above
42
+ // (audit F4-c): the classic OpenSSL RSA/ECDSA verification and RSA raw
43
+ // decryption call forms that the modern EVP + legacy keygen rules don't cover.
44
+ const RE_C_ECDSA_VERIFY = /\bECDSA_verify\s*\(/g;
45
+ const RE_C_RSA_VERIFY = /\bRSA_verify\s*\(/g;
46
+ const RE_C_RSA_CRYPT = /\bRSA_public_encrypt\s*\(|\bRSA_private_decrypt\s*\(/g;
47
+ // C/OpenSSL legacy TLS configuration (mirrors source.ts tlsDetector): forcing a
48
+ // deprecated protocol version or disabling certificate verification.
49
+ const RE_C_TLS_VERSION = /\bTLSv1_method\b|\bSSLv3_method\b/g;
50
+ const RE_C_TLS_VERIFY_NONE = /\bSSL_VERIFY_NONE\b/g;
27
51
 
28
52
  const RULE_C_RSA: RuleMeta = {
29
53
  id: "c-rsa-keygen",
@@ -104,6 +128,153 @@ const RULE_C_DH: RuleMeta = {
104
128
  message:
105
129
  "Finite-field Diffie-Hellman (C/OpenSSL) is broken by Shor's algorithm (harvest-now-decrypt-later).",
106
130
  };
131
+ const RULE_C_EVP_KEYGEN: RuleMeta = {
132
+ id: "c-evp-keygen",
133
+ title: "C/OpenSSL EVP key generation",
134
+ description: "OpenSSL 3.x EVP_PKEY_keygen / EVP_PKEY_Q_keygen / paramgen",
135
+ category: "key-exchange",
136
+ severity: "high",
137
+ confidence: "high",
138
+ algorithm: "unknown",
139
+ hndl: true,
140
+ cwe: CWE_BROKEN_CRYPTO,
141
+ message:
142
+ "Generates an asymmetric key via the OpenSSL 3.x EVP API (the key type — RSA/EC/DH/X25519 — is set on the CTX). Treated conservatively as key-exchange-capable (harvest-now-decrypt-later).",
143
+ remediation:
144
+ "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
145
+ };
146
+ const RULE_C_EVP_DERIVE: RuleMeta = {
147
+ id: "c-evp-derive",
148
+ title: "C/OpenSSL EVP key agreement",
149
+ description: "OpenSSL 3.x EVP_PKEY_derive (ECDH / DH shared secret)",
150
+ category: "key-exchange",
151
+ severity: "high",
152
+ confidence: "high",
153
+ algorithm: "ECDH",
154
+ hndl: true,
155
+ cwe: CWE_BROKEN_CRYPTO,
156
+ message:
157
+ "Derives an (EC)DH shared secret via the OpenSSL EVP API — broken by Shor's algorithm (harvest-now-decrypt-later).",
158
+ };
159
+ const RULE_C_EVP_CRYPT: RuleMeta = {
160
+ id: "c-evp-pkey-crypt",
161
+ title: "C/OpenSSL EVP public-key encryption",
162
+ description: "OpenSSL 3.x EVP_PKEY_encrypt / EVP_PKEY_decrypt (RSA)",
163
+ category: "kem",
164
+ severity: "high",
165
+ confidence: "high",
166
+ algorithm: "RSA",
167
+ hndl: true,
168
+ cwe: CWE_BROKEN_CRYPTO,
169
+ message:
170
+ "RSA public-key encryption/decryption via the OpenSSL EVP API is harvest-now-decrypt-later exposed.",
171
+ };
172
+ const RULE_C_EVP_SIGN: RuleMeta = {
173
+ id: "c-evp-sign",
174
+ title: "C/OpenSSL EVP signing",
175
+ description: "OpenSSL 3.x EVP_DigestSign* / EVP_DigestVerify*",
176
+ category: "signature",
177
+ severity: "high",
178
+ confidence: "high",
179
+ algorithm: "unknown",
180
+ hndl: false,
181
+ cwe: CWE_BROKEN_CRYPTO,
182
+ message:
183
+ "Classical signature via the OpenSSL EVP API (RSA/ECDSA/EdDSA) is forgeable by a quantum attacker.",
184
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
185
+ };
186
+ const RULE_C_SODIUM_BOX: RuleMeta = {
187
+ id: "c-libsodium-box",
188
+ title: "libsodium X25519 key pair",
189
+ description:
190
+ "libsodium crypto_box / crypto_kx keypair + crypto_scalarmult (X25519 key agreement)",
191
+ category: "key-exchange",
192
+ severity: "medium",
193
+ confidence: "high",
194
+ algorithm: "X25519",
195
+ hndl: true,
196
+ cwe: CWE_BROKEN_CRYPTO,
197
+ message:
198
+ "libsodium crypto_box uses X25519 key agreement — modern but classical, and harvest-now-decrypt-later exposed.",
199
+ };
200
+ const RULE_C_SODIUM_SIGN: RuleMeta = {
201
+ id: "c-libsodium-sign",
202
+ title: "libsodium Ed25519 key pair",
203
+ description: "libsodium crypto_sign(_ed25519)(_seed)_keypair (Ed25519 signatures)",
204
+ category: "signature",
205
+ severity: "low",
206
+ confidence: "high",
207
+ algorithm: "EdDSA",
208
+ hndl: false,
209
+ cwe: CWE_BROKEN_CRYPTO,
210
+ message:
211
+ "libsodium crypto_sign uses Ed25519 signatures — classical and forgeable by a quantum attacker.",
212
+ };
213
+ const RULE_C_ECDSA_VERIFY: RuleMeta = {
214
+ id: "c-ecdsa-verify",
215
+ title: "C/OpenSSL ECDSA signature verification",
216
+ description: "OpenSSL ECDSA_verify",
217
+ category: "signature",
218
+ severity: "high",
219
+ confidence: "high",
220
+ algorithm: "ECDSA",
221
+ hndl: false,
222
+ cwe: CWE_BROKEN_CRYPTO,
223
+ message:
224
+ "Classical ECDSA verification (C/OpenSSL) trusts signatures forgeable by a quantum attacker.",
225
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
226
+ };
227
+ const RULE_C_RSA_VERIFY: RuleMeta = {
228
+ id: "c-rsa-verify",
229
+ title: "C/OpenSSL RSA signature verification",
230
+ description: "OpenSSL RSA_verify",
231
+ category: "signature",
232
+ severity: "high",
233
+ confidence: "high",
234
+ algorithm: "RSA",
235
+ hndl: false,
236
+ cwe: CWE_BROKEN_CRYPTO,
237
+ message:
238
+ "Classical RSA signature verification (C/OpenSSL) trusts signatures forgeable by a quantum attacker.",
239
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
240
+ };
241
+ const RULE_C_RSA_CRYPT: RuleMeta = {
242
+ id: "c-rsa-crypt",
243
+ title: "C/OpenSSL RSA public-key encryption",
244
+ description: "OpenSSL RSA_public_encrypt / RSA_private_decrypt",
245
+ category: "kem",
246
+ severity: "high",
247
+ confidence: "high",
248
+ algorithm: "RSA",
249
+ hndl: true,
250
+ cwe: CWE_BROKEN_CRYPTO,
251
+ message:
252
+ "Legacy RSA public-key encryption/decryption (C/OpenSSL) is harvest-now-decrypt-later exposed.",
253
+ };
254
+ const RULE_C_TLS_VERSION: RuleMeta = {
255
+ id: "c-tls-legacy-version",
256
+ title: "Legacy TLS/SSL version pinned (C/OpenSSL)",
257
+ description: "OpenSSL TLSv1_method / SSLv3_method",
258
+ category: "tls",
259
+ severity: "medium",
260
+ confidence: "high",
261
+ hndl: false,
262
+ cwe: CWE_WEAK_STRENGTH,
263
+ message: "TLS 1.0 / SSLv3 are deprecated and insecure; require TLS 1.3.",
264
+ remediation: "Use TLS_method() with a minimum of TLS 1.3 and prefer PQC-hybrid key exchange.",
265
+ };
266
+ const RULE_C_TLS_VERIFY_NONE: RuleMeta = {
267
+ id: "c-tls-verify-none",
268
+ title: "TLS certificate verification disabled (C/OpenSSL)",
269
+ description: "OpenSSL SSL_VERIFY_NONE",
270
+ category: "tls",
271
+ severity: "high",
272
+ confidence: "high",
273
+ hndl: false,
274
+ cwe: CWE_CERT_VALIDATION,
275
+ message: "SSL_VERIFY_NONE disables TLS certificate verification (MITM risk).",
276
+ remediation: "Use SSL_VERIFY_PEER and verify certificates properly.",
277
+ };
107
278
 
108
279
  /** Detects classical asymmetric crypto in C/C++ (OpenSSL). */
109
280
  export const cDetector: Detector = {
@@ -111,7 +282,25 @@ export const cDetector: Detector = {
111
282
  description: "Classical asymmetric crypto in C/C++ (OpenSSL)",
112
283
  scope: "source",
113
284
  language: "c",
114
- rules: [RULE_C_RSA, RULE_C_EC, RULE_C_ECDSA, RULE_C_ECDH, RULE_C_DSA, RULE_C_DH],
285
+ rules: [
286
+ RULE_C_RSA,
287
+ RULE_C_EC,
288
+ RULE_C_ECDSA,
289
+ RULE_C_ECDH,
290
+ RULE_C_DSA,
291
+ RULE_C_DH,
292
+ RULE_C_EVP_KEYGEN,
293
+ RULE_C_EVP_DERIVE,
294
+ RULE_C_EVP_CRYPT,
295
+ RULE_C_EVP_SIGN,
296
+ RULE_C_SODIUM_BOX,
297
+ RULE_C_SODIUM_SIGN,
298
+ RULE_C_ECDSA_VERIFY,
299
+ RULE_C_RSA_VERIFY,
300
+ RULE_C_RSA_CRYPT,
301
+ RULE_C_TLS_VERSION,
302
+ RULE_C_TLS_VERIFY_NONE,
303
+ ],
115
304
  appliesTo: (f) => hasExtension(f, C_EXTENSIONS),
116
305
  detect({ file, content }): Finding[] {
117
306
  const findings: Finding[] = [];
@@ -127,6 +316,17 @@ export const cDetector: Detector = {
127
316
  add(RE_C_ECDH, RULE_C_ECDH);
128
317
  add(RE_C_DSA, RULE_C_DSA);
129
318
  add(RE_C_DH, RULE_C_DH);
319
+ add(RE_C_EVP_KEYGEN, RULE_C_EVP_KEYGEN);
320
+ add(RE_C_EVP_DERIVE, RULE_C_EVP_DERIVE);
321
+ add(RE_C_EVP_CRYPT, RULE_C_EVP_CRYPT);
322
+ add(RE_C_EVP_SIGN, RULE_C_EVP_SIGN);
323
+ add(RE_C_SODIUM_BOX, RULE_C_SODIUM_BOX);
324
+ add(RE_C_SODIUM_SIGN, RULE_C_SODIUM_SIGN);
325
+ add(RE_C_ECDSA_VERIFY, RULE_C_ECDSA_VERIFY);
326
+ add(RE_C_RSA_VERIFY, RULE_C_RSA_VERIFY);
327
+ add(RE_C_RSA_CRYPT, RULE_C_RSA_CRYPT);
328
+ add(RE_C_TLS_VERSION, RULE_C_TLS_VERSION);
329
+ add(RE_C_TLS_VERIFY_NONE, RULE_C_TLS_VERIFY_NONE);
130
330
  return findings;
131
331
  },
132
332
  };