@aimarket/warden 0.20.0 → 0.22.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.
- package/CHANGELOG.md +113 -0
- package/README-es.md +27 -11
- package/README-fr.md +27 -11
- package/README-ru.md +27 -11
- package/README-zh.md +27 -11
- package/README.md +27 -11
- package/dist/annotation-review.d.ts +10 -0
- package/dist/annotation-review.d.ts.map +1 -0
- package/dist/annotation-review.js +36 -0
- package/dist/annotation-review.js.map +1 -0
- package/dist/atomic-screen.d.ts +61 -0
- package/dist/atomic-screen.d.ts.map +1 -0
- package/dist/atomic-screen.js +165 -0
- package/dist/atomic-screen.js.map +1 -0
- package/dist/flow-guard-answer.d.ts +29 -0
- package/dist/flow-guard-answer.d.ts.map +1 -0
- package/dist/flow-guard-answer.js +201 -0
- package/dist/flow-guard-answer.js.map +1 -0
- package/dist/flow-guard-classify.d.ts +14 -0
- package/dist/flow-guard-classify.d.ts.map +1 -0
- package/dist/flow-guard-classify.js +224 -0
- package/dist/flow-guard-classify.js.map +1 -0
- package/dist/flow-guard-decode.d.ts +17 -0
- package/dist/flow-guard-decode.d.ts.map +1 -0
- package/dist/flow-guard-decode.js +149 -0
- package/dist/flow-guard-decode.js.map +1 -0
- package/dist/flow-guard-destinations.d.ts +23 -0
- package/dist/flow-guard-destinations.d.ts.map +1 -0
- package/dist/flow-guard-destinations.js +158 -0
- package/dist/flow-guard-destinations.js.map +1 -0
- package/dist/flow-guard-fingerprints.d.ts +27 -0
- package/dist/flow-guard-fingerprints.d.ts.map +1 -0
- package/dist/flow-guard-fingerprints.js +164 -0
- package/dist/flow-guard-fingerprints.js.map +1 -0
- package/dist/flow-guard-fragments.d.ts +17 -0
- package/dist/flow-guard-fragments.d.ts.map +1 -0
- package/dist/flow-guard-fragments.js +122 -0
- package/dist/flow-guard-fragments.js.map +1 -0
- package/dist/flow-guard-instructions.d.ts +12 -0
- package/dist/flow-guard-instructions.d.ts.map +1 -0
- package/dist/flow-guard-instructions.js +36 -0
- package/dist/flow-guard-instructions.js.map +1 -0
- package/dist/flow-guard-labels.d.ts +10 -0
- package/dist/flow-guard-labels.d.ts.map +1 -0
- package/dist/flow-guard-labels.js +55 -0
- package/dist/flow-guard-labels.js.map +1 -0
- package/dist/flow-guard-leak.d.ts +29 -0
- package/dist/flow-guard-leak.d.ts.map +1 -0
- package/dist/flow-guard-leak.js +135 -0
- package/dist/flow-guard-leak.js.map +1 -0
- package/dist/flow-guard-paths.d.ts +21 -0
- package/dist/flow-guard-paths.d.ts.map +1 -0
- package/dist/flow-guard-paths.js +72 -0
- package/dist/flow-guard-paths.js.map +1 -0
- package/dist/flow-guard-policy.d.ts +15 -0
- package/dist/flow-guard-policy.d.ts.map +1 -0
- package/dist/flow-guard-policy.js +179 -0
- package/dist/flow-guard-policy.js.map +1 -0
- package/dist/flow-guard-post.d.ts +6 -0
- package/dist/flow-guard-post.d.ts.map +1 -0
- package/dist/flow-guard-post.js +131 -0
- package/dist/flow-guard-post.js.map +1 -0
- package/dist/flow-guard-pre.d.ts +13 -0
- package/dist/flow-guard-pre.d.ts.map +1 -0
- package/dist/flow-guard-pre.js +335 -0
- package/dist/flow-guard-pre.js.map +1 -0
- package/dist/flow-guard-screen.d.ts +85 -0
- package/dist/flow-guard-screen.d.ts.map +1 -0
- package/dist/flow-guard-screen.js +558 -0
- package/dist/flow-guard-screen.js.map +1 -0
- package/dist/flow-guard-state.d.ts +12 -0
- package/dist/flow-guard-state.d.ts.map +1 -0
- package/dist/flow-guard-state.js +121 -0
- package/dist/flow-guard-state.js.map +1 -0
- package/dist/flow-guard-text.d.ts +9 -0
- package/dist/flow-guard-text.d.ts.map +1 -0
- package/dist/flow-guard-text.js +46 -0
- package/dist/flow-guard-text.js.map +1 -0
- package/dist/flow-guard-types.d.ts +154 -0
- package/dist/flow-guard-types.d.ts.map +1 -0
- package/dist/flow-guard-types.js +2 -0
- package/dist/flow-guard-types.js.map +1 -0
- package/dist/flow-guard.d.ts +20 -215
- package/dist/flow-guard.d.ts.map +1 -1
- package/dist/flow-guard.js +26 -1400
- package/dist/flow-guard.js.map +1 -1
- package/dist/mcp-client.d.ts +1 -2
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +2 -19
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp-server.js +5 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +46 -9
- package/dist/mcp-tools.js.map +1 -1
- package/dist/public-address.d.ts +3 -0
- package/dist/public-address.d.ts.map +1 -0
- package/dist/public-address.js +58 -0
- package/dist/public-address.js.map +1 -0
- package/dist/review-diff.d.ts +36 -0
- package/dist/review-diff.d.ts.map +1 -1
- package/dist/review-diff.js +177 -7
- package/dist/review-diff.js.map +1 -1
- package/dist/task-adapter.d.ts +73 -0
- package/dist/task-adapter.d.ts.map +1 -0
- package/dist/task-adapter.js +379 -0
- package/dist/task-adapter.js.map +1 -0
- package/dist/task-cli.d.ts +3 -0
- package/dist/task-cli.d.ts.map +1 -0
- package/dist/task-cli.js +162 -0
- package/dist/task-cli.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wrap-cli.d.ts +5 -0
- package/dist/wrap-cli.d.ts.map +1 -1
- package/dist/wrap-cli.js +29 -2
- package/dist/wrap-cli.js.map +1 -1
- package/dist/wrap-screen.d.ts +3 -0
- package/dist/wrap-screen.d.ts.map +1 -0
- package/dist/wrap-screen.js +20 -0
- package/dist/wrap-screen.js.map +1 -0
- package/dist/wrap-state.d.ts +8 -1
- package/dist/wrap-state.d.ts.map +1 -1
- package/dist/wrap-state.js +21 -4
- package/dist/wrap-state.js.map +1 -1
- package/dist/wrap.d.ts.map +1 -1
- package/dist/wrap.js +204 -10
- package/dist/wrap.js.map +1 -1
- package/docs/flow-guard-model-comparison-2026-10-25.es.md +82 -0
- package/docs/flow-guard-model-comparison-2026-10-25.fr.md +82 -0
- package/docs/flow-guard-model-comparison-2026-10-25.md +82 -0
- package/docs/flow-guard-model-comparison-2026-10-25.ru.md +82 -0
- package/docs/flow-guard-model-comparison-2026-10-25.zh.md +82 -0
- package/docs/flow-guard.es.md +307 -12
- package/docs/flow-guard.fr.md +307 -12
- package/docs/flow-guard.md +315 -13
- package/docs/flow-guard.ru.md +314 -13
- package/docs/flow-guard.zh.md +306 -8
- package/docs/scan-limits.es.md +39 -0
- package/docs/scan-limits.fr.md +39 -0
- package/docs/scan-limits.md +39 -0
- package/docs/scan-limits.ru.md +39 -0
- package/docs/scan-limits.zh.md +39 -0
- package/docs/scan.es.md +2 -0
- package/docs/scan.fr.md +2 -0
- package/docs/scan.md +2 -0
- package/docs/scan.ru.md +2 -0
- package/docs/scan.zh.md +2 -0
- package/docs/security-hardening.es.md +1 -1
- package/docs/security-hardening.fr.md +1 -1
- package/docs/security-hardening.md +1 -1
- package/docs/security-hardening.ru.md +1 -1
- package/docs/security-hardening.zh.md +1 -1
- package/examples/atomic-screen-server.mjs +42 -0
- package/native/macos/Package.swift +9 -0
- package/native/macos/Sources/WardenDesktop/NativeSurface.swift +246 -0
- package/native/macos/Sources/WardenDesktop/StandardIO.swift +139 -0
- package/native/macos/Sources/WardenDesktopHost/Main.swift +211 -0
- package/package.json +18 -6
- package/scripts/build-macos.mjs +22 -0
|
@@ -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":"AAAA,oHAAoH;AACpH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAiB,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,aAAa,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5I,MAAM,iBAAiB,GAAG,iIAAiI,CAAC;AAC5J,MAAM,eAAe,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3H,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;AAE9C;;;sGAGsG;AACtG,MAAM,UAAU,oBAAoB,CAAC,MAAyB,EAAE,KAAK,GAAG,kBAAkB;IACxF,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,UAAU,GAAG,gHAAgH,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjJ,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACjJ,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,0FAA0F;QAC1F,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,IAAI,IAAI,WAAW;YAAE,KAAK,IAAI,MAAM,GAAG,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;gBAC7F,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;gBAC9F,IAAI,IAAI,GAAG,aAAa,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAAE,SAAS;gBACpH,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBAAE,SAAS;gBACnF,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC7D,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK;oBAAE,OAAO,GAAG,CAAC;YACrC,CAAC;QACD,IAAI,MAAM;YAAE,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;gBAClE,qFAAqF;gBACrF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACpC,IAAI,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAAE,SAAS;gBAC/E,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;oBACnF,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;oBACzE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;wBAAE,SAAS;oBAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,QAAQ,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBACjG,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK;wBAAE,OAAO,GAAG,CAAC;gBACrC,CAAC;YACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oHAAoH;AACpH,SAAS,aAAa,CAAC,KAA8B;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,EAAE,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACtC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,sCAAsC,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;gBAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3D,CAAC;YACD,0FAA0F;YAC1F,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAAE,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACnF,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAClC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC5D,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;AACjD,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,cAAc,CAAC,KAA8B,EAAE,KAAgB,EAAE,eAAwB,EAAE,QAAiB;IAC1H,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;IAC1B,IAAI,CAAC,CAAC;QAAE,OAAO,eAAe,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACtD,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;YAAE,SAAS;QACjD,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QACjF,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChK,IAAI,QAAQ,EAAE,CAAC;QACb,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;QACzC,IAAI,GAAG,CAAC,MAAM,GAAG,iBAAiB;YAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;IACpD,CAAC;IACD,IAAI,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,4DAA4D,EAAE,CAAC;IAC5F,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1G,CAAC"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** Flow guard: an outgoing call's arguments checked against the session's fingerprint stores, within one call and across calls. */
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { strings } from './flow-guard-text.js';
|
|
4
|
+
import { TOKEN_WINDOWS, WINDOW, alnum, fpHash, normalizeText, piiTokens, secretPattern } from './flow-guard-fingerprints.js';
|
|
5
|
+
import { candidates, scanCandidates } from './flow-guard-decode.js';
|
|
6
|
+
// Long enough to join the longest personal-data token (34) across two calls.
|
|
7
|
+
const ALNUM_TAIL = 33;
|
|
8
|
+
function windowsAll(text) {
|
|
9
|
+
const n = normalizeText(text);
|
|
10
|
+
const out = new Set();
|
|
11
|
+
for (let i = 0; i + WINDOW <= n.length; i++)
|
|
12
|
+
out.add(fpHash(n.slice(i, i + WINDOW)));
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function tokenHit(text, tokFp) {
|
|
16
|
+
for (const a of [alnum(text), alnum([...text].reverse().join(''))]) {
|
|
17
|
+
for (const size of TOKEN_WINDOWS)
|
|
18
|
+
for (let i = 0; i + size <= a.length; i++)
|
|
19
|
+
if (tokFp.has(fpHash(a.slice(i, i + size))))
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function windowHit(text, store) {
|
|
25
|
+
if (!store.size)
|
|
26
|
+
return false;
|
|
27
|
+
const value = normalizeText(text);
|
|
28
|
+
for (let i = 0; i + WINDOW <= value.length; i++)
|
|
29
|
+
if (store.has(fpHash(value.slice(i, i + WINDOW))))
|
|
30
|
+
return true;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
export function readFp(path) {
|
|
34
|
+
try {
|
|
35
|
+
const lines = readFileSync(path, 'utf8').split('\n').filter(Boolean);
|
|
36
|
+
if (lines.some(line => !/^[0-9a-f]{12}$/.test(line)))
|
|
37
|
+
throw new Error('invalid fingerprints');
|
|
38
|
+
return new Set(lines);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err.code === 'ENOENT')
|
|
42
|
+
return new Set();
|
|
43
|
+
throw new Error('flow fingerprints cannot be read safely');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function readStores(paths) {
|
|
47
|
+
return { secret: readFp(paths.secret), fp: readFp(paths.fp), pii: readFp(paths.pii), tok: readFp(paths.tok) };
|
|
48
|
+
}
|
|
49
|
+
/** `typed`: what the user typed this session. A value the user typed is theirs to send; it is skipped
|
|
50
|
+
* for ordinary private text, never for secrets. */
|
|
51
|
+
export function checkLeak(paths, input, typed = [], stores = readStores(paths)) {
|
|
52
|
+
const all = strings(input);
|
|
53
|
+
const scan = scanCandidates(all);
|
|
54
|
+
const cands = scan.values;
|
|
55
|
+
const typedText = typed.join('\n');
|
|
56
|
+
const own = (s) => { const n = normalizeText(s); return n.length > 0 && typedText.includes(n); };
|
|
57
|
+
const pattern = secretPattern(cands);
|
|
58
|
+
if (pattern)
|
|
59
|
+
return { secret: pattern };
|
|
60
|
+
if (scan.incomplete)
|
|
61
|
+
return { incomplete: true };
|
|
62
|
+
const { secret: secretFp, fp: privateFp, pii: piiFp, tok: tokFp } = stores;
|
|
63
|
+
if (!secretFp.size && !privateFp.size && !tokFp.size && !piiFp.size)
|
|
64
|
+
return {};
|
|
65
|
+
for (const c of cands)
|
|
66
|
+
if (windowHit(c, secretFp))
|
|
67
|
+
return { secret: 'text from a credential file this session read' };
|
|
68
|
+
if (tokFp.size)
|
|
69
|
+
for (const c of cands)
|
|
70
|
+
if (tokenHit(c, tokFp))
|
|
71
|
+
return { secret: 'part of a credential this session read' };
|
|
72
|
+
const privateCands = candidates(all.filter(s => !own(s)));
|
|
73
|
+
const result = {};
|
|
74
|
+
if (piiFp.size)
|
|
75
|
+
for (const c of privateCands)
|
|
76
|
+
for (const t of piiTokens(c))
|
|
77
|
+
if (piiFp.has(fpHash(t))) {
|
|
78
|
+
result.pii = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (privateFp.size)
|
|
82
|
+
for (const c of privateCands)
|
|
83
|
+
if (windowHit(c, privateFp)) {
|
|
84
|
+
result.private = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A value sent in pieces, one call each, is checked whole: each argument name keeps the tail of what it carried,
|
|
91
|
+
* and the windows that cross from that tail into this call are checked like any other. Only pieces sent back to back
|
|
92
|
+
* through the same argument join; words between them break the run. Returns the new tails.
|
|
93
|
+
*/
|
|
94
|
+
export function checkStream(input, tails, stores) {
|
|
95
|
+
const leak = {};
|
|
96
|
+
const next = {};
|
|
97
|
+
for (const [key, value] of Object.entries(input)) {
|
|
98
|
+
const n = normalizeText(strings(value).join(''));
|
|
99
|
+
if (!n)
|
|
100
|
+
continue;
|
|
101
|
+
const a = alnum(n);
|
|
102
|
+
const prev = tails[key] ?? { t: '', a: '' };
|
|
103
|
+
if (prev.t) {
|
|
104
|
+
for (const h of windowsAll(prev.t + n.slice(0, WINDOW - 1))) {
|
|
105
|
+
if (stores.secret.has(h))
|
|
106
|
+
leak.secret = 'text from a credential file this session read, sent in pieces';
|
|
107
|
+
if (stores.fp.has(h))
|
|
108
|
+
leak.private = true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (prev.a) {
|
|
112
|
+
for (const size of TOKEN_WINDOWS) {
|
|
113
|
+
const tok = prev.a.slice(-(size - 1)) + a.slice(0, size - 1);
|
|
114
|
+
for (let i = 0; i + size <= tok.length; i++)
|
|
115
|
+
if (stores.tok.has(fpHash(tok.slice(i, i + size))))
|
|
116
|
+
leak.secret = 'part of a credential this session read, sent in pieces';
|
|
117
|
+
}
|
|
118
|
+
if (stores.pii.size) {
|
|
119
|
+
// Personal-data tokens are 8-34 letters and digits: try every one that starts before this call and ends in it.
|
|
120
|
+
const joined = prev.a + a.slice(0, ALNUM_TAIL);
|
|
121
|
+
for (let i = 0; i < prev.a.length && !leak.pii; i++) {
|
|
122
|
+
for (let len = 8; len <= 34 && i + len <= joined.length; len++) {
|
|
123
|
+
if (i + len > prev.a.length && stores.pii.has(fpHash(joined.slice(i, i + len)))) {
|
|
124
|
+
leak.pii = true;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
next[key] = { t: (prev.t + n).slice(-(WINDOW - 1)), a: (prev.a + a).slice(-ALNUM_TAIL) };
|
|
132
|
+
}
|
|
133
|
+
return { leak, next };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=flow-guard-leak.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-guard-leak.js","sourceRoot":"","sources":["../src/flow-guard-leak.ts"],"names":[],"mappings":"AAAA,mIAAmI;AACnI,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7H,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEpE,6EAA6E;AAC7E,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,KAA0B;IACxD,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,KAAK,MAAM,IAAI,IAAI,aAAa;YAAE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;oBAAE,OAAO,IAAI,CAAC;IACxI,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,KAA0B;IACzD,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IAChH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrE,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC9F,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,GAAG,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAGD,MAAM,UAAU,UAAU,CAAC,KAAmC;IAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AAChH,CAAC;AAGD;mDACmD;AACnD,MAAM,UAAU,SAAS,CAAC,KAAmC,EAAE,KAA8B,EAAE,QAA2B,EAAE,EAAE,SAAmB,UAAU,CAAC,KAAK,CAAC;IAChK,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,GAAG,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzG,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACjD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAC3E,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/E,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,IAAI,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,+CAA+C,EAAE,CAAC;IACtH,IAAI,KAAK,CAAC,IAAI;QAAE,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;gBAAE,OAAO,EAAE,MAAM,EAAE,wCAAwC,EAAE,CAAC;IAC3H,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,IAAI,KAAK,CAAC,IAAI;QAAE,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;gBAAE,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC;oBAAC,MAAM;gBAAC,CAAC;IACnI,IAAI,SAAS,CAAC,IAAI;QAAE,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,IAAI,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;gBAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBAAC,MAAM;YAAC,CAAC;IAChH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAA8B,EAAE,KAAwC,EAAE,MAAgB;IACpH,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC;YACX,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,IAAI,CAAC,MAAM,GAAG,+DAA+D,CAAC;gBACxG,IAAI,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YAC5C,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC;YACX,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;gBACjC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;gBAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;oBAAE,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;wBAAE,IAAI,CAAC,MAAM,GAAG,wDAAwD,CAAC;YAC1K,CAAC;YACD,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACpB,+GAA+G;gBAC/G,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;oBACpD,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;wBAC/D,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;4BAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;4BAAC,MAAM;wBAAC,CAAC;oBAC9G,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;IAC3F,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function stateDir(env?: NodeJS.ProcessEnv): string;
|
|
2
|
+
export declare function sessionKey(sessionId: string): string;
|
|
3
|
+
export declare function flowPaths(dir: string, sessionId: string): {
|
|
4
|
+
dir: string;
|
|
5
|
+
state: string;
|
|
6
|
+
fp: string;
|
|
7
|
+
secret: string;
|
|
8
|
+
pii: string;
|
|
9
|
+
tok: string;
|
|
10
|
+
hint: string;
|
|
11
|
+
log: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function absPath(p: string, cwd: string): string;
|
|
14
|
+
export declare function physicalPath(p: string): string;
|
|
15
|
+
export declare function shellPath(p: string, cwd: string, env: NodeJS.ProcessEnv): string;
|
|
16
|
+
export declare function inside(child: string, parent: string): boolean;
|
|
17
|
+
export declare function isSecretPath(path: string): boolean;
|
|
18
|
+
export declare const FOREIGN_DIRS: RegExp;
|
|
19
|
+
/** Files that, if the agent could change them, would let it switch the guard off. */
|
|
20
|
+
export declare function isProtectedPath(path: string, cwd: string, env?: NodeJS.ProcessEnv): boolean;
|
|
21
|
+
//# sourceMappingURL=flow-guard-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-guard-paths.d.ts","sourceRoot":"","sources":["../src/flow-guard-paths.ts"],"names":[],"mappings":"AAMA,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAErE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;EAIvD;AAMD,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAGD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAIhF;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG7D;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,eAAO,MAAM,YAAY,QAAmI,CAAC;AAE7J,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAgBxG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Flow guard: where the guard keeps its state, which files are credentials, and which would let the agent switch it off. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { basename, dirname, isAbsolute, join, normalize, resolve, sep } from 'node:path';
|
|
6
|
+
export function stateDir(env = process.env) {
|
|
7
|
+
return env.WARDEN_STATE_DIR || join(env.XDG_STATE_HOME || join(homedir(), '.local', 'state'), 'warden');
|
|
8
|
+
}
|
|
9
|
+
export function sessionKey(sessionId) {
|
|
10
|
+
return createHash('sha256').update(sessionId).digest('hex');
|
|
11
|
+
}
|
|
12
|
+
export function flowPaths(dir, sessionId) {
|
|
13
|
+
const base = join(dir, 'claude-code', 'flow');
|
|
14
|
+
const key = sessionKey(sessionId);
|
|
15
|
+
return { dir: base, state: join(base, `${key}.json`), fp: join(base, `${key}.fp`), secret: join(base, `${key}.secret.fp`), pii: join(base, `${key}.pii.fp`), tok: join(base, `${key}.tok.fp`), hint: join(base, `${key}.hint.fp`), log: join(base, `${key}.log`) };
|
|
16
|
+
}
|
|
17
|
+
function expandHome(p) {
|
|
18
|
+
return p === '~' ? homedir() : p.startsWith('~/') ? join(homedir(), p.slice(2)) : p;
|
|
19
|
+
}
|
|
20
|
+
export function absPath(p, cwd) {
|
|
21
|
+
const e = expandHome(p);
|
|
22
|
+
return normalize(isAbsolute(e) ? e : resolve(cwd, e));
|
|
23
|
+
}
|
|
24
|
+
// Resolve existing ancestors too: a write to symlink-dir/new-file must protect the real target.
|
|
25
|
+
export function physicalPath(p) {
|
|
26
|
+
try {
|
|
27
|
+
return realpathSync(p);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
const parent = dirname(p);
|
|
31
|
+
return parent === p ? p : join(physicalPath(parent), basename(p));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function shellPath(p, cwd, env) {
|
|
35
|
+
const expanded = p.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, plain) => env[braced || plain] ?? match);
|
|
36
|
+
return absPath(expanded.replace(/^['"]|['"]$/g, '').replace(/^@/, ''), cwd);
|
|
37
|
+
}
|
|
38
|
+
export function inside(child, parent) {
|
|
39
|
+
const p = parent.endsWith(sep) ? parent : parent + sep;
|
|
40
|
+
return child === parent || child.startsWith(p);
|
|
41
|
+
}
|
|
42
|
+
const SECRET_PATH = /(^|\/)(\.ssh|\.aws|\.gnupg|\.kube|\.docker|\.config\/gh|\.config\/gcloud|\.azure|\.password-store|keychains?)(\/|$)|(^|\/)(\.netrc|\.npmrc|\.pypirc|\.git-credentials|\.pgpass|id_(rsa|dsa|ecdsa|ed25519)[^/]*|[^/]*\.(pem|key|p12|pfx|kdbx|keystore|jks)|\.env(\.[^/]*)?|credentials?(\.[^/]*)?|secrets?(\.[^/]*)?)$/i;
|
|
43
|
+
export function isSecretPath(path) {
|
|
44
|
+
return SECRET_PATH.test(path.replace(/\\/g, '/'));
|
|
45
|
+
}
|
|
46
|
+
export const FOREIGN_DIRS = /(^|\/)(node_modules|vendor|\.venv|venv|site-packages|dist-packages|bower_components|Pods|\.cargo\/registry|go\/pkg\/mod)(\/|$)/;
|
|
47
|
+
/** Files that, if the agent could change them, would let it switch the guard off. */
|
|
48
|
+
export function isProtectedPath(path, cwd, env = process.env) {
|
|
49
|
+
// join also normalizes a root home: `${home}/...` would produce a double slash
|
|
50
|
+
// for HOME=/ and fail to match the normalized /.claude.json or plugin path.
|
|
51
|
+
const homeConfig = join(homedir(), '.claude.json').replace(/\\/g, '/');
|
|
52
|
+
const homePlugins = join(homedir(), '.claude', 'plugins').replace(/\\/g, '/');
|
|
53
|
+
return [path, physicalPath(path)].some(candidate => {
|
|
54
|
+
const p = candidate.replace(/\\/g, '/');
|
|
55
|
+
if (inside(p, physicalPath(resolve(stateDir(env))).replace(/\\/g, '/')))
|
|
56
|
+
return true;
|
|
57
|
+
if (env.WARDEN_FLOW_POLICY && p === physicalPath(resolve(env.WARDEN_FLOW_POLICY)).replace(/\\/g, '/'))
|
|
58
|
+
return true;
|
|
59
|
+
if (/(^|\/)\.claude\/settings(\.local)?\.json$/.test(p))
|
|
60
|
+
return true;
|
|
61
|
+
if (p === homeConfig || /(^|\/)\.mcp\.json$/.test(p))
|
|
62
|
+
return true;
|
|
63
|
+
if (inside(p, homePlugins) || /(^|\/)\.claude\/hooks(\/|$)/.test(p))
|
|
64
|
+
return true;
|
|
65
|
+
if (/(^|\/)\.git\/(?:config$|hooks(?:\/|$))/.test(p))
|
|
66
|
+
return true;
|
|
67
|
+
if (/(^|\/)warden\.lock\.json$/.test(p) && inside(p, cwd.replace(/\\/g, '/')))
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=flow-guard-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-guard-paths.js","sourceRoot":"","sources":["../src/flow-guard-paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FlowLabel, FlowPolicy } from './flow-guard-types.js';
|
|
2
|
+
export declare const DEFAULT_POLICY: FlowPolicy;
|
|
3
|
+
export declare const LABELS: readonly FlowLabel[];
|
|
4
|
+
/** User-level policy only (`<state dir>/flow-policy.json`): a project file is something the agent can edit. */
|
|
5
|
+
export declare function loadPolicy(env?: NodeJS.ProcessEnv): FlowPolicy;
|
|
6
|
+
/** Read bounded, pinned operator configuration. The task runtime additionally validates the complete schema. */
|
|
7
|
+
export declare function readPinnedTaskManifest(policy: FlowPolicy, env?: NodeJS.ProcessEnv): string;
|
|
8
|
+
/** This delegates only fixed control IDs to our own enforcing adapter, never clicks or arbitrary MCP servers.
|
|
9
|
+
* The operator must bind this exact MCP server name to `warden-mcp task`; tool output cannot establish trust. */
|
|
10
|
+
export declare function taskDelegation(tool: string, args: Record<string, unknown>, policy: FlowPolicy, env: NodeJS.ProcessEnv): boolean;
|
|
11
|
+
/** Sorted JSON keys; array order and every argument/destination are significant. Never hashes a truncated input. */
|
|
12
|
+
export declare function publicRequestDigest(tool: string, input: Record<string, unknown>): string | undefined;
|
|
13
|
+
export declare function globMatch(glob: string, name: string): boolean;
|
|
14
|
+
export declare function hostMatches(host: string, patterns: readonly string[]): boolean;
|
|
15
|
+
//# sourceMappingURL=flow-guard-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-guard-policy.d.ts","sourceRoot":"","sources":["../src/flow-guard-policy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAsC,SAAS,EAAY,UAAU,EAAwC,MAAM,uBAAuB,CAAC;AAIvJ,eAAO,MAAM,cAAc,EAAE,UAAiL,CAAC;AAE/M,eAAO,MAAM,MAAM,EAAE,SAAS,SAAS,EAAwD,CAAC;AAIhG,+GAA+G;AAC/G,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CA6B3E;AAaD,gHAAgH;AAChH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAavG;AAED;iHACiH;AACjH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAc/H;AAmBD,oHAAoH;AACpH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAepG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM9E"}
|