@quantakrypto/core 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/agent-types.d.ts +48 -0
  2. package/dist/agent-types.d.ts.map +1 -0
  3. package/dist/agent-types.js +2 -0
  4. package/dist/agent-types.js.map +1 -0
  5. package/dist/cache.d.ts +27 -0
  6. package/dist/cache.d.ts.map +1 -0
  7. package/dist/cache.js +93 -0
  8. package/dist/cache.js.map +1 -0
  9. package/dist/cbom.d.ts.map +1 -1
  10. package/dist/cbom.js +26 -1
  11. package/dist/cbom.js.map +1 -1
  12. package/dist/changed.d.ts.map +1 -1
  13. package/dist/changed.js +14 -2
  14. package/dist/changed.js.map +1 -1
  15. package/dist/codemods/config-toggle.d.ts +3 -0
  16. package/dist/codemods/config-toggle.d.ts.map +1 -0
  17. package/dist/codemods/config-toggle.js +26 -0
  18. package/dist/codemods/config-toggle.js.map +1 -0
  19. package/dist/codemods/registry.d.ts +26 -0
  20. package/dist/codemods/registry.d.ts.map +1 -0
  21. package/dist/codemods/registry.js +8 -0
  22. package/dist/codemods/registry.js.map +1 -0
  23. package/dist/comments.d.ts +45 -0
  24. package/dist/comments.d.ts.map +1 -0
  25. package/dist/comments.js +164 -0
  26. package/dist/comments.js.map +1 -0
  27. package/dist/config.d.ts +1 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +7 -0
  30. package/dist/config.js.map +1 -1
  31. package/dist/dependencies.d.ts +31 -13
  32. package/dist/dependencies.d.ts.map +1 -1
  33. package/dist/dependencies.js +460 -40
  34. package/dist/dependencies.js.map +1 -1
  35. package/dist/detect-utils.d.ts +44 -1
  36. package/dist/detect-utils.d.ts.map +1 -1
  37. package/dist/detect-utils.js +113 -17
  38. package/dist/detect-utils.js.map +1 -1
  39. package/dist/detectors/c.d.ts +20 -0
  40. package/dist/detectors/c.d.ts.map +1 -0
  41. package/dist/detectors/c.js +104 -0
  42. package/dist/detectors/c.js.map +1 -0
  43. package/dist/detectors/csharp.d.ts +15 -0
  44. package/dist/detectors/csharp.d.ts.map +1 -0
  45. package/dist/detectors/csharp.js +76 -0
  46. package/dist/detectors/csharp.js.map +1 -0
  47. package/dist/detectors/go.d.ts +25 -0
  48. package/dist/detectors/go.d.ts.map +1 -0
  49. package/dist/detectors/go.js +132 -0
  50. package/dist/detectors/go.js.map +1 -0
  51. package/dist/detectors/java.d.ts +25 -0
  52. package/dist/detectors/java.d.ts.map +1 -0
  53. package/dist/detectors/java.js +200 -0
  54. package/dist/detectors/java.js.map +1 -0
  55. package/dist/detectors/python.d.ts +26 -0
  56. package/dist/detectors/python.d.ts.map +1 -0
  57. package/dist/detectors/python.js +171 -0
  58. package/dist/detectors/python.js.map +1 -0
  59. package/dist/detectors/ruby.d.ts +15 -0
  60. package/dist/detectors/ruby.d.ts.map +1 -0
  61. package/dist/detectors/ruby.js +75 -0
  62. package/dist/detectors/ruby.js.map +1 -0
  63. package/dist/detectors/rust.d.ts +13 -0
  64. package/dist/detectors/rust.d.ts.map +1 -0
  65. package/dist/detectors/rust.js +94 -0
  66. package/dist/detectors/rust.js.map +1 -0
  67. package/dist/detectors/source.d.ts.map +1 -1
  68. package/dist/detectors/source.js +70 -22
  69. package/dist/detectors/source.js.map +1 -1
  70. package/dist/index.d.ts +18 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +15 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/parallel.d.ts +4 -0
  75. package/dist/parallel.d.ts.map +1 -1
  76. package/dist/parallel.js +59 -8
  77. package/dist/parallel.js.map +1 -1
  78. package/dist/patch-policy.d.ts +22 -0
  79. package/dist/patch-policy.d.ts.map +1 -0
  80. package/dist/patch-policy.js +21 -0
  81. package/dist/patch-policy.js.map +1 -0
  82. package/dist/redact.d.ts +19 -0
  83. package/dist/redact.d.ts.map +1 -0
  84. package/dist/redact.js +77 -0
  85. package/dist/redact.js.map +1 -0
  86. package/dist/registry.d.ts +13 -3
  87. package/dist/registry.d.ts.map +1 -1
  88. package/dist/registry.js +31 -4
  89. package/dist/registry.js.map +1 -1
  90. package/dist/remediate-pipeline.d.ts +41 -0
  91. package/dist/remediate-pipeline.d.ts.map +1 -0
  92. package/dist/remediate-pipeline.js +38 -0
  93. package/dist/remediate-pipeline.js.map +1 -0
  94. package/dist/remediate-request.d.ts +21 -0
  95. package/dist/remediate-request.d.ts.map +1 -0
  96. package/dist/remediate-request.js +28 -0
  97. package/dist/remediate-request.js.map +1 -0
  98. package/dist/remediation.d.ts +10 -0
  99. package/dist/remediation.d.ts.map +1 -1
  100. package/dist/remediation.js +21 -8
  101. package/dist/remediation.js.map +1 -1
  102. package/dist/report.d.ts.map +1 -1
  103. package/dist/report.js +16 -1
  104. package/dist/report.js.map +1 -1
  105. package/dist/scan-worker.js +6 -2
  106. package/dist/scan-worker.js.map +1 -1
  107. package/dist/scan.d.ts +5 -4
  108. package/dist/scan.d.ts.map +1 -1
  109. package/dist/scan.js +52 -29
  110. package/dist/scan.js.map +1 -1
  111. package/dist/triage.d.ts +27 -0
  112. package/dist/triage.d.ts.map +1 -0
  113. package/dist/triage.js +33 -0
  114. package/dist/triage.js.map +1 -0
  115. package/dist/types.d.ts +53 -3
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/verify.d.ts +30 -0
  118. package/dist/verify.d.ts.map +1 -0
  119. package/dist/verify.js +65 -0
  120. package/dist/verify.js.map +1 -0
  121. package/dist/version.d.ts +1 -1
  122. package/dist/version.js +1 -1
  123. package/dist/walk.d.ts +7 -0
  124. package/dist/walk.d.ts.map +1 -1
  125. package/dist/walk.js +85 -11
  126. package/dist/walk.js.map +1 -1
  127. package/dist/worktree.d.ts +6 -0
  128. package/dist/worktree.d.ts.map +1 -0
  129. package/dist/worktree.js +43 -0
  130. package/dist/worktree.js.map +1 -0
  131. package/package.json +1 -1
  132. package/src/agent-types.ts +52 -0
  133. package/src/cache.ts +120 -0
  134. package/src/cbom.ts +27 -1
  135. package/src/changed.ts +13 -4
  136. package/src/codemods/config-toggle.ts +35 -0
  137. package/src/codemods/registry.ts +31 -0
  138. package/src/comments.ts +180 -0
  139. package/src/config.ts +8 -0
  140. package/src/dependencies.ts +483 -51
  141. package/src/detect-utils.ts +123 -15
  142. package/src/detectors/c.ts +132 -0
  143. package/src/detectors/csharp.ts +98 -0
  144. package/src/detectors/go.ts +167 -0
  145. package/src/detectors/java.ts +235 -0
  146. package/src/detectors/python.ts +212 -0
  147. package/src/detectors/ruby.ts +97 -0
  148. package/src/detectors/rust.ts +112 -0
  149. package/src/detectors/source.ts +69 -21
  150. package/src/index.ts +45 -1
  151. package/src/parallel.ts +63 -6
  152. package/src/patch-policy.ts +42 -0
  153. package/src/redact.ts +92 -0
  154. package/src/registry.ts +32 -4
  155. package/src/remediate-pipeline.ts +81 -0
  156. package/src/remediate-request.ts +52 -0
  157. package/src/remediation.ts +27 -11
  158. package/src/report.ts +27 -1
  159. package/src/scan-worker.ts +6 -2
  160. package/src/scan.ts +62 -31
  161. package/src/triage.ts +58 -0
  162. package/src/types.ts +66 -3
  163. package/src/verify.ts +82 -0
  164. package/src/version.ts +1 -1
  165. package/src/walk.ts +81 -12
  166. package/src/worktree.ts +45 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Snippet-level fix verification: run the detectors over a piece of code (NOT
3
+ * the filesystem) and report any classical crypto that remains. This is the
4
+ * deterministic gate a fix must pass — the same logic the MCP `verify_fix` tool
5
+ * and the remediation pipeline both use, so they can never disagree on what
6
+ * "the finding is gone" means.
7
+ */
8
+ import type { Finding } from "./types.js";
9
+ /**
10
+ * Map a language name (or a bare extension) to a source extension whose
11
+ * detectors we run. Returns null for languages the scanner does not analyze.
12
+ */
13
+ export declare function languageToExtension(language: string): string | null;
14
+ /** Result of {@link verifyFix}: the findings that remain, and whether the
15
+ * language is one the scanner actually analyzes (so `findings: []` on an
16
+ * unsupported language is NOT a clean verification). */
17
+ export interface VerifyResult {
18
+ supported: boolean;
19
+ findings: Finding[];
20
+ }
21
+ /**
22
+ * Run all detectors over `code`, selecting them by `filename` (extension) or
23
+ * `language`. Pure: no I/O. When neither identifies an analyzable language,
24
+ * `supported` is false and the empty findings list must not be read as "fixed".
25
+ */
26
+ export declare function verifyFix(code: string, opts: {
27
+ filename?: string;
28
+ language?: string;
29
+ }): VerifyResult;
30
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCnE;AAED;;wDAEwD;AACxD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,YAAY,CAed"}
package/dist/verify.js ADDED
@@ -0,0 +1,65 @@
1
+ import { detectFile, detectors } from "./scan.js";
2
+ /**
3
+ * Map a language name (or a bare extension) to a source extension whose
4
+ * detectors we run. Returns null for languages the scanner does not analyze.
5
+ */
6
+ export function languageToExtension(language) {
7
+ const l = language.trim().toLowerCase().replace(/^\./, "");
8
+ const map = {
9
+ js: ".js",
10
+ javascript: ".js",
11
+ jsx: ".jsx",
12
+ ts: ".ts",
13
+ typescript: ".ts",
14
+ tsx: ".tsx",
15
+ mjs: ".mjs",
16
+ cjs: ".cjs",
17
+ py: ".py",
18
+ python: ".py",
19
+ go: ".go",
20
+ golang: ".go",
21
+ java: ".java",
22
+ kotlin: ".kt",
23
+ kt: ".kt",
24
+ cs: ".cs",
25
+ csharp: ".cs",
26
+ "c#": ".cs",
27
+ dotnet: ".cs",
28
+ rs: ".rs",
29
+ rust: ".rs",
30
+ rb: ".rb",
31
+ ruby: ".rb",
32
+ c: ".c",
33
+ "c++": ".cpp",
34
+ cpp: ".cpp",
35
+ cc: ".cc",
36
+ h: ".h",
37
+ hpp: ".hpp",
38
+ };
39
+ return map[l] ?? null;
40
+ }
41
+ /**
42
+ * Run all detectors over `code`, selecting them by `filename` (extension) or
43
+ * `language`. Pure: no I/O. When neither identifies an analyzable language,
44
+ * `supported` is false and the empty findings list must not be read as "fixed".
45
+ */
46
+ export function verifyFix(code, opts) {
47
+ let name;
48
+ if (opts.filename && opts.filename.trim()) {
49
+ name = opts.filename.trim();
50
+ }
51
+ else if (opts.language && opts.language.trim()) {
52
+ const ext = languageToExtension(opts.language);
53
+ if (!ext)
54
+ return { supported: false, findings: [] };
55
+ name = `snippet${ext}`;
56
+ }
57
+ else {
58
+ return { supported: false, findings: [] };
59
+ }
60
+ const dotExt = name.replace(/^.*(\.[^.]+)$/, "$1");
61
+ const supported = languageToExtension(dotExt) !== null;
62
+ const findings = detectFile(name, code, detectors, { source: true, config: true, deps: true });
63
+ return { supported, findings };
64
+ }
65
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +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"}
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.3.0";
6
+ export declare const VERSION = "0.4.0";
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.3.0";
6
+ export const VERSION = "0.4.0";
7
7
  //# sourceMappingURL=version.js.map
package/dist/walk.d.ts CHANGED
@@ -18,6 +18,13 @@ export interface WalkOptions {
18
18
  }
19
19
  /** Normalise a path to forward-slash POSIX separators. */
20
20
  export declare function toPosix(p: string): string;
21
+ /**
22
+ * True if `rel` (a POSIX relative path) matches any pattern. A pattern with a
23
+ * glob metacharacter (`*`, `?`, `[`) is matched as an anchored glob; a plain
24
+ * pattern keeps the historical substring / path-prefix semantics (so `"src"`
25
+ * still matches `src/a.ts` and `"secrets"` matches anywhere).
26
+ */
27
+ export declare function matchesAny(rel: string, patterns: readonly string[]): boolean;
21
28
  /** True if the file's extension marks it as binary / non-text. */
22
29
  export declare function isBinaryPath(rel: string): boolean;
23
30
  /** True if the path looks like generated / bundled output (by name). */
@@ -1 +1 @@
1
- {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAUA,8EAA8E;AAC9E,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAW5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AAyErD,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,0DAA0D;AAC1D,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AA6BD,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMjD;AASD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED;;;;GAIG;AACH,wBAAuB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,CAsBhG"}
1
+ {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAYA,8EAA8E;AAC9E,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAW5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AAyErD,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,0DAA0D;AAC1D,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AA6DD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAc5E;AAgBD,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMjD;AASD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED;;;;GAIG;AACH,wBAAuB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,CAsBhG"}
package/dist/walk.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { readdir, stat } from "node:fs/promises";
8
8
  import * as path from "node:path";
9
+ import { isManifestFile } from "./dependencies.js";
9
10
  /** Directories ignored by default (can be disabled with noDefaultIgnores). */
10
11
  export const DEFAULT_IGNORES = [
11
12
  "node_modules",
@@ -95,13 +96,86 @@ const BINARY_EXTENSIONS = new Set([
95
96
  export function toPosix(p) {
96
97
  return p.split(path.sep).join("/");
97
98
  }
98
- /** True if `rel` (a POSIX relative path) matches any pattern (substring/prefix). */
99
- function matchesAny(rel, patterns) {
99
+ /** A pattern is treated as a glob when it contains one of these metacharacters. */
100
+ function hasGlobMeta(pattern) {
101
+ return /[*?[]/.test(pattern);
102
+ }
103
+ /**
104
+ * Translate a glob into an anchored RegExp: `*` matches within a path segment
105
+ * (not `/`), `**` matches across segments, a leading `**` before a slash is an
106
+ * optional path prefix, `?` matches a single non-`/` char, `[...]` is a
107
+ * character class. Other regex
108
+ * metacharacters are escaped.
109
+ */
110
+ function globToRegExp(glob) {
111
+ let re = "";
112
+ for (let i = 0; i < glob.length; i++) {
113
+ const c = glob[i];
114
+ if (c === "*") {
115
+ if (glob[i + 1] === "*") {
116
+ i++; // consume the second '*'
117
+ if (glob[i + 1] === "/") {
118
+ i++; // consume the trailing '/'
119
+ re += "(?:.*/)?"; // zero or more path segments
120
+ }
121
+ else {
122
+ re += ".*"; // '**' spanning segments
123
+ }
124
+ }
125
+ else {
126
+ re += "[^/]*"; // '*' within a segment
127
+ }
128
+ }
129
+ else if (c === "?") {
130
+ re += "[^/]";
131
+ }
132
+ else if (c === "[") {
133
+ // Pass a character class through; find its closing ']'.
134
+ const end = glob.indexOf("]", i + 1);
135
+ if (end === -1) {
136
+ re += "\\[";
137
+ }
138
+ else {
139
+ re += glob.slice(i, end + 1);
140
+ i = end;
141
+ }
142
+ }
143
+ else if ("\\^$.|+(){}".includes(c)) {
144
+ re += "\\" + c;
145
+ }
146
+ else {
147
+ re += c;
148
+ }
149
+ }
150
+ return new RegExp(`^${re}$`);
151
+ }
152
+ /** Memoized glob→RegExp so a pattern is compiled once, not once per file. */
153
+ const GLOB_CACHE = new Map();
154
+ function globRegExp(pattern) {
155
+ let re = GLOB_CACHE.get(pattern);
156
+ if (!re) {
157
+ re = globToRegExp(pattern);
158
+ GLOB_CACHE.set(pattern, re);
159
+ }
160
+ return re;
161
+ }
162
+ /**
163
+ * True if `rel` (a POSIX relative path) matches any pattern. A pattern with a
164
+ * glob metacharacter (`*`, `?`, `[`) is matched as an anchored glob; a plain
165
+ * pattern keeps the historical substring / path-prefix semantics (so `"src"`
166
+ * still matches `src/a.ts` and `"secrets"` matches anywhere).
167
+ */
168
+ export function matchesAny(rel, patterns) {
100
169
  for (const pattern of patterns) {
101
170
  if (!pattern)
102
171
  continue;
103
172
  const p = toPosix(pattern).replace(/\/+$/, "");
104
- // Substring match (handles "src/legacy" or "secrets")...
173
+ if (hasGlobMeta(p)) {
174
+ if (globRegExp(p).test(rel))
175
+ return true;
176
+ continue;
177
+ }
178
+ // Plain pattern: substring match (handles "src/legacy" or "secrets")...
105
179
  if (rel.includes(p))
106
180
  return true;
107
181
  // ...and explicit path-prefix match ("foo" should match "foo/bar.ts").
@@ -200,15 +274,12 @@ export async function* walkFiles(root, options = {}) {
200
274
  * scanned for vulnerable dependencies instead of being silently dropped.
201
275
  */
202
276
  function passesSizeLimit(rel, size, maxFileSize) {
203
- if (isManifestPath(rel))
277
+ // Dependency manifests are always read (they can exceed the size cap but carry
278
+ // the whole dependency tree). Uses the single {@link isManifestFile} definition.
279
+ if (isManifestFile(rel))
204
280
  return true;
205
281
  return size <= maxFileSize;
206
282
  }
207
- /** True if the path's basename is a dependency manifest we always read. */
208
- function isManifestPath(rel) {
209
- const base = rel.split("/").pop() ?? rel;
210
- return base === "package.json" || base === "package-lock.json";
211
- }
212
283
  /** Internal recursive directory walker. `relDir` is POSIX-relative to the root. */
213
284
  async function* walkDir(absDir, relDir, ctx) {
214
285
  let entries;
@@ -242,9 +313,12 @@ async function* walkDir(absDir, relDir, ctx) {
242
313
  continue;
243
314
  if (!isIncluded(rel, ctx.include))
244
315
  continue;
245
- if (isBinaryPath(rel))
316
+ // Dependency manifests (incl. yarn.lock / pnpm-lock.yaml) are always read —
317
+ // they carry the dependency tree and must not be dropped as binary/generated.
318
+ const manifest = isManifestFile(rel);
319
+ if (!manifest && isBinaryPath(rel))
246
320
  continue;
247
- if (isGeneratedPath(rel))
321
+ if (!manifest && isGeneratedPath(rel))
248
322
  continue;
249
323
  try {
250
324
  const s = await stat(abs);
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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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Create a detached worktree of `repoRoot` at HEAD, run `fn` with its path, and
3
+ * always tear it down. Throws if `repoRoot` is not a git repository.
4
+ */
5
+ export declare function withWorktree<T>(repoRoot: string, fn: (dir: string) => Promise<T>): Promise<T>;
6
+ //# sourceMappingURL=worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9B,OAAO,CAAC,CAAC,CAAC,CAmBZ"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Ephemeral git worktree runner. Remediation applies candidate patches inside a
3
+ * throwaway `git worktree` checked out at HEAD, so the user's working tree is
4
+ * never touched while patches are being tried and verified. The worktree is
5
+ * always removed afterwards, even if the callback throws.
6
+ */
7
+ import { execFile } from "node:child_process";
8
+ import { promisify } from "node:util";
9
+ import { mkdtemp, rm } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ const exec = promisify(execFile);
13
+ async function git(args, cwd) {
14
+ await exec("git", args, { cwd });
15
+ }
16
+ /**
17
+ * Create a detached worktree of `repoRoot` at HEAD, run `fn` with its path, and
18
+ * always tear it down. Throws if `repoRoot` is not a git repository.
19
+ */
20
+ export async function withWorktree(repoRoot, fn) {
21
+ try {
22
+ await git(["rev-parse", "--is-inside-work-tree"], repoRoot);
23
+ }
24
+ catch {
25
+ throw new Error(`withWorktree: "${repoRoot}" is not a git repository`);
26
+ }
27
+ const base = await mkdtemp(join(tmpdir(), "quantakrypto-wt-"));
28
+ const dir = join(base, "wt");
29
+ await git(["worktree", "add", "--detach", dir], repoRoot);
30
+ try {
31
+ return await fn(dir);
32
+ }
33
+ finally {
34
+ try {
35
+ await git(["worktree", "remove", "--force", dir], repoRoot);
36
+ }
37
+ catch {
38
+ // best effort — the temp dir removal below still reclaims the disk.
39
+ }
40
+ await rm(base, { recursive: true, force: true });
41
+ }
42
+ }
43
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +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,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC;QACH,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,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantakrypto/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
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,52 @@
1
+ /**
2
+ * Shared types for the agent (BYOK) plane. These live in `@quantakrypto/core`
3
+ * (offline) so both the deterministic MCP tools and the networked
4
+ * `@quantakrypto/agent` package speak the same shapes without the offline side
5
+ * ever importing the network client.
6
+ */
7
+ import type { Severity, TriageAnnotation } from "./types.js";
8
+
9
+ /** How much source context a redacted request carries. */
10
+ export type ContextLevel = "metadata" | "snippet" | "function" | "file";
11
+
12
+ /** A finding's context, redacted to a {@link ContextLevel} and secret-stripped. */
13
+ export interface RedactedContext {
14
+ level: ContextLevel;
15
+ meta: {
16
+ ruleId: string;
17
+ algorithm?: string;
18
+ severity: Severity;
19
+ hndl: boolean;
20
+ file: string;
21
+ line: number;
22
+ message: string;
23
+ };
24
+ /** Redacted source, or null at `metadata` level / for a `sensitive` finding. */
25
+ code: string | null;
26
+ /** True when key material was stripped from the context. */
27
+ redactedSecret: boolean;
28
+ }
29
+
30
+ /** An LLM triage verdict for a single finding (never suppresses it). The
31
+ * exposure/priority/rationale body is the {@link TriageAnnotation} that gets
32
+ * attached to the finding; `fingerprint` links it back to that finding. */
33
+ export interface TriageVerdict extends TriageAnnotation {
34
+ fingerprint: string;
35
+ }
36
+
37
+ /** A concrete proposed edit: the full new content for a single file. */
38
+ export interface Patch {
39
+ path: string;
40
+ /** Full replacement content for `path` after the fix. */
41
+ newContent: string;
42
+ ruleId: string;
43
+ source: "codemod" | "llm";
44
+ }
45
+
46
+ /** An LLM-proposed fix before it enters the deterministic pipeline. */
47
+ export interface FixProposal {
48
+ fingerprint: string;
49
+ path: string;
50
+ newContent: string;
51
+ explanation: string;
52
+ }
package/src/cache.ts ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Optional on-disk scan cache. Keyed by file content hash so an unchanged file
3
+ * reuses its previous findings instead of re-running every detector — the big
4
+ * win on large monorepos where most files don't change between runs.
5
+ *
6
+ * Invalidation is conservative and whole-cache: the cache stores a RULESET
7
+ * FINGERPRINT (tool version + the active detector ids + disabled rules). If any
8
+ * of those differ from the current run, the entire cache is discarded and
9
+ * rebuilt, so upgrading the tool or changing the rules never serves stale
10
+ * results. Per file, a content-hash mismatch re-scans just that file.
11
+ *
12
+ * The cache is opt-in (`ScanOptions.cacheFile`); with no cache file, scans are
13
+ * exactly as before. A corrupt / unreadable / wrong-version cache file is
14
+ * ignored (treated as empty), never fatal.
15
+ */
16
+ import { createHash } from "node:crypto";
17
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
18
+ import * as path from "node:path";
19
+
20
+ import type { Detector, Finding } from "./types.js";
21
+ import { VERSION } from "./version.js";
22
+
23
+ const CACHE_VERSION = 1;
24
+
25
+ /** One cached file: the content hash it was computed for, and its findings. */
26
+ export interface CacheEntry {
27
+ hash: string;
28
+ findings: Finding[];
29
+ }
30
+
31
+ interface CacheFileShape {
32
+ version: number;
33
+ ruleset: string;
34
+ entries: Record<string, CacheEntry>;
35
+ }
36
+
37
+ /** sha256 of a file's content (hex). */
38
+ export function hashContent(content: string): string {
39
+ return createHash("sha256").update(content, "utf8").digest("hex");
40
+ }
41
+
42
+ /**
43
+ * Fingerprint of everything that changes what a scan produces: the tool version,
44
+ * the active detector ids (sorted), and the disabled-rule set (sorted). A change
45
+ * to any of these invalidates the whole cache.
46
+ */
47
+ export function rulesetFingerprint(
48
+ detectors: readonly Detector[],
49
+ disabledRules: readonly string[] | undefined,
50
+ ): string {
51
+ const ids = detectors.map((d) => d.id).sort();
52
+ const disabled = [...(disabledRules ?? [])].sort();
53
+ return `v${VERSION}|d:${ids.join(",")}|x:${disabled.join(",")}`;
54
+ }
55
+
56
+ /**
57
+ * Load the cache file into a `Map<relPath, CacheEntry>`. Returns an empty map
58
+ * when the file is missing, unparseable, the wrong version, or was written for a
59
+ * different ruleset (in which case its entries are stale and must not be reused).
60
+ */
61
+ export async function loadCache(
62
+ cacheFile: string,
63
+ ruleset: string,
64
+ ): Promise<Map<string, CacheEntry>> {
65
+ let raw: string;
66
+ try {
67
+ raw = await readFile(cacheFile, "utf8");
68
+ } catch {
69
+ return new Map();
70
+ }
71
+ let parsed: CacheFileShape;
72
+ try {
73
+ parsed = JSON.parse(raw) as CacheFileShape;
74
+ } catch {
75
+ return new Map();
76
+ }
77
+ if (
78
+ parsed === null ||
79
+ typeof parsed !== "object" ||
80
+ parsed.version !== CACHE_VERSION ||
81
+ parsed.ruleset !== ruleset ||
82
+ typeof parsed.entries !== "object" ||
83
+ parsed.entries === null
84
+ ) {
85
+ return new Map();
86
+ }
87
+ const map = new Map<string, CacheEntry>();
88
+ for (const [file, entry] of Object.entries(parsed.entries)) {
89
+ if (entry && typeof entry.hash === "string" && Array.isArray(entry.findings)) {
90
+ map.set(file, entry);
91
+ }
92
+ }
93
+ return map;
94
+ }
95
+
96
+ /**
97
+ * Write the cache atomically (temp file + rename) so a concurrent reader never
98
+ * sees a half-written file. Errors are swallowed — a cache is an optimization,
99
+ * never a reason to fail a scan.
100
+ */
101
+ export async function saveCache(
102
+ cacheFile: string,
103
+ ruleset: string,
104
+ entries: Map<string, CacheEntry>,
105
+ ): Promise<void> {
106
+ const doc: CacheFileShape = {
107
+ version: CACHE_VERSION,
108
+ ruleset,
109
+ entries: Object.fromEntries(entries),
110
+ };
111
+ try {
112
+ await mkdir(path.dirname(cacheFile), { recursive: true });
113
+ const tmp = `${cacheFile}.tmp-${process.pid}`;
114
+ await writeFile(tmp, JSON.stringify(doc), "utf8");
115
+ const { rename } = await import("node:fs/promises");
116
+ await rename(tmp, cacheFile);
117
+ } catch {
118
+ // best effort
119
+ }
120
+ }
package/src/cbom.ts CHANGED
@@ -56,6 +56,32 @@ function isQuantumVulnerable(algorithm: AlgorithmFamily): boolean {
56
56
  return algorithm !== "unknown";
57
57
  }
58
58
 
59
+ /**
60
+ * Approximate CLASSICAL security strength (bits) of the common parameterisation
61
+ * of a classical family — RSA-2048 / DH-2048 ≈ 112-bit, 256-bit curves ≈ 128-bit,
62
+ * X448 ≈ 224-bit. (The QUANTUM level is 0 for all of these: Shor breaks them.)
63
+ * 0 when the family is unknown. Reported so a CBOM doesn't imply these primitives
64
+ * have zero classical strength today.
65
+ */
66
+ function classicalSecurityLevelFor(algorithm: AlgorithmFamily): number {
67
+ switch (algorithm) {
68
+ case "RSA":
69
+ case "DH":
70
+ case "DSA":
71
+ return 112;
72
+ case "ECDH":
73
+ case "ECDSA":
74
+ case "EdDSA":
75
+ case "X25519":
76
+ case "ECIES":
77
+ return 128;
78
+ case "X448":
79
+ return 224;
80
+ default:
81
+ return 0;
82
+ }
83
+ }
84
+
59
85
  /** Deterministic bom-ref for a (algorithm, primitive) asset key. */
60
86
  function bomRef(key: string): string {
61
87
  return `crypto:${createHash("sha256").update(key, "utf8").digest("hex").slice(0, 16)}`;
@@ -107,7 +133,7 @@ export function toCbom(result: ScanResult): CycloneDxBom {
107
133
  primitive: g.primitive,
108
134
  parameterSetIdentifier: g.algorithm,
109
135
  executionEnvironment: "software-plain-ram",
110
- classicalSecurityLevel: 0,
136
+ classicalSecurityLevel: classicalSecurityLevelFor(g.algorithm),
111
137
  nistQuantumSecurityLevel: 0,
112
138
  cryptoFunctions:
113
139
  g.primitive === "signature"
package/src/changed.ts CHANGED
@@ -53,11 +53,20 @@ export async function changedFiles(root: string, since?: string): Promise<string
53
53
  // than repo-root-relative, so `scan()` can join them against a sub-directory
54
54
  // root. Without it, a `--changed` scan of a subdirectory resolves nothing.
55
55
  if (since) {
56
- for (const f of toLines(
57
- await git(root, ["diff", "--name-only", "--relative", "--diff-filter=ACMR", since]),
58
- )) {
59
- out.add(f);
56
+ // A `git diff` against `since` returns null ONLY when the command failed —
57
+ // almost always an unknown ref/range (a typo like `--since maiin`). Surface
58
+ // it instead of silently scanning just the uncommitted files and passing CI.
59
+ const diff = await git(root, [
60
+ "diff",
61
+ "--name-only",
62
+ "--relative",
63
+ "--diff-filter=ACMR",
64
+ since,
65
+ ]);
66
+ if (diff === null) {
67
+ throw new Error(`--since: git could not diff against "${since}" (unknown ref or range).`);
60
68
  }
69
+ for (const f of toLines(diff)) out.add(f);
61
70
  }
62
71
 
63
72
  // Always include local uncommitted edits (staged + unstaged), filtered to ACMR.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Config-toggle codemod: mechanical, unambiguous fixes for insecure TLS config.
3
+ * Both target rules have a single correct replacement, so the fix is
4
+ * deterministic and clears the finding (verified by the pipeline's gate).
5
+ *
6
+ * - `tls-legacy-version` — bump a pinned TLS 1.0/1.1 to TLS 1.3.
7
+ * - `tls-reject-unauthorized` — turn certificate verification back on.
8
+ */
9
+ import type { Finding } from "../types.js";
10
+ import type { Patch } from "../agent-types.js";
11
+ import type { Codemod } from "./registry.js";
12
+
13
+ export const configToggleCodemod: Codemod = {
14
+ id: "config-toggle",
15
+ applies(finding: Finding): boolean {
16
+ return finding.ruleId === "tls-legacy-version" || finding.ruleId === "tls-reject-unauthorized";
17
+ },
18
+ apply(content: string, finding: Finding): Patch | null {
19
+ let out = content;
20
+ if (finding.ruleId === "tls-legacy-version") {
21
+ out = out
22
+ .replace(/((?:minVersion|maxVersion)\s*:\s*['"`])TLSv1(?:\.1)?(['"`])/g, "$1TLSv1.3$2")
23
+ .replace(/(secureProtocol\s*:\s*['"`])TLSv1(?:_1)?_method(['"`])/g, "$1TLSv1_3_method$2");
24
+ } else if (finding.ruleId === "tls-reject-unauthorized") {
25
+ out = out.replace(/(rejectUnauthorized\s*:\s*)false/g, "$1true");
26
+ }
27
+ if (out === content) return null;
28
+ return {
29
+ path: finding.location.file,
30
+ newContent: out,
31
+ ruleId: finding.ruleId,
32
+ source: "codemod",
33
+ };
34
+ },
35
+ };