@quantakrypto/core 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +38 -10
  3. package/dist/agent-types.js.map +1 -1
  4. package/dist/baseline.js.map +1 -1
  5. package/dist/cache.js.map +1 -1
  6. package/dist/cbom-merge.d.ts +9 -0
  7. package/dist/cbom-merge.d.ts.map +1 -0
  8. package/dist/cbom-merge.js +99 -0
  9. package/dist/cbom-merge.js.map +1 -0
  10. package/dist/cbom.js.map +1 -1
  11. package/dist/changed.js.map +1 -1
  12. package/dist/codemods/config-toggle.js.map +1 -1
  13. package/dist/codemods/registry.js.map +1 -1
  14. package/dist/comments.d.ts +29 -2
  15. package/dist/comments.d.ts.map +1 -1
  16. package/dist/comments.js +178 -18
  17. package/dist/comments.js.map +1 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/cwe.js.map +1 -1
  20. package/dist/dependencies.d.ts +3 -2
  21. package/dist/dependencies.d.ts.map +1 -1
  22. package/dist/dependencies.js +138 -2
  23. package/dist/dependencies.js.map +1 -1
  24. package/dist/detect-utils.d.ts +19 -4
  25. package/dist/detect-utils.d.ts.map +1 -1
  26. package/dist/detect-utils.js +43 -6
  27. package/dist/detect-utils.js.map +1 -1
  28. package/dist/detectors/c.d.ts +4 -2
  29. package/dist/detectors/c.d.ts.map +1 -1
  30. package/dist/detectors/c.js +382 -3
  31. package/dist/detectors/c.js.map +1 -1
  32. package/dist/detectors/cicd.d.ts +27 -0
  33. package/dist/detectors/cicd.d.ts.map +1 -0
  34. package/dist/detectors/cicd.js +116 -0
  35. package/dist/detectors/cicd.js.map +1 -0
  36. package/dist/detectors/cloud-kms.d.ts +21 -0
  37. package/dist/detectors/cloud-kms.d.ts.map +1 -0
  38. package/dist/detectors/cloud-kms.js +57 -0
  39. package/dist/detectors/cloud-kms.js.map +1 -0
  40. package/dist/detectors/cloudformation.d.ts +30 -0
  41. package/dist/detectors/cloudformation.d.ts.map +1 -0
  42. package/dist/detectors/cloudformation.js +170 -0
  43. package/dist/detectors/cloudformation.js.map +1 -0
  44. package/dist/detectors/csharp.d.ts.map +1 -1
  45. package/dist/detectors/csharp.js +141 -3
  46. package/dist/detectors/csharp.js.map +1 -1
  47. package/dist/detectors/database.d.ts +17 -0
  48. package/dist/detectors/database.d.ts.map +1 -0
  49. package/dist/detectors/database.js +60 -0
  50. package/dist/detectors/database.js.map +1 -0
  51. package/dist/detectors/dnssec.d.ts +55 -0
  52. package/dist/detectors/dnssec.d.ts.map +1 -0
  53. package/dist/detectors/dnssec.js +107 -0
  54. package/dist/detectors/dnssec.js.map +1 -0
  55. package/dist/detectors/elixir.d.ts +20 -0
  56. package/dist/detectors/elixir.d.ts.map +1 -0
  57. package/dist/detectors/elixir.js +195 -0
  58. package/dist/detectors/elixir.js.map +1 -0
  59. package/dist/detectors/go.d.ts +5 -3
  60. package/dist/detectors/go.d.ts.map +1 -1
  61. package/dist/detectors/go.js +187 -3
  62. package/dist/detectors/go.js.map +1 -1
  63. package/dist/detectors/java.d.ts.map +1 -1
  64. package/dist/detectors/java.js +169 -4
  65. package/dist/detectors/java.js.map +1 -1
  66. package/dist/detectors/jose.d.ts +20 -0
  67. package/dist/detectors/jose.d.ts.map +1 -0
  68. package/dist/detectors/jose.js +62 -0
  69. package/dist/detectors/jose.js.map +1 -0
  70. package/dist/detectors/jwk.d.ts +25 -0
  71. package/dist/detectors/jwk.d.ts.map +1 -0
  72. package/dist/detectors/jwk.js +95 -0
  73. package/dist/detectors/jwk.js.map +1 -0
  74. package/dist/detectors/k8s.d.ts +17 -0
  75. package/dist/detectors/k8s.d.ts.map +1 -0
  76. package/dist/detectors/k8s.js +85 -0
  77. package/dist/detectors/k8s.js.map +1 -0
  78. package/dist/detectors/mesh.d.ts +35 -0
  79. package/dist/detectors/mesh.d.ts.map +1 -0
  80. package/dist/detectors/mesh.js +109 -0
  81. package/dist/detectors/mesh.js.map +1 -0
  82. package/dist/detectors/messaging.d.ts +16 -0
  83. package/dist/detectors/messaging.d.ts.map +1 -0
  84. package/dist/detectors/messaging.js +72 -0
  85. package/dist/detectors/messaging.js.map +1 -0
  86. package/dist/detectors/pem.d.ts.map +1 -1
  87. package/dist/detectors/pem.js +50 -2
  88. package/dist/detectors/pem.js.map +1 -1
  89. package/dist/detectors/php.d.ts +24 -0
  90. package/dist/detectors/php.d.ts.map +1 -0
  91. package/dist/detectors/php.js +167 -0
  92. package/dist/detectors/php.js.map +1 -0
  93. package/dist/detectors/python.d.ts.map +1 -1
  94. package/dist/detectors/python.js +144 -3
  95. package/dist/detectors/python.js.map +1 -1
  96. package/dist/detectors/ruby.d.ts +9 -5
  97. package/dist/detectors/ruby.d.ts.map +1 -1
  98. package/dist/detectors/ruby.js +122 -2
  99. package/dist/detectors/ruby.js.map +1 -1
  100. package/dist/detectors/rust.d.ts +8 -3
  101. package/dist/detectors/rust.d.ts.map +1 -1
  102. package/dist/detectors/rust.js +253 -3
  103. package/dist/detectors/rust.js.map +1 -1
  104. package/dist/detectors/secrets.d.ts +21 -0
  105. package/dist/detectors/secrets.d.ts.map +1 -0
  106. package/dist/detectors/secrets.js +79 -0
  107. package/dist/detectors/secrets.js.map +1 -0
  108. package/dist/detectors/source.d.ts.map +1 -1
  109. package/dist/detectors/source.js +305 -52
  110. package/dist/detectors/source.js.map +1 -1
  111. package/dist/detectors/stateful-hbs.d.ts +22 -0
  112. package/dist/detectors/stateful-hbs.d.ts.map +1 -0
  113. package/dist/detectors/stateful-hbs.js +142 -0
  114. package/dist/detectors/stateful-hbs.js.map +1 -0
  115. package/dist/detectors/terraform.d.ts +21 -0
  116. package/dist/detectors/terraform.d.ts.map +1 -0
  117. package/dist/detectors/terraform.js +123 -0
  118. package/dist/detectors/terraform.js.map +1 -0
  119. package/dist/detectors/vpn.d.ts +22 -0
  120. package/dist/detectors/vpn.d.ts.map +1 -0
  121. package/dist/detectors/vpn.js +99 -0
  122. package/dist/detectors/vpn.js.map +1 -0
  123. package/dist/errors.js.map +1 -1
  124. package/dist/evidence.d.ts +53 -0
  125. package/dist/evidence.d.ts.map +1 -0
  126. package/dist/evidence.js +81 -0
  127. package/dist/evidence.js.map +1 -0
  128. package/dist/index.d.ts +9 -2
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +10 -2
  131. package/dist/index.js.map +1 -1
  132. package/dist/inventory.d.ts +2 -0
  133. package/dist/inventory.d.ts.map +1 -1
  134. package/dist/inventory.js +27 -1
  135. package/dist/inventory.js.map +1 -1
  136. package/dist/parallel.js.map +1 -1
  137. package/dist/patch-policy.js.map +1 -1
  138. package/dist/policy.d.ts +67 -0
  139. package/dist/policy.d.ts.map +1 -0
  140. package/dist/policy.js +115 -0
  141. package/dist/policy.js.map +1 -0
  142. package/dist/redact.d.ts.map +1 -1
  143. package/dist/redact.js +39 -0
  144. package/dist/redact.js.map +1 -1
  145. package/dist/registry.d.ts +2 -2
  146. package/dist/registry.d.ts.map +1 -1
  147. package/dist/registry.js +34 -2
  148. package/dist/registry.js.map +1 -1
  149. package/dist/remediate-pipeline.d.ts +3 -0
  150. package/dist/remediate-pipeline.d.ts.map +1 -1
  151. package/dist/remediate-pipeline.js +71 -0
  152. package/dist/remediate-pipeline.js.map +1 -1
  153. package/dist/remediate-request.js.map +1 -1
  154. package/dist/remediation.d.ts +9 -3
  155. package/dist/remediation.d.ts.map +1 -1
  156. package/dist/remediation.js +17 -5
  157. package/dist/remediation.js.map +1 -1
  158. package/dist/report.d.ts +9 -0
  159. package/dist/report.d.ts.map +1 -1
  160. package/dist/report.js +38 -0
  161. package/dist/report.js.map +1 -1
  162. package/dist/scan-worker.js.map +1 -1
  163. package/dist/scan.d.ts.map +1 -1
  164. package/dist/scan.js +8 -1
  165. package/dist/scan.js.map +1 -1
  166. package/dist/severity.js.map +1 -1
  167. package/dist/standards.d.ts +96 -0
  168. package/dist/standards.d.ts.map +1 -0
  169. package/dist/standards.js +107 -0
  170. package/dist/standards.js.map +1 -0
  171. package/dist/triage.js.map +1 -1
  172. package/dist/types.d.ts +2 -2
  173. package/dist/types.d.ts.map +1 -1
  174. package/dist/types.js.map +1 -1
  175. package/dist/verify.js.map +1 -1
  176. package/dist/version.d.ts +1 -1
  177. package/dist/version.js +1 -1
  178. package/dist/version.js.map +1 -1
  179. package/dist/walk.js.map +1 -1
  180. package/dist/worktree.js.map +1 -1
  181. package/package.json +1 -1
  182. package/src/cbom-merge.ts +116 -0
  183. package/src/comments.ts +183 -18
  184. package/src/dependencies.ts +144 -4
  185. package/src/detect-utils.ts +47 -6
  186. package/src/detectors/c.ts +411 -5
  187. package/src/detectors/cicd.ts +158 -0
  188. package/src/detectors/cloud-kms.ts +87 -0
  189. package/src/detectors/cloudformation.ts +215 -0
  190. package/src/detectors/csharp.ts +154 -3
  191. package/src/detectors/database.ts +88 -0
  192. package/src/detectors/dnssec.ts +186 -0
  193. package/src/detectors/elixir.ts +237 -0
  194. package/src/detectors/go.ts +209 -6
  195. package/src/detectors/java.ts +191 -4
  196. package/src/detectors/jose.ts +94 -0
  197. package/src/detectors/jwk.ts +128 -0
  198. package/src/detectors/k8s.ts +112 -0
  199. package/src/detectors/mesh.ts +154 -0
  200. package/src/detectors/messaging.ts +98 -0
  201. package/src/detectors/pem.ts +54 -2
  202. package/src/detectors/php.ts +225 -0
  203. package/src/detectors/python.ts +153 -3
  204. package/src/detectors/ruby.ts +138 -7
  205. package/src/detectors/rust.ts +282 -7
  206. package/src/detectors/secrets.ts +114 -0
  207. package/src/detectors/source.ts +382 -72
  208. package/src/detectors/stateful-hbs.ts +180 -0
  209. package/src/detectors/terraform.ts +154 -0
  210. package/src/detectors/vpn.ts +131 -0
  211. package/src/evidence.ts +135 -0
  212. package/src/index.ts +15 -1
  213. package/src/inventory.ts +31 -1
  214. package/src/policy.ts +180 -0
  215. package/src/redact.ts +41 -0
  216. package/src/registry.ts +34 -2
  217. package/src/remediate-pipeline.ts +78 -0
  218. package/src/remediation.ts +23 -8
  219. package/src/report.ts +48 -2
  220. package/src/scan.ts +13 -1
  221. package/src/standards.ts +186 -0
  222. package/src/types.ts +3 -1
  223. package/src/version.ts +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standards.js","sourceRoot":"","sources":["../src/standards.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2DH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,YAAY,EAAE,YAAY;IAC1B,UAAU,EAAE,YAAY;IACxB,oBAAoB,EAAE,CAAC;IAEvB,IAAI,EAAE;QACJ,KAAK,EAAE;YACL,OAAO,EAAE,2DAA2D;YACpE,MAAM,EAAE,eAAe;YACvB,IAAI,EAAE,SAAS;SAChB;QACD,KAAK,EAAE;YACL,OAAO,EAAE,gEAAgE;YACzE,MAAM,EAAE,eAAe;YACvB,IAAI,EAAE,SAAS;SAChB;QACD,MAAM,EAAE;YACN,OAAO,EAAE,6EAA6E;YACtF,MAAM,EAAE,eAAe;YACvB,IAAI,EAAE,SAAS;SAChB;KACF;IAED,IAAI,EAAE;QACJ,SAAS,EAAE,EAAE,GAAG,EAAE,uBAAuB,EAAE,SAAS,EAAE,sBAAsB,EAAE;QAC9E,SAAS,EAAE,EAAE,GAAG,EAAE,wBAAwB,EAAE,SAAS,EAAE,sBAAsB,EAAE;QAC/E,MAAM,EAAE,0EAA0E;QAClF,IAAI,EAAE,SAAS;KAChB;IAED,WAAW,EAAE;QACX,OAAO,EACL,iFAAiF;YACjF,mFAAmF;QACrF,MAAM,EAAE,iBAAiB;QACzB,IAAI,EAAE,SAAS;KAChB;IAED,kBAAkB,EAAE;QAClB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,kEAAkE;QAC1E,IAAI,EAAE,SAAS;KAChB;IAED,QAAQ,EAAE;QACR;YACE,OAAO,EACL,yFAAyF;gBACzF,sDAAsD;YACxD,MAAM,EAAE,0BAA0B;YAClC,IAAI,EAAE,SAAS;SAChB;QACD;YACE,OAAO,EAAE,+CAA+C;YACxD,MAAM,EAAE,qBAAqB;YAC7B,IAAI,EAAE,SAAS;SAChB;QACD;YACE,OAAO,EAAE,oEAAoE;YAC7E,MAAM,EAAE,oCAAoC;YAC5C,IAAI,EAAE,SAAS;SAChB;KACF;IAED,OAAO,EAAE;QACP;YACE,OAAO,EAAE,iEAAiE;YAC1E,MAAM,EAAE,iCAAiC;YACzC,IAAI,EAAE,SAAS;SAChB;QACD;YACE,OAAO,EAAE,uEAAuE;YAChF,MAAM,EAAE,iCAAiC;YACzC,IAAI,EAAE,SAAS;SAChB;KACF;CACF,CAAC;AAYF;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAS,EACT,YAA0B,aAAa;IAEvC,MAAM,UAAU,GAAG,UAAU,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,UAAU,YAAY,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,UAAU,CAAC,CAAC;IAC1D,OAAO,EAAE,GAAG,EAAE,SAAS,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE,CAAC;AAC9E,CAAC","sourcesContent":["/**\n * Single source of truth for the post-quantum standards this tool depends on.\n *\n * The scanner's credibility rests on its recommendations tracking the current\n * NIST / CNSA / IETF state. That tracking used to be ad-hoc — facts were spread\n * across `remediation.ts` with no dates, no citations, and nothing to catch code\n * drifting from the published standards. This module makes the standards facts\n * explicit, dated, and cited, and the companion drift test\n * (`test/standards.test.ts`) fails the build if the runtime constants\n * (`TIER_PARAMS`, `PQC_TRANSITION_NOTE`, `STATEFUL_HBS_NOTE`) fall out of sync\n * with what is recorded here.\n *\n * ## Cadence\n *\n * Re-verify every quarter (see `docs/standards/pqc-standards.md` for the runbook).\n * On each review: check the sources below for changes, update the facts + their\n * `asOf`, and roll `lastReviewed` / `nextReview` forward. `scripts/standards-check.mjs`\n * (advisory, runs in CI) flags when `nextReview` has passed so a review can't be\n * silently skipped. `standardsReviewStatus(now)` is the pure predicate behind it.\n */\n\n/** A single standards fact with its citation and when it was last verified. */\nexport interface StandardsCitation {\n /** One-line statement of the fact. */\n readonly summary: string;\n /** Spec identifier / publication (and URL where stable). */\n readonly source: string;\n /** `YYYY-MM` — when this fact was last verified against its source. */\n readonly asOf: string;\n}\n\n/** The full post-quantum standards snapshot the tool tracks. */\nexport interface PqcStandards {\n /** `YYYY-MM-DD` — when the whole snapshot was last reviewed. */\n readonly lastReviewed: string;\n /** `YYYY-MM-DD` — when the next review is due. */\n readonly nextReview: string;\n /** Cadence, in months, between reviews. */\n readonly reviewIntervalMonths: number;\n\n /** NIST's finalized PQC FIPS (the recommendation targets). */\n readonly fips: {\n readonly mlKem: StandardsCitation; // FIPS 203\n readonly mlDsa: StandardsCitation; // FIPS 204\n readonly slhDsa: StandardsCitation; // FIPS 205\n };\n\n /**\n * CNSA 2.0 security tiers → the KEM / signature parameter sets. These MUST\n * mirror `remediation.TIER_PARAMS`; the drift test asserts they stay identical.\n */\n readonly cnsa: {\n readonly category3: { readonly kem: string; readonly signature: string };\n readonly category5: { readonly kem: string; readonly signature: string };\n readonly source: string;\n readonly asOf: string;\n };\n\n /** Stateful hash-based signatures (firmware / boot signing). */\n readonly statefulHbs: StandardsCitation; // SP 800-208\n\n /** The migration deadline the transition note surfaces. */\n readonly transitionTimeline: {\n /** Year after which classical public-key crypto is deprecated. */\n readonly deprecateAfter: number;\n /** Year after which it is disallowed. */\n readonly disallowAfter: number;\n readonly source: string;\n readonly asOf: string;\n };\n\n /** Emerging / backup standards worth tracking beyond the current FIPS. */\n readonly emerging: readonly StandardsCitation[];\n\n /** Recommended hybrid key-exchange groups. */\n readonly hybrids: readonly StandardsCitation[];\n}\n\n/**\n * The current snapshot. Update on each quarterly review; the drift test keeps the\n * runtime remediation constants aligned with it.\n */\nexport const PQC_STANDARDS: PqcStandards = {\n lastReviewed: \"2026-07-19\",\n nextReview: \"2026-10-19\",\n reviewIntervalMonths: 3,\n\n fips: {\n mlKem: {\n summary: \"ML-KEM (Kyber) key encapsulation — finalized August 2024.\",\n source: \"NIST FIPS 203\",\n asOf: \"2026-07\",\n },\n mlDsa: {\n summary: \"ML-DSA (Dilithium) lattice signatures — finalized August 2024.\",\n source: \"NIST FIPS 204\",\n asOf: \"2026-07\",\n },\n slhDsa: {\n summary: \"SLH-DSA (SPHINCS+) stateless hash-based signatures — finalized August 2024.\",\n source: \"NIST FIPS 205\",\n asOf: \"2026-07\",\n },\n },\n\n cnsa: {\n category3: { kem: \"ML-KEM-768 (FIPS 203)\", signature: \"ML-DSA-65 (FIPS 204)\" },\n category5: { kem: \"ML-KEM-1024 (FIPS 203)\", signature: \"ML-DSA-87 (FIPS 204)\" },\n source: \"NSA CNSA 2.0 (national-security systems; 2030/2033 migration milestones)\",\n asOf: \"2026-07\",\n },\n\n statefulHbs: {\n summary:\n \"LMS/HSS and XMSS/XMSSMT stateful hash-based signatures (incl. the SHAKE256 and \" +\n \"192-bit parameter sets) are approved for firmware/boot signing, but are STATEFUL.\",\n source: \"NIST SP 800-208\",\n asOf: \"2026-07\",\n },\n\n transitionTimeline: {\n deprecateAfter: 2030,\n disallowAfter: 2035,\n source: \"NIST IR 8547 (transition to post-quantum cryptography standards)\",\n asOf: \"2026-07\",\n },\n\n emerging: [\n {\n summary:\n \"HQC — NIST's code-based backup KEM (selected March 2025; draft FIPS expected ~2026), a \" +\n \"diversity hedge against ML-KEM's lattice assumption.\",\n source: \"NIST PQC (HQC selection)\",\n asOf: \"2026-07\",\n },\n {\n summary: \"FN-DSA / Falcon — compact lattice signatures.\",\n source: \"NIST draft FIPS 206\",\n asOf: \"2026-07\",\n },\n {\n summary: \"X-Wing — X25519 + ML-KEM-768 hybrid KEM for HPKE-style encryption.\",\n source: \"IETF draft-connolly-cfrg-xwing-kem\",\n asOf: \"2026-07\",\n },\n ],\n\n hybrids: [\n {\n summary: \"X25519MLKEM768 — the default TLS 1.3 hybrid key-exchange group.\",\n source: \"IETF draft-ietf-tls-ecdhe-mlkem\",\n asOf: \"2026-07\",\n },\n {\n summary: \"SecP384r1MLKEM1024 — the Category-5 / CNSA hybrid key-exchange group.\",\n source: \"IETF draft-ietf-tls-ecdhe-mlkem\",\n asOf: \"2026-07\",\n },\n ],\n};\n\n/** Result of {@link standardsReviewStatus}. */\nexport interface StandardsReviewStatus {\n /** True when `now` is on or after `nextReview` — a review is due. */\n readonly due: boolean;\n /** The `nextReview` date being compared against (`YYYY-MM-DD`). */\n readonly nextReview: string;\n /** Whole days from `now` until `nextReview` (negative when overdue). */\n readonly daysUntil: number;\n}\n\n/**\n * Whether a standards review is due as of `now`. Pure (takes `now` explicitly) so\n * it is deterministic in tests; the CI script passes the real clock. Compares on\n * whole UTC days so a same-day run is not spuriously \"overdue\".\n */\nexport function standardsReviewStatus(\n now: Date,\n standards: PqcStandards = PQC_STANDARDS,\n): StandardsReviewStatus {\n const MS_PER_DAY = 86_400_000;\n const next = Date.parse(`${standards.nextReview}T00:00:00Z`);\n const today = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());\n const daysUntil = Math.round((next - today) / MS_PER_DAY);\n return { due: daysUntil <= 0, nextReview: standards.nextReview, daysUntil };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"triage.js","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GACxB,kFAAkF;IAClF,8EAA8E;IAC9E,kFAAkF;IAClF,8EAA8E;IAC9E,kFAAkF;IAClF,oFAAoF;IACpF,kFAAkF;IAClF,mFAAmF;IACnF,qEAAqE,CAAC;AAExE,qDAAqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAA4B;IAC5D,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,eAAe,EAAE,UAAU,EAAE,WAAW,CAAC;IACpD,UAAU,EAAE;QACV,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE;QAC3D,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC5C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC9B;CACF,CAAC;AASF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAA4B,EAC5B,QAAsB,UAAU,EAChC,WAA0C;IAE1C,MAAM,cAAc,GAAiB,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;IACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CACnE,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"triage.js","sourceRoot":"","sources":["../src/triage.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GACxB,kFAAkF;IAClF,8EAA8E;IAC9E,kFAAkF;IAClF,8EAA8E;IAC9E,kFAAkF;IAClF,oFAAoF;IACpF,kFAAkF;IAClF,mFAAmF;IACnF,qEAAqE,CAAC;AAExE,qDAAqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAA4B;IAC5D,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,eAAe,EAAE,UAAU,EAAE,WAAW,CAAC;IACpD,UAAU,EAAE;QACV,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE;QAC3D,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC5C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC9B;CACF,CAAC;AASF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAA4B,EAC5B,QAAsB,UAAU,EAChC,WAA0C;IAE1C,MAAM,cAAc,GAAiB,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;IACtE,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CACnE,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC;AAC5E,CAAC","sourcesContent":["/**\n * Triage rubric, verdict schema, and request-bundle builder — all offline and\n * deterministic. Both planes share them: `@quantakrypto/agent` (networked)\n * prompts a model with the rubric, while the MCP `triage_findings` tool emits a\n * request bundle for the HOST agent to reason over. Keeping them here means the\n * MCP server never imports the networked package and both sides use one rubric.\n */\nimport type { Finding } from \"./types.js\";\nimport type { ContextLevel, RedactedContext } from \"./agent-types.js\";\nimport { buildContext } from \"./redact.js\";\n\n/** The system rubric that defines what an exposure verdict means. */\nexport const TRIAGE_RUBRIC =\n \"You are a post-quantum cryptography triage assistant. You are given ONE finding \" +\n \"of classical (quantum-vulnerable) cryptography detected in a codebase, with \" +\n \"limited, possibly-redacted context. Assess its REAL-WORLD exposure and urgency. \" +\n \"exposureScore (0-100): how exploitable/exposed this usage is — a long-lived \" +\n \"confidentiality key over the network (harvest-now-decrypt-later) scores high; a \" +\n \"short-lived local signature scores lower. priority: 'now' for HNDL/high-exposure, \" +\n \"'soon' for important-but-not-urgent, 'later' for low-exposure. You NEVER decide \" +\n \"whether the finding is valid and you NEVER suppress it — you only rank exposure. \" +\n \"Base your rationale only on the given context; do not invent facts.\";\n\n/** JSON Schema every triage verdict must satisfy. */\nexport const TRIAGE_VERDICT_SCHEMA: Record<string, unknown> = {\n type: \"object\",\n required: [\"exposureScore\", \"priority\", \"rationale\"],\n properties: {\n exposureScore: { type: \"number\", minimum: 0, maximum: 100 },\n priority: { enum: [\"now\", \"soon\", \"later\"] },\n rationale: { type: \"string\" },\n },\n};\n\n/** A triage request bundle: the rubric, the verdict schema, and redacted contexts. */\nexport interface TriageRequest {\n rubric: string;\n schema: Record<string, unknown>;\n contexts: RedactedContext[];\n}\n\n/**\n * Build a triage request bundle for a set of findings. Offline: for non-metadata\n * levels, pass `readContent` to supply each finding's file text; without it,\n * contexts are built at `metadata` level (no source), which is the safe default\n * for the MCP plane where the host agent already has the code open.\n */\nexport function buildTriageRequest(\n findings: readonly Finding[],\n level: ContextLevel = \"metadata\",\n readContent?: (finding: Finding) => string,\n): TriageRequest {\n const effectiveLevel: ContextLevel = readContent ? level : \"metadata\";\n const contexts = findings.map((f) =>\n buildContext(f, effectiveLevel, readContent ? readContent(f) : \"\"),\n );\n return { rubric: TRIAGE_RUBRIC, schema: TRIAGE_VERDICT_SCHEMA, contexts };\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -68,7 +68,7 @@ export interface Finding {
68
68
  location: SourceLocation;
69
69
  }
70
70
  /** Package ecosystems the dependency scanner understands. */
71
- export type DependencyEcosystem = "npm" | "pypi" | "cargo" | "go" | "maven" | "rubygems";
71
+ export type DependencyEcosystem = "npm" | "pypi" | "cargo" | "go" | "maven" | "rubygems" | "nuget";
72
72
  /** A known quantum-vulnerable dependency entry. */
73
73
  export interface VulnerableDependency {
74
74
  /** Package name (as written in the ecosystem's manifest). */
@@ -96,7 +96,7 @@ export type DetectorScope = "source" | "config";
96
96
  * The programming language / surface a detector targets. `"any"` means the
97
97
  * detector is language-agnostic (e.g. PEM material, config files).
98
98
  */
99
- export type DetectorLanguage = "js" | "python" | "go" | "java" | "csharp" | "rust" | "ruby" | "c" | "any";
99
+ export type DetectorLanguage = "js" | "python" | "go" | "java" | "csharp" | "rust" | "ruby" | "php" | "elixir" | "c" | "any";
100
100
  /**
101
101
  * Declarative metadata for a single rule a detector can emit. This is the
102
102
  * catalog entry: the stable, queryable description of a rule keyed by the
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * @quantakrypto/core — shared types (the locked public contract).\n *\n * These types are the stable interface between every tool in the monorepo\n * (qScan, the MCP server, the GitHub Action). Treat additions as backwards\n * compatible; treat renames/removals as breaking.\n */\n\n/** How serious a finding is, ordered most → least severe. */\nexport type Severity = \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\";\n\n/** How sure the detector is that the finding is a real use of the algorithm. */\nexport type Confidence = \"high\" | \"medium\" | \"low\";\n\n/** What kind of cryptographic concern a finding represents. */\nexport type FindingCategory =\n | \"kem\" // key encapsulation / public-key encryption (e.g. RSA-OAEP)\n | \"key-exchange\" // (EC)DH\n | \"signature\" // RSA/ECDSA/EdDSA signing\n | \"tls\" // transport configuration\n | \"certificate\" // X.509 / PKI material\n | \"dependency\" // a quantum-vulnerable library in the dependency tree\n | \"hash\" // weak / pre-quantum hash usage\n | \"rng\"; // randomness concerns\n\n/** Classical asymmetric algorithm families that are not quantum-safe. */\nexport type AlgorithmFamily =\n | \"RSA\"\n | \"ECDH\"\n | \"ECDSA\"\n | \"EdDSA\"\n | \"DH\"\n | \"DSA\"\n | \"X25519\"\n | \"X448\"\n | \"ECIES\"\n | \"unknown\";\n\n/** A precise location inside a scanned file. */\nexport interface SourceLocation {\n /** Path relative to the scan root, using POSIX separators. */\n file: string;\n /** 1-based line number. */\n line: number;\n /** 1-based column number, if known. */\n column?: number;\n /** 1-based end line, for multi-line matches. */\n endLine?: number;\n /** The matched source text (trimmed, single line). */\n snippet?: string;\n}\n\n/** Relative urgency an LLM triage pass assigns to a finding. */\nexport type TriagePriority = \"now\" | \"soon\" | \"later\";\n\n/**\n * Optional LLM triage annotation attached to a finding by `qscan --triage`.\n * Purely additive: it re-ranks and explains, it never suppresses a finding and\n * never influences the exit code (which is computed from `severity` alone).\n */\nexport interface TriageAnnotation {\n /** 0–100 real-world exposure/exploitability estimate. */\n exposureScore: number;\n priority: TriagePriority;\n rationale: string;\n}\n\n/** A single detected concern. */\nexport interface Finding {\n /** Stable rule identifier, e.g. \"rsa-keygen\", \"ecdh-usage\", \"tls-legacy-version\", \"dep-vulnerable\". */\n ruleId: string;\n title: string;\n category: FindingCategory;\n severity: Severity;\n confidence: Confidence;\n /** The classical algorithm involved, when applicable. */\n algorithm?: AlgorithmFamily;\n /** True when this is exposed to \"harvest now, decrypt later\". */\n hndl: boolean;\n /** One-line human explanation of the concern. */\n message: string;\n /** Suggested post-quantum remediation (e.g. ML-KEM, hybrid X25519MLKEM768). */\n remediation?: string;\n /** Associated CWE identifier, e.g. \"CWE-327\" (broken crypto), \"CWE-326\" (weak strength). */\n cwe?: string;\n /**\n * True when the matched snippet IS the sensitive value (e.g. a PEM private/\n * public key block, an `ssh-rsa AAAA…` public key). Reporters ALWAYS drop the\n * snippet for such findings, regardless of any redaction flag.\n */\n sensitive?: boolean;\n /** Optional LLM triage annotation (`qscan --triage`); never affects exit code. */\n triage?: TriageAnnotation;\n location: SourceLocation;\n}\n\n/** Package ecosystems the dependency scanner understands. */\nexport type DependencyEcosystem = \"npm\" | \"pypi\" | \"cargo\" | \"go\" | \"maven\" | \"rubygems\" | \"nuget\";\n\n/** A known quantum-vulnerable dependency entry. */\nexport interface VulnerableDependency {\n /** Package name (as written in the ecosystem's manifest). */\n name: string;\n ecosystem: DependencyEcosystem;\n /** Why it's flagged (what classical crypto it provides). */\n reason: string;\n /** Algorithm families the package primarily exposes. */\n algorithms: AlgorithmFamily[];\n severity: Severity;\n /**\n * Explicit harvest-now-decrypt-later override. When omitted, HNDL is inferred\n * from whether any listed family is a confidentiality family. Set `false` for\n * signing-only packages (e.g. JWS/JWT libraries) that list RSA/EC as families\n * but never do key transport or key agreement — signatures are not HNDL-exposed.\n */\n hndl?: boolean;\n}\n\n/**\n * Which logical scope a detector belongs to. Drives the source/config scope\n * toggles in {@link ScanOptions} (replacing the old ruleId-prefix inference).\n */\nexport type DetectorScope = \"source\" | \"config\";\n\n/**\n * The programming language / surface a detector targets. `\"any\"` means the\n * detector is language-agnostic (e.g. PEM material, config files).\n */\nexport type DetectorLanguage =\n | \"js\"\n | \"python\"\n | \"go\"\n | \"java\"\n | \"csharp\"\n | \"rust\"\n | \"ruby\"\n | \"php\"\n | \"elixir\"\n | \"c\"\n | \"any\";\n\n/**\n * Declarative metadata for a single rule a detector can emit. This is the\n * catalog entry: the stable, queryable description of a rule keyed by the\n * `ruleId` it stamps onto findings. It is the single source of truth for a\n * rule's title / severity / category / remediation, consumed by the SARIF\n * `rules[]` block, the MCP `explain_finding` resolver, and future per-rule\n * enable/disable + language-pack work.\n *\n * For most rules the metadata is fixed and `detect()` builds findings straight\n * from it (see `findingFromRule`). A few rules are inherently multi-variant\n * (e.g. `node-crypto-keygen` spans RSA/EC/DSA/DH/Ed25519 at different\n * severities); for those the metadata here is a REPRESENTATIVE umbrella and\n * `detect()` refines the per-finding fields at match time. The catalog always\n * enumerates every emittable ruleId regardless.\n */\nexport interface RuleMeta {\n /** Stable rule id — matches {@link Finding.ruleId}. Unique across the catalog. */\n id: string;\n /** Canonical human title. */\n title: string;\n category: FindingCategory;\n severity: Severity;\n /** Default confidence for findings of this rule. */\n confidence: Confidence;\n /** Harvest-now-decrypt-later exposure. */\n hndl: boolean;\n /** Representative classical algorithm family; refined per-finding when it varies. */\n algorithm?: AlgorithmFamily;\n /** Associated CWE identifier (e.g. \"CWE-327\"). */\n cwe?: string;\n /** Suggested post-quantum remediation. When omitted, derived from {@link algorithm}. */\n remediation?: string;\n /** Canonical one-line human explanation; may be refined per-finding. */\n message: string;\n /** True when this rule's matched snippet IS sensitive key material. */\n sensitive?: boolean;\n /** Short description of what the rule detects (for catalog / MCP surfaces). */\n description?: string;\n}\n\n/** A pluggable source detector. Detectors are pure and stateless. */\nexport interface Detector {\n /** Unique id, used as the Finding.ruleId prefix space. */\n id: string;\n /** Human description of what the detector looks for. */\n description: string;\n /**\n * Logical scope of this detector's findings. Used by `scan()` to honour the\n * `config` / `source` toggles. Defaults to `\"source\"` when omitted (for\n * backward compatibility with externally-defined detectors).\n */\n scope?: DetectorScope;\n /**\n * Language this detector targets, for documentation / registry filtering.\n * Defaults to `\"js\"` when omitted.\n */\n language?: DetectorLanguage;\n /**\n * The rules this detector can emit, as declarative metadata. Together across\n * all detectors these form the rule catalog ({@link DetectorRegistry.ruleCatalog}).\n * Optional for backward compatibility with externally-defined detectors, but\n * all built-in detectors declare it.\n */\n rules?: RuleMeta[];\n /** Whether this detector should run against the given file path. */\n appliesTo(filePath: string): boolean;\n /** Inspect a single file's contents and return zero or more findings. */\n detect(input: DetectorInput): Finding[];\n}\n\nexport interface DetectorInput {\n /** Path relative to the scan root (POSIX). */\n file: string;\n /** Full file contents. */\n content: string;\n}\n\n/** Options controlling a scan. */\nexport interface ScanOptions {\n /** Absolute or relative directory (or single file) to scan. */\n root: string;\n /**\n * Restrict the walk to paths matching one of these include patterns\n * (substring or relative-path-prefix match). When omitted, all non-excluded\n * files are scanned. Wired into the walker.\n */\n include?: string[];\n /** Extra exclude patterns (in addition to the built-in defaults). */\n exclude?: string[];\n /** Disable the built-in ignore list (node_modules, .git, dist, …). */\n noDefaultIgnores?: boolean;\n /** Scan source files for inline crypto usage. Default: true. */\n source?: boolean;\n /** Scan dependency manifests/lockfiles for vulnerable libraries. Default: true. */\n dependencies?: boolean;\n /** Scan config files (TLS, certificates). Default: true. */\n config?: boolean;\n /** Max file size to read, in bytes. Default: 2 MiB. */\n maxFileSize?: number;\n /**\n * Scan minified / generated / bundled files (large single-line content)\n * instead of skipping them. Default: false (skip them for speed).\n */\n scanMinified?: boolean;\n /**\n * Explicit relative file list (POSIX, relative to `root`) to scan instead of\n * walking the tree. Used for incremental / changed-files scans. Each path is\n * still subject to the binary / size filters. When set, the directory walk is\n * bypassed entirely.\n */\n files?: string[];\n /**\n * Override / extend the built-in detector set. When omitted, the default\n * registry's detectors are used.\n */\n detectors?: Detector[];\n /**\n * Rule ids to suppress. Any finding whose `ruleId` is listed here is dropped\n * after detection. Serializable (a plain string array), so it is honoured on\n * both the serial and the worker-thread (`scanParallel`) paths. See the rule\n * catalog ({@link DetectorRegistry.ruleCatalog}) for the valid ids.\n */\n disabledRules?: string[];\n /**\n * Path to an on-disk scan cache. When set, unchanged files (same content hash)\n * reuse their previous findings instead of re-running detectors, and the cache\n * is rewritten after the scan. Invalidated wholesale when the tool version,\n * detector set, or `disabledRules` change. Optional; omitted = no caching.\n * The cache forces the in-process (serial) path.\n */\n cacheFile?: string;\n /** Optional progress callback. */\n onFile?: (file: string) => void;\n /**\n * Optional abort signal. When it fires mid-walk the scan stops cooperatively\n * and rejects with an `AbortError` (a `DOMException`-like error with\n * `name === \"AbortError\"`).\n */\n signal?: AbortSignal;\n /**\n * Work budget: maximum number of files to scan. When exceeded mid-walk the\n * scan stops and throws a `BudgetExceededError`. Unlimited when omitted.\n */\n maxFiles?: number;\n /**\n * Work budget: maximum cumulative bytes of scanned file content. When\n * exceeded mid-walk the scan stops and throws a `BudgetExceededError`.\n * Unlimited when omitted.\n */\n maxBytes?: number;\n}\n\n/** Extra options for {@link scanParallel}, layered onto {@link ScanOptions}. */\nexport interface ParallelScanOptions extends ScanOptions {\n /**\n * Number of worker threads. Default: `os.availableParallelism()`. A value of\n * 0 or 1 forces the in-process serial path.\n */\n concurrency?: number;\n /**\n * Combined-size floor (bytes) below which the scan always runs in-process.\n * Default: 2 MiB. Also stays serial below `parallelFileThreshold` files.\n */\n parallelThresholdBytes?: number;\n /** File-count floor below which the scan always runs in-process. Default: 200. */\n parallelFileThreshold?: number;\n /** Target bytes per worker chunk. Default: 4 MiB. */\n chunkBytes?: number;\n}\n\n/** Aggregated counts produced from a scan's findings. */\nexport interface CryptoInventory {\n byAlgorithm: Partial<Record<AlgorithmFamily, number>>;\n byCategory: Partial<Record<FindingCategory, number>>;\n bySeverity: Record<Severity, number>;\n /** Number of findings exposed to harvest-now-decrypt-later. */\n hndlCount: number;\n /** 0–100 readiness score (100 = no classical asymmetric crypto found). */\n readinessScore: number;\n}\n\n/**\n * Non-fatal things that happened during a scan that reduce coverage. Surfaced so\n * a silent under-scan (e.g. half the tree was unreadable) can't masquerade as a\n * clean \"0 findings\" result — reporters warn when any count is non-zero.\n */\nexport interface ScanDiagnostics {\n /** Files that could not be read (permissions, vanished, decode failure) and were skipped. */\n unreadable: number;\n /** Files skipped because they look machine-minified / generated (scan with `scanMinified` to include). */\n skippedMinified: number;\n}\n\n/** The full result of a scan. */\nexport interface ScanResult {\n /** The scan root (as provided). */\n root: string;\n findings: Finding[];\n filesScanned: number;\n /**\n * Coverage diagnostics: counts of files skipped as unreadable or minified.\n * Optional for backward compatibility with hand-built results.\n */\n diagnostics?: ScanDiagnostics;\n /**\n * Of `filesScanned`, how many were in a source language the scanner can\n * actually analyze for inline crypto (JS/TS, Python, Go, Java). When this is 0 but\n * `filesScanned` > 0, the readiness score reflects NO analyzable code — the\n * crypto likely lives in an unsupported language (Go, Java, Rust, …) — and\n * reporters surface that so a bare 100/100 can't read as \"safe\". Optional for\n * backward compatibility with hand-built results.\n */\n analyzedFiles?: number;\n inventory: CryptoInventory;\n /** ISO timestamps. */\n startedAt: string;\n finishedAt: string;\n /** Tool version that produced the result. */\n toolVersion: string;\n}\n\n/** Output formats qScan / reporters can emit. */\nexport type ReportFormat = \"human\" | \"json\" | \"sarif\";\n\n/** A remediation recommendation for a classical algorithm. */\nexport interface Remediation {\n algorithm: AlgorithmFamily;\n /** Short recommended replacement, e.g. \"ML-KEM-768 (hybrid X25519MLKEM768)\". */\n recommendation: string;\n /** Longer rationale. */\n detail: string;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAElD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,GAAG,GAA2B;QAClC,EAAE,EAAE,KAAK;QACT,UAAU,EAAE,KAAK;QACjB,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,UAAU,EAAE,KAAK;QACjB,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,CAAC,EAAE,IAAI;QACP,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,CAAC,EAAE,IAAI;QACP,GAAG,EAAE,MAAM;KACZ,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,IAA8C;IAE9C,IAAI,IAAY,CAAC;IACjB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACpD,IAAI,GAAG,UAAU,GAAG,EAAE,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAElD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,GAAG,GAA2B;QAClC,EAAE,EAAE,KAAK;QACT,UAAU,EAAE,KAAK;QACjB,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,UAAU,EAAE,KAAK;QACjB,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,KAAK;QACb,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,CAAC,EAAE,IAAI;QACP,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,MAAM;QACX,EAAE,EAAE,KAAK;QACT,CAAC,EAAE,IAAI;QACP,GAAG,EAAE,MAAM;KACZ,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,IAA8C;IAE9C,IAAI,IAAY,CAAC;IACjB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;SAAM,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACpD,IAAI,GAAG,UAAU,GAAG,EAAE,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC","sourcesContent":["/**\n * Snippet-level fix verification: run the detectors over a piece of code (NOT\n * the filesystem) and report any classical crypto that remains. This is the\n * deterministic gate a fix must pass — the same logic the MCP `verify_fix` tool\n * and the remediation pipeline both use, so they can never disagree on what\n * \"the finding is gone\" means.\n */\nimport type { Finding } from \"./types.js\";\nimport { detectFile, detectors } from \"./scan.js\";\n\n/**\n * Map a language name (or a bare extension) to a source extension whose\n * detectors we run. Returns null for languages the scanner does not analyze.\n */\nexport function languageToExtension(language: string): string | null {\n const l = language.trim().toLowerCase().replace(/^\\./, \"\");\n const map: Record<string, string> = {\n js: \".js\",\n javascript: \".js\",\n jsx: \".jsx\",\n ts: \".ts\",\n typescript: \".ts\",\n tsx: \".tsx\",\n mjs: \".mjs\",\n cjs: \".cjs\",\n py: \".py\",\n python: \".py\",\n go: \".go\",\n golang: \".go\",\n java: \".java\",\n kotlin: \".kt\",\n kt: \".kt\",\n cs: \".cs\",\n csharp: \".cs\",\n \"c#\": \".cs\",\n dotnet: \".cs\",\n rs: \".rs\",\n rust: \".rs\",\n rb: \".rb\",\n ruby: \".rb\",\n c: \".c\",\n \"c++\": \".cpp\",\n cpp: \".cpp\",\n cc: \".cc\",\n h: \".h\",\n hpp: \".hpp\",\n };\n return map[l] ?? null;\n}\n\n/** Result of {@link verifyFix}: the findings that remain, and whether the\n * language is one the scanner actually analyzes (so `findings: []` on an\n * unsupported language is NOT a clean verification). */\nexport interface VerifyResult {\n supported: boolean;\n findings: Finding[];\n}\n\n/**\n * Run all detectors over `code`, selecting them by `filename` (extension) or\n * `language`. Pure: no I/O. When neither identifies an analyzable language,\n * `supported` is false and the empty findings list must not be read as \"fixed\".\n */\nexport function verifyFix(\n code: string,\n opts: { filename?: string; language?: string },\n): VerifyResult {\n let name: string;\n if (opts.filename && opts.filename.trim()) {\n name = opts.filename.trim();\n } else if (opts.language && opts.language.trim()) {\n const ext = languageToExtension(opts.language);\n if (!ext) return { supported: false, findings: [] };\n name = `snippet${ext}`;\n } else {\n return { supported: false, findings: [] };\n }\n const dotExt = name.replace(/^.*(\\.[^.]+)$/, \"$1\");\n const supported = languageToExtension(dotExt) !== null;\n const findings = detectFile(name, code, detectors, { source: true, config: true, deps: true });\n return { supported, findings };\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  * the scan orchestrator can import it without creating a cycle through index.ts.
4
4
  * Keep in sync with packages/core/package.json.
5
5
  */
6
- export declare const VERSION = "0.4.2";
6
+ export declare const VERSION = "0.4.4";
7
7
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -3,5 +3,5 @@
3
3
  * the scan orchestrator can import it without creating a cycle through index.ts.
4
4
  * Keep in sync with packages/core/package.json.
5
5
  */
6
- export const VERSION = "0.4.2";
6
+ export const VERSION = "0.4.4";
7
7
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC","sourcesContent":["/**\n * The tool version surfaced in reports. Kept in its own module so reporters and\n * the scan orchestrator can import it without creating a cycle through index.ts.\n * Keep in sync with packages/core/package.json.\n */\nexport const VERSION = \"0.4.4\";\n"]}
package/dist/walk.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Filesystem walker. A zero-dependency recursive async generator that yields\n * scannable text files as relative POSIX paths. It honours a default ignore\n * list, user-supplied exclude patterns, a max file size, and a binary-extension\n * filter. The root may be a directory or a single file.\n */\nimport { readdir, stat } from \"node:fs/promises\";\nimport type { Dirent } from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { isManifestFile } from \"./dependencies.js\";\n\n/** Directories ignored by default (can be disabled with noDefaultIgnores). */\nexport const DEFAULT_IGNORES: readonly string[] = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \"vendor\",\n \".turbo\",\n \".cache\",\n];\n\n/** Default maximum file size to read: 2 MiB. */\nexport const DEFAULT_MAX_FILE_SIZE = 2 * 1024 * 1024;\n\n/**\n * File extensions we treat as binary / non-text and therefore skip. Keeping this\n * as an extension allow-list-by-exclusion is cheap and avoids reading bytes.\n */\nconst BINARY_EXTENSIONS = new Set<string>([\n // images\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".avif\",\n // fonts\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".eot\",\n // archives / compressed\n \".zip\",\n \".gz\",\n \".tgz\",\n \".bz2\",\n \".xz\",\n \".7z\",\n \".rar\",\n \".tar\",\n // media\n \".mp3\",\n \".mp4\",\n \".mov\",\n \".avi\",\n \".mkv\",\n \".wav\",\n \".flac\",\n \".ogg\",\n \".webm\",\n // documents / binaries\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".bin\",\n \".o\",\n \".a\",\n \".class\",\n \".wasm\",\n // data blobs / db\n \".db\",\n \".sqlite\",\n \".sqlite3\",\n \".dat\",\n \".pack\",\n \".idx\",\n // misc\n \".lock\",\n \".map\",\n \".min.js\",\n \".node\",\n]);\n\n/** Options accepted by {@link walkFiles}. */\nexport interface WalkOptions {\n /**\n * Restrict to paths matching one of these include patterns (substring or\n * relative-path-prefix match). When omitted/empty, all files pass.\n */\n include?: string[];\n /** Extra exclude patterns (substring or relative-path-prefix match). */\n exclude?: string[];\n /** Disable the built-in directory ignore list. */\n noDefaultIgnores?: boolean;\n /** Max file size in bytes; larger files are skipped. */\n maxFileSize?: number;\n}\n\n/** Normalise a path to forward-slash POSIX separators. */\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** A pattern is treated as a glob when it contains one of these metacharacters. */\nfunction hasGlobMeta(pattern: string): boolean {\n return /[*?[]/.test(pattern);\n}\n\n/**\n * Translate a glob into an anchored RegExp: `*` matches within a path segment\n * (not `/`), `**` matches across segments, a leading `**` before a slash is an\n * optional path prefix, `?` matches a single non-`/` char, `[...]` is a\n * character class. Other regex\n * metacharacters are escaped.\n */\nfunction globToRegExp(glob: string): RegExp {\n let re = \"\";\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === \"*\") {\n if (glob[i + 1] === \"*\") {\n i++; // consume the second '*'\n if (glob[i + 1] === \"/\") {\n i++; // consume the trailing '/'\n re += \"(?:.*/)?\"; // zero or more path segments\n } else {\n re += \".*\"; // '**' spanning segments\n }\n } else {\n re += \"[^/]*\"; // '*' within a segment\n }\n } else if (c === \"?\") {\n re += \"[^/]\";\n } else if (c === \"[\") {\n // Pass a character class through; find its closing ']'.\n const end = glob.indexOf(\"]\", i + 1);\n if (end === -1) {\n re += \"\\\\[\";\n } else {\n re += glob.slice(i, end + 1);\n i = end;\n }\n } else if (\"\\\\^$.|+(){}\".includes(c)) {\n re += \"\\\\\" + c;\n } else {\n re += c;\n }\n }\n return new RegExp(`^${re}$`);\n}\n\n/** Memoized glob→RegExp so a pattern is compiled once, not once per file. */\nconst GLOB_CACHE = new Map<string, RegExp>();\nfunction globRegExp(pattern: string): RegExp {\n let re = GLOB_CACHE.get(pattern);\n if (!re) {\n re = globToRegExp(pattern);\n GLOB_CACHE.set(pattern, re);\n }\n return re;\n}\n\n/**\n * True if `rel` (a POSIX relative path) matches any pattern. A pattern with a\n * glob metacharacter (`*`, `?`, `[`) is matched as an anchored glob; a plain\n * pattern keeps the historical substring / path-prefix semantics (so `\"src\"`\n * still matches `src/a.ts` and `\"secrets\"` matches anywhere).\n */\nexport function matchesAny(rel: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (!pattern) continue;\n const p = toPosix(pattern).replace(/\\/+$/, \"\");\n if (hasGlobMeta(p)) {\n if (globRegExp(p).test(rel)) return true;\n continue;\n }\n // Plain pattern: substring match (handles \"src/legacy\" or \"secrets\")...\n if (rel.includes(p)) return true;\n // ...and explicit path-prefix match (\"foo\" should match \"foo/bar.ts\").\n if (rel === p || rel.startsWith(`${p}/`)) return true;\n }\n return false;\n}\n\n/** True if `rel` (a POSIX relative path) matches any exclude pattern. */\nfunction isExcluded(rel: string, exclude: readonly string[]): boolean {\n return matchesAny(rel, exclude);\n}\n\n/**\n * True if `rel` passes the include filter. An empty include list means \"include\n * everything\"; otherwise the file must match at least one include pattern.\n */\nfunction isIncluded(rel: string, include: readonly string[]): boolean {\n if (include.length === 0) return true;\n return matchesAny(rel, include);\n}\n\n/** True if the file's extension marks it as binary / non-text. */\nexport function isBinaryPath(rel: string): boolean {\n const lower = rel.toLowerCase();\n // Handle compound extensions like \".min.js\" first.\n if (lower.endsWith(\".min.js\")) return true;\n const ext = path.posix.extname(lower);\n return BINARY_EXTENSIONS.has(ext);\n}\n\n/**\n * Compound / pattern extensions that mark generated or bundled output we skip\n * by default (beyond `.min.js` / `.map`, which {@link isBinaryPath} handles).\n */\nconst GENERATED_PATH_RE =\n /(?:\\.min\\.[mc]?js|[.-]min\\.[mc]?js|\\.bundle\\.[mc]?js|\\.chunk\\.[mc]?js|\\.generated\\.[jt]sx?|_pb\\.js|\\.pb\\.go)$/i;\n\n/** True if the path looks like generated / bundled output (by name). */\nexport function isGeneratedPath(rel: string): boolean {\n return GENERATED_PATH_RE.test(rel.toLowerCase());\n}\n\n/**\n * Heuristic content check for machine-minified / generated files with no\n * telltale extension: a very long average line length, or any single line over\n * ~50 KB, in the first ~64 KB sampled. Used at read time, not in the walker.\n */\nexport function looksMinified(content: string): boolean {\n const sample = content.length > 65_536 ? content.slice(0, 65_536) : content;\n if (sample.length === 0) return false;\n let maxLine = 0;\n let cur = 0;\n let lines = 1;\n for (let i = 0; i < sample.length; i++) {\n if (sample.charCodeAt(i) === 10 /* \\n */) {\n if (cur > maxLine) maxLine = cur;\n cur = 0;\n lines++;\n } else {\n cur++;\n }\n }\n if (cur > maxLine) maxLine = cur;\n if (maxLine > 50_000) return true;\n const avgLine = sample.length / lines;\n return avgLine > 1_000;\n}\n\n/**\n * Recursively yield scannable file paths (relative to `root`, POSIX) under a\n * directory. If `root` points at a single file, yields just that file's\n * basename (subject to the size / binary filters).\n */\nexport async function* walkFiles(root: string, options: WalkOptions = {}): AsyncGenerator<string> {\n const include = options.include ?? [];\n const exclude = options.exclude ?? [];\n const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;\n const ignores = options.noDefaultIgnores ? [] : DEFAULT_IGNORES;\n\n const rootStat = await stat(root);\n\n // Single-file mode: yield the file itself (by basename) if it passes filters.\n if (rootStat.isFile()) {\n const name = toPosix(path.basename(root));\n if (\n !isBinaryPath(name) &&\n isIncluded(name, include) &&\n passesSizeLimit(name, rootStat.size, maxFileSize)\n ) {\n yield name;\n }\n return;\n }\n\n yield* walkDir(root, \"\", { include, exclude, maxFileSize, ignores });\n}\n\ninterface WalkContext {\n include: readonly string[];\n exclude: readonly string[];\n maxFileSize: number;\n ignores: readonly string[];\n}\n\n/**\n * True if a file passes the size limit. Dependency manifests (package.json /\n * package-lock.json) are exempt from the cap so large lockfiles still get\n * scanned for vulnerable dependencies instead of being silently dropped.\n */\nfunction passesSizeLimit(rel: string, size: number, maxFileSize: number): boolean {\n // Dependency manifests are always read (they can exceed the size cap but carry\n // the whole dependency tree). Uses the single {@link isManifestFile} definition.\n if (isManifestFile(rel)) return true;\n return size <= maxFileSize;\n}\n\n/** Internal recursive directory walker. `relDir` is POSIX-relative to the root. */\nasync function* walkDir(absDir: string, relDir: string, ctx: WalkContext): AsyncGenerator<string> {\n let entries: Dirent[];\n try {\n entries = await readdir(absDir, { withFileTypes: true });\n } catch {\n // Unreadable directory (permissions, transient races) — skip silently.\n return;\n }\n\n // Stable, deterministic ordering for reproducible scans.\n entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n for (const entry of entries) {\n const rel = relDir ? `${relDir}/${entry.name}` : entry.name;\n const abs = path.join(absDir, entry.name);\n\n if (entry.isSymbolicLink()) {\n // Don't follow symlinks: avoids cycles and escaping the root.\n continue;\n }\n\n if (entry.isDirectory()) {\n if (ctx.ignores.includes(entry.name)) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n yield* walkDir(abs, rel, ctx);\n continue;\n }\n\n if (!entry.isFile()) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n if (!isIncluded(rel, ctx.include)) continue;\n // Dependency manifests (incl. yarn.lock / pnpm-lock.yaml) are always read —\n // they carry the dependency tree and must not be dropped as binary/generated.\n const manifest = isManifestFile(rel);\n if (!manifest && isBinaryPath(rel)) continue;\n if (!manifest && isGeneratedPath(rel)) continue;\n\n try {\n const s = await stat(abs);\n if (!passesSizeLimit(rel, s.size, ctx.maxFileSize)) continue;\n } catch {\n continue;\n }\n\n yield rel;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,GAAY;IAC7C,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,EAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,2BAA2B,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,0EAA0E;IAC1E,8DAA8D;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC1D,OAAO,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;QACD,8EAA8E;QAC9E,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;QACD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,GAAY;IAC7C,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,EAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,2BAA2B,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,0EAA0E;IAC1E,8DAA8D;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC1D,OAAO,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;QACD,8EAA8E;QAC9E,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;QACD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC","sourcesContent":["/**\n * Ephemeral git worktree runner. Remediation applies candidate patches inside a\n * throwaway `git worktree` checked out at HEAD, so the user's working tree is\n * never touched while patches are being tried and verified. The worktree is\n * always removed afterwards, even if the callback throws.\n */\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst exec = promisify(execFile);\n\nasync function git(args: string[], cwd?: string): Promise<void> {\n await exec(\"git\", args, { cwd });\n}\n\n/**\n * Create a detached worktree of `repoRoot` at HEAD, run `fn` with its path, and\n * always tear it down. Throws if `repoRoot` is not a git repository.\n */\nexport async function withWorktree<T>(\n repoRoot: string,\n fn: (dir: string) => Promise<T>,\n): Promise<T> {\n try {\n await git([\"rev-parse\", \"--is-inside-work-tree\"], repoRoot);\n } catch {\n throw new Error(`withWorktree: \"${repoRoot}\" is not a git repository`);\n }\n const base = await mkdtemp(join(tmpdir(), \"quantakrypto-wt-\"));\n const dir = join(base, \"wt\");\n // `worktree add` is INSIDE the try so a failure (locked index, disk full)\n // still cleans up the temp dir in `finally` (audit: arch #6).\n try {\n await git([\"worktree\", \"add\", \"--detach\", dir], repoRoot);\n return await fn(dir);\n } finally {\n try {\n await git([\"worktree\", \"remove\", \"--force\", dir], repoRoot);\n } catch {\n // best effort — the temp dir removal below still reclaims the disk.\n }\n // Prune stale `.git/worktrees/<name>` metadata a failed add/remove can leave.\n try {\n await git([\"worktree\", \"prune\"], repoRoot);\n } catch {\n // best effort\n }\n await rm(base, { recursive: true, force: true });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantakrypto/core",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Shared post-quantum readiness library: crypto detectors, vulnerable-dependency database, inventory + SARIF reporting. Zero runtime dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dandelion Labs <hello@dandelionlabs.io> (https://dandelionlabs.io)",
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Merge multiple CycloneDX 1.6 CBOMs into one combined cryptographic bill of
3
+ * materials. This is what turns the "code + infra + live-endpoint" story into a
4
+ * single artifact: a qScan CBOM (code + infrastructure-as-code/config) and a
5
+ * qProbe CBOM (live endpoints) both emit `cryptographic-asset` components keyed by
6
+ * a deterministic `bom-ref` per (algorithm, primitive), so merging is a union with
7
+ * dedup — the same crypto seen by two planes collapses to one component whose
8
+ * occurrence evidence spans both.
9
+ *
10
+ * Output is deterministic (components sorted by bom-ref, occurrences sorted by
11
+ * location, a stable serial derived from the merged content), so re-merging the
12
+ * same inputs yields byte-identical output.
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import type { CycloneDxBom, CbomComponent } from "./cbom.js";
16
+
17
+ /** Occurrence record shape inside a component's `evidence.occurrences`. */
18
+ interface Occurrence {
19
+ location: string;
20
+ additionalContext?: string;
21
+ }
22
+
23
+ function occurrencesOf(c: CbomComponent): Occurrence[] {
24
+ const ev = c.evidence as { occurrences?: unknown } | undefined;
25
+ const occ = ev?.occurrences;
26
+ return Array.isArray(occ) ? (occ as Occurrence[]) : [];
27
+ }
28
+
29
+ function hndlOf(c: CbomComponent): boolean {
30
+ return (
31
+ (c.cryptoProperties as { harvestNowDecryptLater?: unknown }).harvestNowDecryptLater === true
32
+ );
33
+ }
34
+
35
+ /** Union + dedup occurrences by `location`, sorted deterministically. */
36
+ function mergeOccurrences(a: Occurrence[], b: Occurrence[]): Occurrence[] {
37
+ const byLoc = new Map<string, Occurrence>();
38
+ for (const o of [...a, ...b]) if (!byLoc.has(o.location)) byLoc.set(o.location, o);
39
+ return [...byLoc.values()].sort((x, y) =>
40
+ x.location < y.location ? -1 : x.location > y.location ? 1 : 0,
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Merge CBOMs into one. Components with the same `bom-ref` (same algorithm +
46
+ * primitive) are combined; their occurrence evidence is unioned and their
47
+ * harvest-now-decrypt-later flag is OR-ed. Tool and root metadata from every input
48
+ * is preserved.
49
+ */
50
+ export function mergeCboms(boms: readonly CycloneDxBom[]): CycloneDxBom {
51
+ const byRef = new Map<string, CbomComponent>();
52
+ const toolComponents: unknown[] = [];
53
+ const toolSeen = new Set<string>();
54
+ const roots: string[] = [];
55
+
56
+ for (const bom of boms) {
57
+ // Collect tool + root metadata (dedup tools by name@version).
58
+ const tools = (bom.metadata as { tools?: { components?: unknown[] } })?.tools?.components;
59
+ if (Array.isArray(tools)) {
60
+ for (const t of tools) {
61
+ const tc = t as { name?: string; version?: string };
62
+ const key = `${tc.name}@${tc.version}`;
63
+ if (!toolSeen.has(key)) {
64
+ toolSeen.add(key);
65
+ toolComponents.push(t);
66
+ }
67
+ }
68
+ }
69
+ const root = (bom.metadata as { component?: { name?: string } })?.component?.name;
70
+ if (typeof root === "string" && root && !roots.includes(root)) roots.push(root);
71
+
72
+ for (const c of bom.components) {
73
+ const ref = c["bom-ref"];
74
+ const existing = byRef.get(ref);
75
+ if (!existing) {
76
+ // Deep-ish copy so we can mutate evidence/hndl without touching the input.
77
+ byRef.set(ref, {
78
+ ...c,
79
+ cryptoProperties: { ...c.cryptoProperties },
80
+ evidence: { occurrences: occurrencesOf(c) },
81
+ });
82
+ } else {
83
+ const merged = mergeOccurrences(occurrencesOf(existing), occurrencesOf(c));
84
+ existing.evidence = { occurrences: merged };
85
+ existing.cryptoProperties = {
86
+ ...existing.cryptoProperties,
87
+ harvestNowDecryptLater: hndlOf(existing) || hndlOf(c),
88
+ };
89
+ }
90
+ }
91
+ }
92
+
93
+ const components = [...byRef.values()].sort((a, b) =>
94
+ a["bom-ref"] < b["bom-ref"] ? -1 : a["bom-ref"] > b["bom-ref"] ? 1 : 0,
95
+ );
96
+
97
+ const serialSeed = components.map((c) => c["bom-ref"]).join("|") + `|${roots.join(",")}`;
98
+ const h = createHash("sha256").update(serialSeed, "utf8").digest("hex");
99
+ const serial = `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-8${h.slice(17, 20)}-${h.slice(20, 32)}`;
100
+
101
+ return {
102
+ bomFormat: "CycloneDX",
103
+ specVersion: "1.6",
104
+ serialNumber: `urn:uuid:${serial}`,
105
+ version: 1,
106
+ metadata: {
107
+ tools: { components: toolComponents },
108
+ component: {
109
+ type: "application",
110
+ "bom-ref": "root",
111
+ name: roots.length ? `combined: ${roots.join(" + ")}` : "combined",
112
+ },
113
+ },
114
+ components,
115
+ };
116
+ }
package/src/comments.ts CHANGED
@@ -54,32 +54,64 @@ export function commentStyleForFile(file: string): CommentStyle | null {
54
54
  return null;
55
55
  }
56
56
 
57
+ /**
58
+ * Advance past a string / char literal starting at `content[i]` (a quote char);
59
+ * returns the index just after it. Two correctness rules a naive escape-scan got
60
+ * wrong (both verified as real bugs):
61
+ * - a `'` char literal must close within a short window (`'\u{10FFFF}'` is ~11
62
+ * chars); otherwise it is a Rust lifetime (`'a`) or a stray apostrophe, NOT a
63
+ * literal — return `i + 1` so the scan continues instead of consuming the
64
+ * rest of the file.
65
+ * - Go raw strings (backtick, when `rawBacktick`) do NOT process escapes, so a
66
+ * trailing backslash (`` `C:\` ``) must not swallow the closing delimiter.
67
+ */
68
+ function skipQuoted(content: string, i: number, n: number, rawBacktick: boolean): number {
69
+ const quote = content[i];
70
+ if (quote === "'") {
71
+ const limit = Math.min(n, i + 1 + 12);
72
+ let j = i + 1;
73
+ while (j < limit) {
74
+ if (content[j] === "\\") {
75
+ j += 2;
76
+ continue;
77
+ }
78
+ if (content[j] === "'") return j + 1;
79
+ j++;
80
+ }
81
+ return i + 1; // a lifetime / stray apostrophe, not a char literal
82
+ }
83
+ const escapes = !(quote === "`" && rawBacktick);
84
+ let j = i + 1;
85
+ while (j < n) {
86
+ if (escapes && content[j] === "\\") {
87
+ j += 2;
88
+ continue;
89
+ }
90
+ if (content[j] === quote) return j + 1;
91
+ j++;
92
+ }
93
+ return n;
94
+ }
95
+
57
96
  /**
58
97
  * Compute the comment spans (`[start, end)` offsets) of `content`, skipping over
59
98
  * string literals so a comment marker inside a string is not treated as a
60
- * comment. Spans are returned sorted and non-overlapping.
99
+ * comment. Spans are returned sorted and non-overlapping. `rawBacktick` (Go)
100
+ * makes backtick strings raw (no escapes).
61
101
  */
62
- export function commentSpans(content: string, style: CommentStyle): Array<[number, number]> {
102
+ export function commentSpans(
103
+ content: string,
104
+ style: CommentStyle,
105
+ rawBacktick = false,
106
+ ): Array<[number, number]> {
63
107
  const spans: Array<[number, number]> = [];
64
108
  const n = content.length;
65
109
  let i = 0;
66
110
  while (i < n) {
67
111
  const c = content[i];
68
- // String / template literal: skip to the matching unescaped delimiter.
112
+ // String / char literal: skip past it (lexically correct for lifetimes + raw strings).
69
113
  if (c === '"' || c === "'" || c === "`") {
70
- const quote = c;
71
- i++;
72
- while (i < n) {
73
- if (content[i] === "\\") {
74
- i += 2;
75
- continue;
76
- }
77
- if (content[i] === quote) {
78
- i++;
79
- break;
80
- }
81
- i++;
82
- }
114
+ i = skipQuoted(content, i, n, rawBacktick);
83
115
  continue;
84
116
  }
85
117
  if (style === "c" && c === "/" && content[i + 1] === "/") {
@@ -109,6 +141,54 @@ export function commentSpans(content: string, style: CommentStyle): Array<[numbe
109
141
  return spans;
110
142
  }
111
143
 
144
+ /**
145
+ * Compute Python triple-quoted string spans (`"""…"""` / `'''…'''`). These are
146
+ * docstrings / prose in practice, so a crypto *name* mentioned inside one
147
+ * (`:param key_type: eg "ssh-ed25519"`) should not fire a token finding — but a
148
+ * PEM key pasted into one is still real material, so {@link stripCommentFindings}
149
+ * exempts `pem-*` rules. Comments and normal strings are skipped so a `"""`
150
+ * delimiter inside them is not mis-detected.
151
+ */
152
+ export function pythonDocstringSpans(content: string): Array<[number, number]> {
153
+ const spans: Array<[number, number]> = [];
154
+ const n = content.length;
155
+ let i = 0;
156
+ while (i < n) {
157
+ const c = content[i];
158
+ if (c === "#") {
159
+ i++;
160
+ while (i < n && content[i] !== "\n") i++;
161
+ continue;
162
+ }
163
+ if ((c === '"' || c === "'") && content[i + 1] === c && content[i + 2] === c) {
164
+ const start = i;
165
+ i += 3;
166
+ while (i < n && !(content[i] === c && content[i + 1] === c && content[i + 2] === c)) i++;
167
+ i = Math.min(n, i + 3);
168
+ spans.push([start, i]);
169
+ continue;
170
+ }
171
+ if (c === '"' || c === "'") {
172
+ const q = c;
173
+ i++;
174
+ while (i < n) {
175
+ if (content[i] === "\\") {
176
+ i += 2;
177
+ continue;
178
+ }
179
+ if (content[i] === q) {
180
+ i++;
181
+ break;
182
+ }
183
+ i++;
184
+ }
185
+ continue;
186
+ }
187
+ i++;
188
+ }
189
+ return spans;
190
+ }
191
+
112
192
  /** True if `offset` falls inside one of the (sorted, non-overlapping) spans. */
113
193
  export function offsetInSpans(spans: ReadonlyArray<[number, number]>, offset: number): boolean {
114
194
  let lo = 0;
@@ -162,8 +242,10 @@ export function stripCommentFindings(
162
242
  if (findings.length === 0) return findings;
163
243
  const style = commentStyleForFile(file);
164
244
  if (!style) return findings;
165
- const spans = commentSpans(content, style);
166
- if (spans.length === 0) return findings;
245
+ const spans = commentSpans(content, style, /\.go$/i.test(file));
246
+ // Python docstrings suppress prose *token* rules but keep real PEM material.
247
+ const docSpans = style === "hash" ? pythonDocstringSpans(content) : [];
248
+ if (spans.length === 0 && docSpans.length === 0) return findings;
167
249
 
168
250
  // 1-based line → start offset, to turn a finding's (line, column) back into an
169
251
  // absolute offset (column is 1-based: offset = lineStart + column - 1).
@@ -173,6 +255,89 @@ export function stripCommentFindings(
173
255
  }
174
256
 
175
257
  return findings.filter((f) => {
258
+ const start = lineStarts[f.location.line - 1] ?? 0;
259
+ const offset = start + ((f.location.column ?? 1) - 1);
260
+ if (offsetInSpans(spans, offset)) return false;
261
+ if (docSpans.length > 0 && !f.ruleId.startsWith("pem-") && offsetInSpans(docSpans, offset)) {
262
+ return false;
263
+ }
264
+ return true;
265
+ });
266
+ }
267
+
268
+ /**
269
+ * Compute the string-literal spans (`[start, end)`) of `content`, skipping over
270
+ * comments so a quote inside a comment is not treated as a string. Used to
271
+ * suppress findings from IDENTIFIER-only rules (e.g. a Go `SigningMethodRS256`
272
+ * mentioned inside an error-message string) — the mirror of {@link commentSpans}.
273
+ */
274
+ export function stringSpans(
275
+ content: string,
276
+ style: CommentStyle,
277
+ rawBacktick = false,
278
+ ): Array<[number, number]> {
279
+ const spans: Array<[number, number]> = [];
280
+ const n = content.length;
281
+ let i = 0;
282
+ while (i < n) {
283
+ const c = content[i];
284
+ if (style === "c" && c === "/" && content[i + 1] === "/") {
285
+ i += 2;
286
+ while (i < n && content[i] !== "\n") i++;
287
+ continue;
288
+ }
289
+ if (style === "c" && c === "/" && content[i + 1] === "*") {
290
+ i += 2;
291
+ while (i < n && !(content[i] === "*" && content[i + 1] === "/")) i++;
292
+ i = Math.min(n, i + 2);
293
+ continue;
294
+ }
295
+ if (style === "hash" && c === "#") {
296
+ i++;
297
+ while (i < n && content[i] !== "\n") i++;
298
+ continue;
299
+ }
300
+ if (c === '"' || c === "'" || c === "`") {
301
+ const start = i;
302
+ const end = skipQuoted(content, i, n, rawBacktick);
303
+ // A lone `'` (Rust lifetime, end === start+1) is not a string; skip it.
304
+ if (c !== "'" || end > start + 1) spans.push([start, end]);
305
+ i = end;
306
+ continue;
307
+ }
308
+ i++;
309
+ }
310
+ return spans;
311
+ }
312
+
313
+ /**
314
+ * Drop findings of "code-only" rules (`ruleIds`) whose match starts inside a
315
+ * string literal. Some rules — an identifier-form JWT signing method, a Go
316
+ * `SigningMethodRS256` — are only meaningful as code; when the same token appears
317
+ * inside a string (a test's `t.Error("SigningMethodPS256 …")`) it is prose, not a
318
+ * usage. Rules that legitimately match inside strings (quoted `"RS256"` alg
319
+ * tokens, cipher-suite strings, ssh-key tokens) are NOT in `ruleIds` and pass
320
+ * through untouched.
321
+ */
322
+ export function stripStringLiteralFindings(
323
+ findings: Finding[],
324
+ content: string,
325
+ file: string,
326
+ ruleIds: ReadonlySet<string>,
327
+ ): Finding[] {
328
+ if (findings.length === 0 || !findings.some((f) => ruleIds.has(f.ruleId))) return findings;
329
+ const style = commentStyleForFile(file);
330
+ if (!style) return findings;
331
+ const spans = stringSpans(content, style, /\.go$/i.test(file));
332
+ if (spans.length === 0) return findings;
333
+
334
+ const lineStarts: number[] = [0];
335
+ for (let i = 0; i < content.length; i++) {
336
+ if (content[i] === "\n") lineStarts.push(i + 1);
337
+ }
338
+
339
+ return findings.filter((f) => {
340
+ if (!ruleIds.has(f.ruleId)) return true;
176
341
  const start = lineStarts[f.location.line - 1] ?? 0;
177
342
  const offset = start + ((f.location.column ?? 1) - 1);
178
343
  return !offsetInSpans(spans, offset);