@aimarket/warden 0.21.0 → 0.22.1

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 (212) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README-es.md +24 -7
  3. package/README-fr.md +24 -7
  4. package/README-ru.md +24 -7
  5. package/README-zh.md +24 -7
  6. package/README.md +24 -7
  7. package/dist/annotation-review.d.ts +10 -0
  8. package/dist/annotation-review.d.ts.map +1 -0
  9. package/dist/annotation-review.js +36 -0
  10. package/dist/annotation-review.js.map +1 -0
  11. package/dist/answer-gate.d.ts +94 -0
  12. package/dist/answer-gate.d.ts.map +1 -0
  13. package/dist/answer-gate.js +234 -0
  14. package/dist/answer-gate.js.map +1 -0
  15. package/dist/atomic-screen.d.ts.map +1 -1
  16. package/dist/classifier.d.ts +2 -2
  17. package/dist/classifier.d.ts.map +1 -1
  18. package/dist/claude-hook.js.map +1 -1
  19. package/dist/declare.d.ts.map +1 -1
  20. package/dist/declare.js.map +1 -1
  21. package/dist/flow-guard-answer.d.ts +30 -0
  22. package/dist/flow-guard-answer.d.ts.map +1 -0
  23. package/dist/flow-guard-answer.js +228 -0
  24. package/dist/flow-guard-answer.js.map +1 -0
  25. package/dist/flow-guard-classify.d.ts +14 -0
  26. package/dist/flow-guard-classify.d.ts.map +1 -0
  27. package/dist/flow-guard-classify.js +224 -0
  28. package/dist/flow-guard-classify.js.map +1 -0
  29. package/dist/flow-guard-constraints.d.ts +43 -0
  30. package/dist/flow-guard-constraints.d.ts.map +1 -0
  31. package/dist/flow-guard-constraints.js +69 -0
  32. package/dist/flow-guard-constraints.js.map +1 -0
  33. package/dist/flow-guard-decode.d.ts +17 -0
  34. package/dist/flow-guard-decode.d.ts.map +1 -0
  35. package/dist/flow-guard-decode.js +149 -0
  36. package/dist/flow-guard-decode.js.map +1 -0
  37. package/dist/flow-guard-destinations.d.ts +23 -0
  38. package/dist/flow-guard-destinations.d.ts.map +1 -0
  39. package/dist/flow-guard-destinations.js +158 -0
  40. package/dist/flow-guard-destinations.js.map +1 -0
  41. package/dist/flow-guard-fingerprints.d.ts +27 -0
  42. package/dist/flow-guard-fingerprints.d.ts.map +1 -0
  43. package/dist/flow-guard-fingerprints.js +164 -0
  44. package/dist/flow-guard-fingerprints.js.map +1 -0
  45. package/dist/flow-guard-fragments.d.ts +17 -0
  46. package/dist/flow-guard-fragments.d.ts.map +1 -0
  47. package/dist/flow-guard-fragments.js +122 -0
  48. package/dist/flow-guard-fragments.js.map +1 -0
  49. package/dist/flow-guard-instructions.d.ts +12 -0
  50. package/dist/flow-guard-instructions.d.ts.map +1 -0
  51. package/dist/flow-guard-instructions.js +36 -0
  52. package/dist/flow-guard-instructions.js.map +1 -0
  53. package/dist/flow-guard-labels.d.ts +10 -0
  54. package/dist/flow-guard-labels.d.ts.map +1 -0
  55. package/dist/flow-guard-labels.js +55 -0
  56. package/dist/flow-guard-labels.js.map +1 -0
  57. package/dist/flow-guard-leak.d.ts +29 -0
  58. package/dist/flow-guard-leak.d.ts.map +1 -0
  59. package/dist/flow-guard-leak.js +135 -0
  60. package/dist/flow-guard-leak.js.map +1 -0
  61. package/dist/flow-guard-paths.d.ts +21 -0
  62. package/dist/flow-guard-paths.d.ts.map +1 -0
  63. package/dist/flow-guard-paths.js +72 -0
  64. package/dist/flow-guard-paths.js.map +1 -0
  65. package/dist/flow-guard-policy.d.ts +15 -0
  66. package/dist/flow-guard-policy.d.ts.map +1 -0
  67. package/dist/flow-guard-policy.js +179 -0
  68. package/dist/flow-guard-policy.js.map +1 -0
  69. package/dist/flow-guard-post.d.ts +6 -0
  70. package/dist/flow-guard-post.d.ts.map +1 -0
  71. package/dist/flow-guard-post.js +131 -0
  72. package/dist/flow-guard-post.js.map +1 -0
  73. package/dist/flow-guard-pre.d.ts +13 -0
  74. package/dist/flow-guard-pre.d.ts.map +1 -0
  75. package/dist/flow-guard-pre.js +335 -0
  76. package/dist/flow-guard-pre.js.map +1 -0
  77. package/dist/flow-guard-screen.d.ts +85 -0
  78. package/dist/flow-guard-screen.d.ts.map +1 -0
  79. package/dist/flow-guard-screen.js +558 -0
  80. package/dist/flow-guard-screen.js.map +1 -0
  81. package/dist/flow-guard-state.d.ts +12 -0
  82. package/dist/flow-guard-state.d.ts.map +1 -0
  83. package/dist/flow-guard-state.js +129 -0
  84. package/dist/flow-guard-state.js.map +1 -0
  85. package/dist/flow-guard-text.d.ts +9 -0
  86. package/dist/flow-guard-text.d.ts.map +1 -0
  87. package/dist/flow-guard-text.js +46 -0
  88. package/dist/flow-guard-text.js.map +1 -0
  89. package/dist/flow-guard-types.d.ts +162 -0
  90. package/dist/flow-guard-types.d.ts.map +1 -0
  91. package/dist/flow-guard-types.js +2 -0
  92. package/dist/flow-guard-types.js.map +1 -0
  93. package/dist/flow-guard.d.ts +22 -394
  94. package/dist/flow-guard.d.ts.map +1 -1
  95. package/dist/flow-guard.js +27 -2522
  96. package/dist/flow-guard.js.map +1 -1
  97. package/dist/histor.js.map +1 -1
  98. package/dist/index.d.ts +4 -0
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +2 -0
  101. package/dist/index.js.map +1 -1
  102. package/dist/jcs.d.ts.map +1 -1
  103. package/dist/jcs.js.map +1 -1
  104. package/dist/mcp-client.d.ts +1 -2
  105. package/dist/mcp-client.d.ts.map +1 -1
  106. package/dist/mcp-client.js +2 -19
  107. package/dist/mcp-client.js.map +1 -1
  108. package/dist/mcp-rpc.js.map +1 -1
  109. package/dist/mcp-tools.d.ts.map +1 -1
  110. package/dist/mcp-tools.js.map +1 -1
  111. package/dist/mtl.d.ts +1 -1
  112. package/dist/mtl.d.ts.map +1 -1
  113. package/dist/mtl.js.map +1 -1
  114. package/dist/origin.d.ts.map +1 -1
  115. package/dist/pin-store.d.ts.map +1 -1
  116. package/dist/pin-store.js.map +1 -1
  117. package/dist/pinning.d.ts.map +1 -1
  118. package/dist/pinning.js.map +1 -1
  119. package/dist/public-address.d.ts +3 -0
  120. package/dist/public-address.d.ts.map +1 -0
  121. package/dist/public-address.js +58 -0
  122. package/dist/public-address.js.map +1 -0
  123. package/dist/review-diff.d.ts +3 -3
  124. package/dist/review-diff.d.ts.map +1 -1
  125. package/dist/review-diff.js +3 -5
  126. package/dist/review-diff.js.map +1 -1
  127. package/dist/sandbox.d.ts.map +1 -1
  128. package/dist/sanitize.js.map +1 -1
  129. package/dist/scan-lock.d.ts.map +1 -1
  130. package/dist/scan-lock.js.map +1 -1
  131. package/dist/scan.d.ts.map +1 -1
  132. package/dist/scan.js.map +1 -1
  133. package/dist/static-scan.d.ts.map +1 -1
  134. package/dist/static-scan.js.map +1 -1
  135. package/dist/task-adapter.d.ts.map +1 -1
  136. package/dist/task-adapter.js.map +1 -1
  137. package/dist/threat-feed.d.ts.map +1 -1
  138. package/dist/threat-feed.js.map +1 -1
  139. package/dist/tool-list.js.map +1 -1
  140. package/dist/toxic-flow.js.map +1 -1
  141. package/dist/wrap-cli.d.ts +1 -1
  142. package/dist/wrap-cli.d.ts.map +1 -1
  143. package/dist/wrap-cli.js +2 -1
  144. package/dist/wrap-cli.js.map +1 -1
  145. package/dist/wrap-screen.js.map +1 -1
  146. package/dist/wrap-state.d.ts +3 -1
  147. package/dist/wrap-state.d.ts.map +1 -1
  148. package/dist/wrap-state.js +18 -3
  149. package/dist/wrap-state.js.map +1 -1
  150. package/dist/wrap-wire.d.ts.map +1 -1
  151. package/dist/wrap-wire.js.map +1 -1
  152. package/dist/wrap.d.ts.map +1 -1
  153. package/dist/wrap.js +45 -7
  154. package/dist/wrap.js.map +1 -1
  155. package/docs/flow-guard-answer-gate.es.md +53 -0
  156. package/docs/flow-guard-answer-gate.fr.md +53 -0
  157. package/docs/flow-guard-answer-gate.md +86 -0
  158. package/docs/flow-guard-answer-gate.ru.md +53 -0
  159. package/docs/flow-guard-answer-gate.zh.md +53 -0
  160. package/docs/flow-guard-constraint-evidence.es.md +76 -0
  161. package/docs/flow-guard-constraint-evidence.fr.md +76 -0
  162. package/docs/flow-guard-constraint-evidence.md +78 -0
  163. package/docs/flow-guard-constraint-evidence.ru.md +78 -0
  164. package/docs/flow-guard-constraint-evidence.zh.md +76 -0
  165. package/docs/flow-guard-final-witness-2026-10-26.es.md +24 -0
  166. package/docs/flow-guard-final-witness-2026-10-26.fr.md +24 -0
  167. package/docs/flow-guard-final-witness-2026-10-26.md +24 -0
  168. package/docs/flow-guard-final-witness-2026-10-26.ru.md +24 -0
  169. package/docs/flow-guard-final-witness-2026-10-26.zh.md +24 -0
  170. package/docs/flow-guard-fresh-rechecks-2026-10-26.es.md +39 -0
  171. package/docs/flow-guard-fresh-rechecks-2026-10-26.fr.md +39 -0
  172. package/docs/flow-guard-fresh-rechecks-2026-10-26.md +39 -0
  173. package/docs/flow-guard-fresh-rechecks-2026-10-26.ru.md +39 -0
  174. package/docs/flow-guard-fresh-rechecks-2026-10-26.zh.md +39 -0
  175. package/docs/flow-guard-model-comparison-2026-10-25.es.md +109 -0
  176. package/docs/flow-guard-model-comparison-2026-10-25.fr.md +109 -0
  177. package/docs/flow-guard-model-comparison-2026-10-25.md +109 -0
  178. package/docs/flow-guard-model-comparison-2026-10-25.ru.md +109 -0
  179. package/docs/flow-guard-model-comparison-2026-10-25.zh.md +109 -0
  180. package/docs/flow-guard-prospective-holdout-2026-10-26.es.md +30 -0
  181. package/docs/flow-guard-prospective-holdout-2026-10-26.fr.md +30 -0
  182. package/docs/flow-guard-prospective-holdout-2026-10-26.md +44 -0
  183. package/docs/flow-guard-prospective-holdout-2026-10-26.ru.md +44 -0
  184. package/docs/flow-guard-prospective-holdout-2026-10-26.zh.md +30 -0
  185. package/docs/flow-guard-review-factors-2026-10-26.es.md +41 -0
  186. package/docs/flow-guard-review-factors-2026-10-26.fr.md +41 -0
  187. package/docs/flow-guard-review-factors-2026-10-26.md +45 -0
  188. package/docs/flow-guard-review-factors-2026-10-26.ru.md +45 -0
  189. package/docs/flow-guard-review-factors-2026-10-26.zh.md +41 -0
  190. package/docs/flow-guard-structured-model-2026-10-26.es.md +35 -0
  191. package/docs/flow-guard-structured-model-2026-10-26.fr.md +35 -0
  192. package/docs/flow-guard-structured-model-2026-10-26.md +44 -0
  193. package/docs/flow-guard-structured-model-2026-10-26.ru.md +46 -0
  194. package/docs/flow-guard-structured-model-2026-10-26.zh.md +35 -0
  195. package/docs/flow-guard.es.md +54 -4
  196. package/docs/flow-guard.fr.md +54 -4
  197. package/docs/flow-guard.md +61 -4
  198. package/docs/flow-guard.ru.md +60 -4
  199. package/docs/flow-guard.zh.md +54 -4
  200. package/docs/scan-limits.es.md +39 -0
  201. package/docs/scan-limits.fr.md +39 -0
  202. package/docs/scan-limits.md +39 -0
  203. package/docs/scan-limits.ru.md +39 -0
  204. package/docs/scan-limits.zh.md +39 -0
  205. package/docs/scan.es.md +2 -0
  206. package/docs/scan.fr.md +2 -0
  207. package/docs/scan.md +2 -0
  208. package/docs/scan.ru.md +2 -0
  209. package/docs/scan.zh.md +2 -0
  210. package/examples/answer-gate-host.mjs +33 -0
  211. package/native/macos/Sources/WardenDesktop/NativeSurface.swift +1 -1
  212. package/package.json +6 -5
@@ -0,0 +1,164 @@
1
+ /** Flow guard: fingerprints of what the session read (private text, personal numbers, credentials) and the secret formats it knows. */
2
+ import { createHash } from 'node:crypto';
3
+ import { MAX_TEXT } from './flow-guard-text.js';
4
+ export const WINDOW = 20;
5
+ const STRIDE = 4;
6
+ // Credentials are fingerprinted again in short windows of their letters and digits only, so a piece of a key, or the
7
+ // key with separators between its characters, is still recognised. The sizes are the smallest at which none of 480
8
+ // made-up keys in eight formats matched anywhere in 100 million letters and digits of ordinary text
9
+ // (scripts/redteam/window-collisions.mjs): 8 for keys of letters and digits, 10 for hex keys, whose characters carry
10
+ // half the information and which share their alphabet with every hash and address in the text around them.
11
+ const TOKEN_WINDOW = 8;
12
+ const HEX_TOKEN_WINDOW = 10;
13
+ export const TOKEN_WINDOWS = [TOKEN_WINDOW, HEX_TOKEN_WINDOW];
14
+ const NORMALIZED_CREDENTIAL_PREFIX = /^(?:[rs]k(?:live|test)|skantapi[0-9]+|skant|skproj|sk|gh[pousr]|githubpat|glpat|npm|xox[abprs]|akia|asia|aiza)/;
15
+ // Text every key of a kind shares, so it is no piece of any one key: the RSA, EC and signature algorithm
16
+ // identifiers of DER keys and certificates, in letters and digits.
17
+ export const KEY_STRUCTURE = ['anbgkqhkig9w0baqefaa', 'a0gcsqgsib3dqebcwua', 'a0gcsqgsib3dqebbquaa', 'byqgsm49ageg', 'ccqgsm49aweh', 'bsubbaaiq', 'mcowbqydk2vw', 'mcowbqydk2vu'];
18
+ export function normalizeText(s) {
19
+ return s.normalize('NFKC').toLowerCase().replace(/\s+/g, '');
20
+ }
21
+ export function fpHash(window) {
22
+ return createHash('sha256').update(window).digest('hex').slice(0, 12);
23
+ }
24
+ /** Windows of the private text at every STRIDE-th position: any leaked run of WINDOW+STRIDE-1 normalized chars hits one. */
25
+ export function fingerprints(text) {
26
+ const n = normalizeText(text.slice(0, MAX_TEXT));
27
+ const out = new Set();
28
+ for (let i = 0; i + WINDOW <= n.length; i += STRIDE) {
29
+ const w = n.slice(i, i + WINDOW);
30
+ if (/^(.)\1+$/.test(w) || /^[0-9]+$/.test(w))
31
+ continue;
32
+ out.add(fpHash(w));
33
+ }
34
+ return [...out];
35
+ }
36
+ // Short structured values a 20-character window misses: card, passport, account and phone numbers.
37
+ // Words are split on whitespace; consecutive words that carry digits ("DE89 3704 0044", "+1 (555) 123-4567")
38
+ // join into one value, and a word without digits ends it.
39
+ const DATE_LIKE = /^(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{0,6}$/;
40
+ export function piiTokens(text) {
41
+ const out = new Set();
42
+ const flush = (group) => {
43
+ const t = group.join('').replace(/[^a-z0-9]/gi, '').toLowerCase();
44
+ const digits = (t.match(/\d/g) || []).length;
45
+ if (t.length < 8 || t.length > 34 || digits < 5 || DATE_LIKE.test(t))
46
+ return;
47
+ if (/^\d+$/.test(t) && t.length < 9)
48
+ return;
49
+ out.add(t);
50
+ };
51
+ let group = [];
52
+ for (const word of text.slice(0, MAX_TEXT).split(/\s+/)) {
53
+ const bare = word.replace(/^[^A-Za-z0-9+(]+|[^A-Za-z0-9)]+$/g, '');
54
+ if (/\d/.test(bare) && /^[A-Za-z0-9+()./-]+$/.test(bare)) {
55
+ group.push(bare);
56
+ continue;
57
+ }
58
+ if (group.length) {
59
+ flush(group);
60
+ group = [];
61
+ }
62
+ }
63
+ if (group.length)
64
+ flush(group);
65
+ return [...out];
66
+ }
67
+ const SECRET_PATTERNS = [
68
+ ['private key', /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
69
+ ['AWS access key', /\b(AKIA|ASIA)[0-9A-Z]{16}\b/],
70
+ ['GitHub token', /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b|\bgithub_pat_[A-Za-z0-9_]{60,}/],
71
+ ['GitLab token', /\bglpat-[A-Za-z0-9_-]{20,}/],
72
+ ['Slack token', /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
73
+ ['npm token', /\bnpm_[A-Za-z0-9]{36}\b/],
74
+ ['API key', /\bsk-(ant-)?[A-Za-z0-9_-]{20,}/],
75
+ ['Google API key', /\bAIza[0-9A-Za-z_-]{35}\b/],
76
+ ];
77
+ export function secretPattern(texts) {
78
+ for (const t of texts)
79
+ for (const [name, re] of SECRET_PATTERNS)
80
+ if (re.test(t))
81
+ return name;
82
+ return undefined;
83
+ }
84
+ // Cyrillic and Greek letters that look like Latin ones: a key retyped with them is the same key to a reader.
85
+ export const CONFUSABLE = {
86
+ а: 'a', в: 'b', е: 'e', ё: 'e', к: 'k', м: 'm', н: 'h', о: 'o', р: 'p', с: 'c', т: 't', у: 'y', х: 'x', і: 'i', ј: 'j', ѕ: 's', ԁ: 'd', ԛ: 'q', ԝ: 'w',
87
+ α: 'a', β: 'b', ε: 'e', η: 'n', ι: 'i', κ: 'k', ν: 'v', ο: 'o', ρ: 'p', τ: 't', υ: 'u', χ: 'x', γ: 'y', ζ: 'z',
88
+ };
89
+ /** Letters and digits only, lower-cased: separators, case, spacing and look-alike letters do not hide a key. */
90
+ export function alnum(s) {
91
+ return s.normalize('NFKC').toLowerCase().replace(/[^a-z0-9]/g, ch => CONFUSABLE[ch] ?? '');
92
+ }
93
+ function entropy(s) {
94
+ const counts = new Map();
95
+ for (const ch of s)
96
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
97
+ let h = 0;
98
+ for (const n of counts.values())
99
+ h -= (n / s.length) * Math.log2(n / s.length);
100
+ return h;
101
+ }
102
+ const TOKEN_RUN = /[A-Za-z0-9+/_-]{16,}={0,2}/g;
103
+ /** Key-like values in private text: every random-looking run of a credential file, and every well-known
104
+ * secret format wherever it was read. Words, paths and host names are not random enough to count. */
105
+ export function secretValues(text, credentialFile) {
106
+ const out = new Set();
107
+ const t = text.slice(0, MAX_TEXT);
108
+ const add = (raw) => {
109
+ // A base64 JSON object (a JWT's header or claims) is readable by anyone who holds the token; its signature,
110
+ // the run after the next dot, is the secret part and is kept.
111
+ if (raw.startsWith('eyJ'))
112
+ return;
113
+ const a = alnum(raw);
114
+ if (a.length >= 16 && /\d/.test(a) && /[a-z]/.test(a) && entropy(a) >= 3.5)
115
+ out.add(raw);
116
+ };
117
+ if (credentialFile)
118
+ for (const m of t.matchAll(TOKEN_RUN))
119
+ add(m[0]);
120
+ for (const [, re] of SECRET_PATTERNS)
121
+ for (const m of t.matchAll(new RegExp(re.source, 'g')))
122
+ add(m[0]);
123
+ return [...out];
124
+ }
125
+ /** Normalized credentials for the original strong-window check. Raw values exist only transiently. */
126
+ export function secretTokens(text, credentialFile) {
127
+ return [...new Set(secretValues(text, credentialFile).map(alnum))];
128
+ }
129
+ /** Known providers' alphabetic payload prefixes are still secret: do not skip them merely because the first
130
+ * digit comes late. Keep every previously indexed window. Generic values retain the old conservative start:
131
+ * public word-based environment values ending in a year must not gain new hard-denial substrings. */
132
+ export function tokenFingerprintStart(a) {
133
+ const leading = /^[a-z]*/.exec(a)[0].length;
134
+ if (/^(mii|mig|mhc|mc4|mco)/.test(a))
135
+ return Math.max(48, leading);
136
+ const prefix = NORMALIZED_CREDENTIAL_PREFIX.exec(a)?.[0];
137
+ return prefix ? Math.min(leading, prefix.length) : leading;
138
+ }
139
+ /** Windows hold both a letter and a digit; alphabetic-only words are not strong credential evidence.
140
+ * Public format/DER structure is excluded. The 8/10-character thresholds and stored hash format are unchanged. */
141
+ export function tokenFingerprints(tokens) {
142
+ const out = new Set();
143
+ for (const a of tokens) {
144
+ const size = /^[0-9a-f]+$/.test(a) ? HEX_TOKEN_WINDOW : TOKEN_WINDOW;
145
+ const start = tokenFingerprintStart(a);
146
+ for (let i = start; i + size <= a.length; i++) {
147
+ const w = a.slice(i, i + size);
148
+ if (/\d/.test(w) && /[a-z]/.test(w) && !KEY_STRUCTURE.some(k => k.includes(w)))
149
+ out.add(fpHash(w));
150
+ }
151
+ }
152
+ return [...out];
153
+ }
154
+ /** Every window of letters and digits of both token sizes, forwards and backwards: what a call is checked by. */
155
+ export function tokenWindows(text) {
156
+ const out = [];
157
+ for (const a of [alnum(text), alnum([...text].reverse().join(''))]) {
158
+ for (const size of TOKEN_WINDOWS)
159
+ for (let i = 0; i + size <= a.length; i++)
160
+ out.push(fpHash(a.slice(i, i + size)));
161
+ }
162
+ return out;
163
+ }
164
+ //# sourceMappingURL=flow-guard-fingerprints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-fingerprints.js","sourceRoot":"","sources":["../src/flow-guard-fingerprints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { FlowState, FragmentRecord } from './flow-guard-types.js';
2
+ export declare const MAX_FRAGMENT_INDEX = 16384;
3
+ export declare const MAX_FRAGMENT_SEEN = 256;
4
+ export declare const fragmentKey: (r: FragmentRecord) => string;
5
+ export declare const SHORT_SIZES: readonly [5, 6, 7];
6
+ /** Weak evidence asks before its first transmission, never silently learns by sending it.
7
+ * Five-char windows require upper/lower/digit classes in the source, while 6/7-char atoms may be words.
8
+ * Exact prefix-boundary slices cover chunks that already contain payload but no full six-char window.
9
+ * Only hash/offset/size metadata is persisted; public format and DER structure alone do not count. */
10
+ export declare function fragmentFingerprints(tokens: readonly string[], limit?: number): FragmentRecord[];
11
+ /** Must run under the session lock when remembering evidence. Audit never consumes or whitelists a match. */
12
+ export declare function checkFragments(input: Record<string, unknown>, state: FlowState, hasLegacyTokens: boolean, remember: boolean): {
13
+ secret?: string;
14
+ review?: boolean;
15
+ unavailable?: boolean;
16
+ };
17
+ //# sourceMappingURL=flow-guard-fragments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-fragments.d.ts","sourceRoot":"","sources":["../src/flow-guard-fragments.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMvE,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAGrC,eAAO,MAAM,WAAW,MAAO,cAAc,WAA8F,CAAC;AAG5I,eAAO,MAAM,WAAW,YAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;AAE9C;;;sGAGsG;AACtG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,SAAqB,GAAG,cAAc,EAAE,CA6B5G;AA+BD,6GAA6G;AAC7G,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAwB1L"}
@@ -0,0 +1,122 @@
1
+ /** Flow guard: short fragments of credentials, kept as hashes only, so a key sent in pieces is still recognised. */
2
+ import { createHash } from 'node:crypto';
3
+ import { strings } from './flow-guard-text.js';
4
+ import { CONFUSABLE, KEY_STRUCTURE, alnum } from './flow-guard-fingerprints.js';
5
+ import { scanCandidates } from './flow-guard-decode.js';
6
+ const FRAGMENT_SIZE = 6;
7
+ export const MAX_FRAGMENT_INDEX = 16_384;
8
+ export const MAX_FRAGMENT_SEEN = 256;
9
+ const fragmentHash = (s) => createHash('sha256').update(`warden-fragment-v1:${s}`).digest('hex').slice(0, 32);
10
+ export const fragmentKey = (r) => `${r.group}:${r.offset}:${r.size ?? FRAGMENT_SIZE}:${r.edge ? 'edge' : 'atom'}:${r.hash}`;
11
+ const CREDENTIAL_PREFIX = /^(?:[rs]k_(?:live|test)_|sk-ant-api[0-9]+-|sk-ant-|sk-proj-|sk-|gh[pousr]_|github_pat_|glpat-|npm_|xox[abprs]-|AKIA|ASIA|AIza)/i;
12
+ const fragmentVisible = (s) => s.normalize('NFKC').toLowerCase().replace(/[^a-z0-9]/g, ch => CONFUSABLE[ch] ?? ch);
13
+ export const SHORT_SIZES = [5, 6, 7];
14
+ /** Weak evidence asks before its first transmission, never silently learns by sending it.
15
+ * Five-char windows require upper/lower/digit classes in the source, while 6/7-char atoms may be words.
16
+ * Exact prefix-boundary slices cover chunks that already contain payload but no full six-char window.
17
+ * Only hash/offset/size metadata is persisted; public format and DER structure alone do not count. */
18
+ export function fragmentFingerprints(tokens, limit = MAX_FRAGMENT_INDEX) {
19
+ const out = [];
20
+ for (const raw of new Set(tokens)) {
21
+ const a = alnum(raw), prefix = CREDENTIAL_PREFIX.exec(raw)?.[0];
22
+ const normPrefix = /^(?:[rs]k(?:live|test)|skantapi[0-9]+|skant|skproj|sk|gh[pousr]|githubpat|glpat|npm|xox[abprs]|akia|asia|aiza)/.exec(a)?.[0];
23
+ const start = /^(mii|mig|mhc|mc4|mco)/.test(a) ? Math.max(48, /^[a-z]*/.exec(a)[0].length) : normPrefix?.length ?? /^[a-z]*/.exec(a)[0].length;
24
+ const group = fragmentHash(`key:${a}`);
25
+ // Preserve case only transiently to avoid indexing every short hex/hash or ordinary word.
26
+ const caseful = raw.normalize('NFKC').replace(/[^a-z0-9]/gi, '');
27
+ for (const size of SHORT_SIZES)
28
+ for (let offset = start; offset + size <= a.length; offset++) {
29
+ const piece = a.slice(offset, offset + size), original = caseful.slice(offset, offset + size);
30
+ if (size < FRAGMENT_SIZE && !(/[A-Z]/.test(original) && /[a-z]/.test(original) && /[0-9]/.test(original)))
31
+ continue;
32
+ if (/^(.)\1+$/.test(piece) || KEY_STRUCTURE.some(k => k.includes(piece)))
33
+ continue;
34
+ out.push({ group, hash: fragmentHash(piece), offset, size });
35
+ if (out.length > limit)
36
+ return out;
37
+ }
38
+ if (prefix)
39
+ for (let offset = 0; offset < prefix.length; offset++) {
40
+ // Require a real format delimiter, or the entire delimiter-free prefix (AWS/Google).
41
+ const suffix = prefix.slice(offset);
42
+ if (offset !== 0 && (!/[_-]/.test(suffix) || !/[a-z]/i.test(suffix)))
43
+ continue;
44
+ for (let length = 1; length <= 7 && prefix.length + length <= raw.length; length++) {
45
+ const piece = fragmentVisible(raw.slice(offset, prefix.length + length));
46
+ if (piece.length < 5)
47
+ continue;
48
+ out.push({ group, hash: fragmentHash(`edge:${piece}`), offset, size: piece.length, edge: true });
49
+ if (out.length > limit)
50
+ return out;
51
+ }
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ /** Full isolated atoms, not every substring of long ordinary hashes. Structured encoded candidates are included. */
57
+ function fragmentAtoms(input) {
58
+ const hashes = new Set(), scan = scanCandidates(strings(input));
59
+ if (scan.incomplete)
60
+ return { hashes, incomplete: true };
61
+ for (const text of scan.values) {
62
+ const visible = fragmentVisible(text);
63
+ for (const run of visible.matchAll(/\b(?:[a-z0-9][\t ]+){4,6}[a-z0-9]\b/g)) {
64
+ const atom = alnum(run[0]);
65
+ hashes.add(fragmentHash(atom));
66
+ hashes.add(fragmentHash([...atom].reverse().join('')));
67
+ }
68
+ for (const m of visible.matchAll(/[a-z0-9+/_-]+/g)) {
69
+ const raw = m[0];
70
+ if (raw.length <= 64)
71
+ hashes.add(fragmentHash(`edge:${raw}`));
72
+ const joined = alnum(raw);
73
+ if (/^[a-z0-9]{5,7}$/.test(joined)) {
74
+ hashes.add(fragmentHash(joined));
75
+ hashes.add(fragmentHash([...joined].reverse().join('')));
76
+ }
77
+ // Separators delimit atoms; a long uninterrupted hash is never scanned with weak windows.
78
+ for (const atom of raw.matchAll(/[a-z0-9]+/g))
79
+ if (/^[a-z0-9]{5,7}$/.test(atom[0])) {
80
+ hashes.add(fragmentHash(atom[0]));
81
+ hashes.add(fragmentHash([...atom[0]].reverse().join('')));
82
+ }
83
+ }
84
+ }
85
+ return { hashes, incomplete: scan.incomplete };
86
+ }
87
+ /** Must run under the session lock when remembering evidence. Audit never consumes or whitelists a match. */
88
+ export function checkFragments(input, state, hasLegacyTokens, remember) {
89
+ const f = state.fragments;
90
+ if (!f)
91
+ return hasLegacyTokens ? { unavailable: true } : {};
92
+ if (f.v !== 2 || f.full)
93
+ return { unavailable: true };
94
+ if (!f.index.length)
95
+ return {};
96
+ const { hashes, incomplete } = fragmentAtoms(input);
97
+ const matches = f.index.filter(r => hashes.has(r.hash));
98
+ if (!matches.length)
99
+ return incomplete ? { unavailable: true } : {};
100
+ const seen = new Map(f.seen.map(r => [fragmentKey(r), r]));
101
+ for (const r of matches)
102
+ seen.set(fragmentKey(r), r);
103
+ const all = [...seen.values()];
104
+ const offsets = new Map();
105
+ for (const r of all) {
106
+ if (r.edge || r.size !== FRAGMENT_SIZE)
107
+ continue;
108
+ const key = `${r.group}:${r.offset}`, at = offsets.get(key) ?? new Set();
109
+ at.add(r.hash);
110
+ offsets.set(key, at);
111
+ }
112
+ const paired = all.some(r => !r.edge && r.size === FRAGMENT_SIZE && [...(offsets.get(`${r.group}:${r.offset + FRAGMENT_SIZE}`) ?? [])].some(h => h !== r.hash));
113
+ if (remember) {
114
+ f.seen = all.slice(0, MAX_FRAGMENT_SEEN);
115
+ if (all.length > MAX_FRAGMENT_SEEN)
116
+ f.full = true;
117
+ }
118
+ if (paired)
119
+ return { secret: 'adjacent short fragments of a credential this session read' };
120
+ return { review: true, ...(incomplete || all.length > MAX_FRAGMENT_SEEN ? { unavailable: true } : {}) };
121
+ }
122
+ //# sourceMappingURL=flow-guard-fragments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-fragments.js","sourceRoot":"","sources":["../src/flow-guard-fragments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export declare const INJECTION_HINTS: RegExp;
2
+ export interface InstructionSignal {
3
+ kind: string;
4
+ start: number;
5
+ end: number;
6
+ }
7
+ export interface InstructionSignals {
8
+ matches: InstructionSignal[];
9
+ limited: boolean;
10
+ }
11
+ export declare function instructionSignals(text: string): InstructionSignals;
12
+ //# sourceMappingURL=flow-guard-instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-instructions.d.ts","sourceRoot":"","sources":["../src/flow-guard-instructions.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,eAAe,QAA2E,CAAC;AACxG,MAAM,WAAW,iBAAiB;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAC/E,MAAM,WAAW,kBAAkB;IAAG,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAEtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAenE"}
@@ -0,0 +1,36 @@
1
+ /** Bounded, fallible signals in source text. Never an authority or a semantic verdict. */
2
+ import { MAX_TEXT } from './flow-guard-text.js';
3
+ const LEGACY = /ignore (all |any |the )?(previous|prior|above) (instructions|messages)|disregard (the|your|all) (previous|prior|system)|<\/?(important|system|instructions?|critical[_-]?requirement)\b|do not (tell|show|inform|mention to) the user|you are now|new instructions:|assistant must|before (any|every) (reply|response), (call|run|use)/i;
4
+ // Require instruction syntax or an authority claim together with a nearby directive.
5
+ // Ordinary amenities, preferences and approvals alone are not instruction signals.
6
+ const RULES = [
7
+ ['legacy', LEGACY],
8
+ ['role-boundary', /\[im_start\][ \t]*(?:system|developer|assistant)\b|<\|start_header_id\|>[ \t]*(?:system|developer|assistant)\b|<(?:system|developer|assistant)(?:[ \t][^>\n]{0,80})?>/i],
9
+ ['relayed-preference', /(?:verified|latest|newer|updated|final)\s+(?:customer|user)\s+(?:update|preference|request)[^\n]{0,360}?\b(?:return|choose|select|recommend|put)\b|(?:customer|user)\s+(?:update|preference|request)[^\n]{0,160}?\b(?:newer than|supersedes|overrides)\b[^\n]{0,160}?\b(?:return|choose|select|recommend|put)\b/i],
10
+ ['claimed-prior-decision', /(?:cached|previous|prior)\s+(?:assistant\s+)?(?:analysis|reasoning|decision)[^\n]{0,360}?\b(?:continue|return|choose|select|recommend)\b|(?:customer|user)\s+(?:already|previously)\s+(?:approved|selected|chose)[^\n]{0,240}?\b(?:continue|return|choose|select|recommend)\b/i],
11
+ ['private-rubric', /(?:internal|private|hidden)\s+(?:evaluation\s+)?(?:rubric|evaluator|scoring|benchmark)[^\n]{0,240}?\b(?:must\s+(?:choose|select|recommend)|expects?\b|(?:return|put)\b[^\n]{0,80}?\b(?:id|answer|response))/i],
12
+ ['concealed-directive', /do not\s+(?:reveal|disclose|mention|describe)\s+(?:this|the|our)\s+(?:(?:internal|private|scoring|hidden|selection)\s+){0,2}(?:instruction|note|rubric|handoff|relay)\b|without\s+(?:asking for confirmation or\s+)?(?:mentioning|describing)\s+(?:this|the)\s+(?:relay|handoff|instruction)\b/i],
13
+ ['relayed-preference-ru', /(?:последнее|обновл[её]нное|новое)\s+предпочтение\s+(?:пользователя|клиента)[^\n]{0,300}?(?:выберите|верните|рекомендуйте)|не\s+(?:упоминайте|раскрывайте)\s+(?:это|данное)\s+(?:служебное\s+)?(?:сообщение|указание|инструкцию)/i],
14
+ ];
15
+ // Kept non-global for callers using .test(). It has no mutable lastIndex state.
16
+ export const INJECTION_HINTS = new RegExp(RULES.map(([, rule]) => `(?:${rule.source})`).join('|'), 'i');
17
+ export function instructionSignals(text) {
18
+ const input = text.slice(0, MAX_TEXT);
19
+ const matches = [];
20
+ // Upstream text extraction may already have stopped at this budget. Treat an
21
+ // exactly full buffer conservatively as limited rather than declaring coverage.
22
+ let limited = text.length >= MAX_TEXT;
23
+ for (const [kind, rule] of RULES) {
24
+ let count = 0;
25
+ for (const match of input.matchAll(new RegExp(rule.source, 'gi'))) {
26
+ if (count++ === 16) {
27
+ limited = true;
28
+ break;
29
+ }
30
+ matches.push({ kind, start: match.index, end: match.index + match[0].length });
31
+ }
32
+ }
33
+ matches.sort((a, b) => a.start - b.start || a.end - b.end);
34
+ return { matches, limited };
35
+ }
36
+ //# sourceMappingURL=flow-guard-instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-instructions.js","sourceRoot":"","sources":["../src/flow-guard-instructions.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,MAAM,MAAM,GAAG,yUAAyU,CAAC;AAEzV,qFAAqF;AACrF,mFAAmF;AACnF,MAAM,KAAK,GAAG;IACZ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAClB,CAAC,eAAe,EAAE,wKAAwK,CAAC;IAC3L,CAAC,oBAAoB,EAAE,kTAAkT,CAAC;IAC1U,CAAC,wBAAwB,EAAE,gRAAgR,CAAC;IAC5S,CAAC,gBAAgB,EAAE,8MAA8M,CAAC;IAClO,CAAC,qBAAqB,EAAE,iSAAiS,CAAC;IAC1T,CAAC,uBAAuB,EAAE,mOAAmO,CAAC;CACtP,CAAC;AAEX,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,MAAM,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAIxG,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACtC,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,6EAA6E;IAC7E,gFAAgF;IAChF,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YAClE,IAAI,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;gBAAC,OAAO,GAAG,IAAI,CAAC;gBAAC,MAAM;YAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAM,EAAE,GAAG,EAAE,KAAK,CAAC,KAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { FlowLabel } from './flow-guard-types.js';
2
+ /** MCP tools that sessions used without an operator label, with the guard's guess and the sessions that used them. */
3
+ export declare function unlabeledTools(env?: NodeJS.ProcessEnv): Array<{
4
+ tool: string;
5
+ guessed: FlowLabel[];
6
+ sessions: number;
7
+ }>;
8
+ /** `warden-mcp flow-labels`: the unlabeled tools, and a labels block to review and paste into the policy file. */
9
+ export declare function flowLabelsReport(env?: NodeJS.ProcessEnv): string;
10
+ //# sourceMappingURL=flow-guard-labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-labels.d.ts","sourceRoot":"","sources":["../src/flow-guard-labels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,uBAAuB,CAAC;AAKlE,sHAAsH;AACtH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBpI;AAED,kHAAkH;AAClH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAgB7E"}
@@ -0,0 +1,55 @@
1
+ /** Flow guard: `warden-mcp flow-labels`, the MCP tools sessions used without an operator label. */
2
+ import { readdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { flowPaths, stateDir } from './flow-guard-paths.js';
5
+ import { LABELS } from './flow-guard-policy.js';
6
+ import { readFlowState, safeToolName } from './flow-guard-state.js';
7
+ /** MCP tools that sessions used without an operator label, with the guard's guess and the sessions that used them. */
8
+ export function unlabeledTools(env = process.env) {
9
+ const dir = flowPaths(stateDir(env), 'x').dir;
10
+ const seen = new Map();
11
+ let files = [];
12
+ try {
13
+ files = readdirSync(dir).filter(f => /^[0-9a-f]{64}\.json$/.test(f));
14
+ }
15
+ catch {
16
+ return [];
17
+ }
18
+ for (const f of files) {
19
+ let s;
20
+ try {
21
+ s = readFlowState(join(dir, f));
22
+ }
23
+ catch {
24
+ continue;
25
+ }
26
+ for (const [tool, labels] of Object.entries(s.unlabeled ?? {})) {
27
+ const e = seen.get(tool) ?? { guessed: new Set(), sessions: 0 };
28
+ for (const l of labels)
29
+ e.guessed.add(l);
30
+ e.sessions++;
31
+ seen.set(tool, e);
32
+ }
33
+ }
34
+ return [...seen].map(([tool, e]) => ({ tool, guessed: LABELS.filter(l => e.guessed.has(l)), sessions: e.sessions })).sort((a, b) => b.sessions - a.sessions || a.tool.localeCompare(b.tool));
35
+ }
36
+ /** `warden-mcp flow-labels`: the unlabeled tools, and a labels block to review and paste into the policy file. */
37
+ export function flowLabelsReport(env = process.env) {
38
+ const tools = unlabeledTools(env);
39
+ const policyPath = env.WARDEN_FLOW_POLICY || join(stateDir(env), 'flow-policy.json');
40
+ if (!tools.length)
41
+ return `No MCP tool without a label has been used in a recorded session. Policy file: ${policyPath}\n`;
42
+ const lines = tools.map(t => ` ${safeToolName(t.tool).padEnd(48)} guessed: ${t.guessed.join(', ') || '-'} sessions: ${t.sessions}`);
43
+ const labels = Object.fromEntries(tools.map(t => [t.tool, t.guessed]));
44
+ return [
45
+ `MCP tools used without an operator label (${tools.length}). The guard guessed from names and arguments; a guess only adds caution.`,
46
+ ...lines,
47
+ '',
48
+ `Check each guess against what the tool really does, then put the corrected labels into ${policyPath}:`,
49
+ ' private: returns the user\'s own data untrusted: returns text others wrote outbound: reaches a person or a host destructive: changes or deletes things',
50
+ '',
51
+ JSON.stringify({ labels }, null, 2),
52
+ '',
53
+ ].join('\n');
54
+ }
55
+ //# sourceMappingURL=flow-guard-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-labels.js","sourceRoot":"","sources":["../src/flow-guard-labels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { OutTail } from './flow-guard-types.js';
2
+ import { flowPaths } from './flow-guard-paths.js';
3
+ export declare function readFp(path: string): Set<string>;
4
+ export interface FpStores {
5
+ secret: Set<string>;
6
+ fp: Set<string>;
7
+ pii: Set<string>;
8
+ tok: Set<string>;
9
+ }
10
+ export declare function readStores(paths: ReturnType<typeof flowPaths>): FpStores;
11
+ export interface LeakCheck {
12
+ secret?: string;
13
+ private?: boolean;
14
+ pii?: boolean;
15
+ incomplete?: boolean;
16
+ }
17
+ /** `typed`: what the user typed this session. A value the user typed is theirs to send; it is skipped
18
+ * for ordinary private text, never for secrets. */
19
+ export declare function checkLeak(paths: ReturnType<typeof flowPaths>, input: Record<string, unknown>, typed?: readonly string[], stores?: FpStores): LeakCheck;
20
+ /**
21
+ * A value sent in pieces, one call each, is checked whole: each argument name keeps the tail of what it carried,
22
+ * and the windows that cross from that tail into this call are checked like any other. Only pieces sent back to back
23
+ * through the same argument join; words between them break the run. Returns the new tails.
24
+ */
25
+ export declare function checkStream(input: Record<string, unknown>, tails: Readonly<Record<string, OutTail>>, stores: FpStores): {
26
+ leak: LeakCheck;
27
+ next: Record<string, OutTail>;
28
+ };
29
+ //# sourceMappingURL=flow-guard-leak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow-guard-leak.d.ts","sourceRoot":"","sources":["../src/flow-guard-leak.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AA6BlD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAShD;AAED,MAAM,WAAW,QAAQ;IAAG,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE;AACtG,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,GAAG,QAAQ,CAExE;AAED,MAAM,WAAW,SAAS;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE;AACtG;mDACmD;AACnD,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,GAAE,SAAS,MAAM,EAAO,EAAE,MAAM,GAAE,QAA4B,GAAG,SAAS,CAkB7K;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAgC1K"}