@intutic/mcp-governance-proxy 1.10.0 → 1.10.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.
- package/README.md +28 -0
- package/dist/anomaly/detectors.d.ts +129 -0
- package/dist/anomaly/detectors.d.ts.map +1 -0
- package/dist/anomaly/detectors.js +368 -0
- package/dist/anomaly/detectors.js.map +1 -0
- package/dist/anomaly/index.d.ts +52 -0
- package/dist/anomaly/index.d.ts.map +1 -0
- package/dist/anomaly/index.js +71 -0
- package/dist/anomaly/index.js.map +1 -0
- package/dist/config.d.ts +107 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -6
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +45 -46
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/index.d.ts +15 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +78 -9
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/instrumentation.d.ts +20 -0
- package/dist/daemon/instrumentation.d.ts.map +1 -0
- package/dist/daemon/instrumentation.js +61 -0
- package/dist/daemon/instrumentation.js.map +1 -0
- package/dist/daemon/policyCache.d.ts +83 -0
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +280 -11
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/daemon/statusReporter.d.ts +12 -0
- package/dist/daemon/statusReporter.d.ts.map +1 -0
- package/dist/daemon/statusReporter.js +130 -0
- package/dist/daemon/statusReporter.js.map +1 -0
- package/dist/daemon/telemetryBatcher.d.ts +3 -1
- package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
- package/dist/daemon/telemetryBatcher.js +56 -13
- package/dist/daemon/telemetryBatcher.js.map +1 -1
- package/dist/daemonClient.d.ts +1 -1
- package/dist/daemonClient.d.ts.map +1 -1
- package/dist/daemonClient.js +0 -2
- package/dist/daemonClient.js.map +1 -1
- package/dist/dlp.d.ts +19 -0
- package/dist/dlp.d.ts.map +1 -1
- package/dist/dlp.js +95 -18
- package/dist/dlp.js.map +1 -1
- package/dist/emitter.d.ts +40 -2
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +27 -5
- package/dist/emitter.js.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/injection.d.ts +46 -0
- package/dist/injection.d.ts.map +1 -0
- package/dist/injection.js +147 -0
- package/dist/injection.js.map +1 -0
- package/dist/interceptor.d.ts +131 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +301 -5
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +110 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +206 -5
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +205 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +572 -65
- package/dist/proxy.js.map +1 -1
- package/dist/remoteBridge.d.ts +51 -0
- package/dist/remoteBridge.d.ts.map +1 -0
- package/dist/remoteBridge.js +182 -0
- package/dist/remoteBridge.js.map +1 -0
- package/dist/session.d.ts +158 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +206 -0
- package/dist/session.js.map +1 -0
- package/dist/sessionScope.d.ts +51 -0
- package/dist/sessionScope.d.ts.map +1 -0
- package/dist/sessionScope.js +94 -0
- package/dist/sessionScope.js.map +1 -0
- package/dist/sessionStore.d.ts +69 -0
- package/dist/sessionStore.d.ts.map +1 -0
- package/dist/sessionStore.js +143 -0
- package/dist/sessionStore.js.map +1 -0
- package/dist/tofu.d.ts +112 -0
- package/dist/tofu.d.ts.map +1 -0
- package/dist/tofu.js +180 -0
- package/dist/tofu.js.map +1 -0
- package/dist/toolPoison.d.ts +32 -0
- package/dist/toolPoison.d.ts.map +1 -0
- package/dist/toolPoison.js +81 -0
- package/dist/toolPoison.js.map +1 -0
- package/dist/valkeyErrors.d.ts +17 -0
- package/dist/valkeyErrors.d.ts.map +1 -0
- package/dist/valkeyErrors.js +24 -0
- package/dist/valkeyErrors.js.map +1 -0
- package/dist/wasm/context.d.ts +57 -0
- package/dist/wasm/context.d.ts.map +1 -0
- package/dist/wasm/context.js +59 -0
- package/dist/wasm/context.js.map +1 -0
- package/dist/wasm/hostImports.d.ts +43 -0
- package/dist/wasm/hostImports.d.ts.map +1 -0
- package/dist/wasm/hostImports.js +158 -0
- package/dist/wasm/hostImports.js.map +1 -0
- package/dist/wasm/loader.d.ts +95 -0
- package/dist/wasm/loader.d.ts.map +1 -0
- package/dist/wasm/loader.js +198 -0
- package/dist/wasm/loader.js.map +1 -0
- package/dist/wasm/referencedFiles.d.ts +115 -0
- package/dist/wasm/referencedFiles.d.ts.map +1 -0
- package/dist/wasm/referencedFiles.js +366 -0
- package/dist/wasm/referencedFiles.js.map +1 -0
- package/dist/wasm/runner.d.ts +91 -0
- package/dist/wasm/runner.d.ts.map +1 -0
- package/dist/wasm/runner.js +308 -0
- package/dist/wasm/runner.js.map +1 -0
- package/dist/wasm/watch.d.ts +5 -0
- package/dist/wasm/watch.d.ts.map +1 -0
- package/dist/wasm/watch.js +54 -0
- package/dist/wasm/watch.js.map +1 -0
- package/dist/wasm/worker.d.ts +39 -0
- package/dist/wasm/worker.d.ts.map +1 -0
- package/dist/wasm/worker.js +245 -0
- package/dist/wasm/worker.js.map +1 -0
- package/package.json +14 -4
package/dist/policy.js
CHANGED
|
@@ -11,6 +11,37 @@ import * as node_http from 'node:http';
|
|
|
11
11
|
import { createStderrLogger as createLogger } from './stderrLog.js';
|
|
12
12
|
import { callDaemonSocket } from './daemonClient.js';
|
|
13
13
|
const log = createLogger('mcp-proxy-policy');
|
|
14
|
+
/**
|
|
15
|
+
* Is this unvalidated object actually a policy rule?
|
|
16
|
+
*
|
|
17
|
+
* The daemon caches `sopRules` as `Record<string, unknown>[]` — it filters the
|
|
18
|
+
* control plane's response to plain objects and validates nothing further. The
|
|
19
|
+
* client then used `as SopRule[]`, behind a `Promise<any>` that hid the
|
|
20
|
+
* mismatch entirely.
|
|
21
|
+
*
|
|
22
|
+
* That matters because these drive blocking decisions: a rule missing
|
|
23
|
+
* `toolPattern` reaches `new RegExp(undefined)` and matches everything, and a
|
|
24
|
+
* rule with an unrecognised `action` falls through every branch and enforces
|
|
25
|
+
* nothing. Neither is a shape the control plane intends to send — which is the
|
|
26
|
+
* argument for dropping the malformed ones loudly rather than trusting a cast.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Exported because it is the contract between this package and whatever
|
|
30
|
+
* produces rules for it, not merely an internal helper. `policyCache`'s snapshot
|
|
31
|
+
* seed has to satisfy it, and a test that asserted against a *copy* of this
|
|
32
|
+
* predicate would prove nothing about the real one — which is precisely the
|
|
33
|
+
* two-copies-of-one-rule failure the harness gates were just rebuilt to remove.
|
|
34
|
+
*/
|
|
35
|
+
export function isSopRule(value) {
|
|
36
|
+
if (typeof value !== 'object' || value === null)
|
|
37
|
+
return false;
|
|
38
|
+
const r = value;
|
|
39
|
+
return (typeof r['id'] === 'string' &&
|
|
40
|
+
typeof r['toolPattern'] === 'string' &&
|
|
41
|
+
typeof r['reason'] === 'string' &&
|
|
42
|
+
(r['argPattern'] === undefined || typeof r['argPattern'] === 'string') &&
|
|
43
|
+
(r['action'] === 'block' || r['action'] === 'warn' || r['action'] === 'require_approval'));
|
|
44
|
+
}
|
|
14
45
|
export class PolicyClient {
|
|
15
46
|
controlPlaneUrl;
|
|
16
47
|
apiKey;
|
|
@@ -18,6 +49,57 @@ export class PolicyClient {
|
|
|
18
49
|
ttlMs;
|
|
19
50
|
mcpProxyMode;
|
|
20
51
|
rules = [];
|
|
52
|
+
/**
|
|
53
|
+
* Workspace DLP regex sources, delivered to the scanner on every refresh.
|
|
54
|
+
* The daemon has cached these since the policy cache existed; this client
|
|
55
|
+
* received and dropped them — a workspace's custom patterns were a setting
|
|
56
|
+
* wired to nothing.
|
|
57
|
+
*/
|
|
58
|
+
dlpPatterns = [];
|
|
59
|
+
/** Workspace injection-pattern sources (`mcpInjectionPatterns`, TD-436); empty = floor only. */
|
|
60
|
+
injectionPatterns = [];
|
|
61
|
+
/**
|
|
62
|
+
* Additive tool scoping: when non-empty, ONLY these tools may be called
|
|
63
|
+
* through this proxy. Empty means unrestricted — the same convention as
|
|
64
|
+
* `allow_harnesses` and the starter rules' allowlist inversion warnings:
|
|
65
|
+
* an empty allowlist read as "permit nothing" would block every workspace
|
|
66
|
+
* that never declared one.
|
|
67
|
+
*/
|
|
68
|
+
allowedTools = [];
|
|
69
|
+
/** Operator-curated tool descriptions, applied to tools/list responses. */
|
|
70
|
+
toolDescriptionOverrides = {};
|
|
71
|
+
/**
|
|
72
|
+
* Additive SERVER scoping: when non-empty, ONLY these MCP server names may
|
|
73
|
+
* be proxied by this process. Empty means unrestricted — same convention
|
|
74
|
+
* and same source as `allowedTools` above (mirrors `mcpAllowedTools` /
|
|
75
|
+
* `mcpAllowedServers` in workspace settings, delivered via the same
|
|
76
|
+
* `GET /api/v1/sop/rules` response this class already polls).
|
|
77
|
+
*/
|
|
78
|
+
allowedServers = [];
|
|
79
|
+
/**
|
|
80
|
+
* Control-plane-delivered prompt-injection disposition override. `undefined`
|
|
81
|
+
* (never `'warn'`/`'block'` by default) means "the control plane did not
|
|
82
|
+
* send this field" — distinct from the field being explicitly set to
|
|
83
|
+
* `'warn'` — so the caller (interceptor.ts) knows to fall back to
|
|
84
|
+
* `config.ts`'s `mcpInjectionAction` env-derived default rather than
|
|
85
|
+
* silently treating an unset policy as an explicit warn.
|
|
86
|
+
*/
|
|
87
|
+
injectionAction;
|
|
88
|
+
/**
|
|
89
|
+
* Control-plane-delivered anomaly-detection mode override (Phase 2).
|
|
90
|
+
* `undefined` when absent — same "absent means no override, fall back to
|
|
91
|
+
* config.ts's env-derived default" convention as `injectionAction` above.
|
|
92
|
+
*/
|
|
93
|
+
anomalyMode;
|
|
94
|
+
/**
|
|
95
|
+
* Control-plane-delivered per-detector disposition overrides. Always an
|
|
96
|
+
* object (never `undefined`) — an absent policy field reads as "no
|
|
97
|
+
* overrides," which `resolveEffectiveDisposition` (anomaly/index.ts)
|
|
98
|
+
* already treats as a no-op via `overrides[detectorId] === undefined`, the
|
|
99
|
+
* same empty-object-means-unrestricted convention `toolDescriptionOverrides`
|
|
100
|
+
* above uses.
|
|
101
|
+
*/
|
|
102
|
+
anomalyOverrides = {};
|
|
21
103
|
lastFetchAt = 0;
|
|
22
104
|
refreshTimer = null;
|
|
23
105
|
constructor(controlPlaneUrl, apiKey, workspaceId, ttlMs = 60_000, mcpProxyMode = 'per-session') {
|
|
@@ -27,17 +109,31 @@ export class PolicyClient {
|
|
|
27
109
|
this.ttlMs = ttlMs;
|
|
28
110
|
this.mcpProxyMode = mcpProxyMode;
|
|
29
111
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Start background refresh timer.
|
|
114
|
+
*
|
|
115
|
+
* @param onTick - Optional callback invoked on the SAME interval as the
|
|
116
|
+
* policy refresh (including the initial kick below) — Phase 3's
|
|
117
|
+
* `~/.intutic/wasm/` rescan (`WasmRunner.rescan`, wired from proxy.ts)
|
|
118
|
+
* hooks this rather than starting a second timer, per the task's own
|
|
119
|
+
* "don't add a second timer" instruction. Errors are swallowed the same
|
|
120
|
+
* way `refresh()`'s own are — a rescan failure must not take down the
|
|
121
|
+
* policy refresh it rides alongside.
|
|
122
|
+
*/
|
|
123
|
+
start(onTick) {
|
|
32
124
|
this.refreshTimer = setInterval(() => {
|
|
33
125
|
void this.refresh().catch(() => {
|
|
34
126
|
// Errors already logged inside refresh()
|
|
35
127
|
});
|
|
128
|
+
if (onTick)
|
|
129
|
+
void Promise.resolve(onTick()).catch(() => { });
|
|
36
130
|
}, this.ttlMs);
|
|
37
131
|
// Don't block Node.js exit on this timer
|
|
38
132
|
this.refreshTimer.unref();
|
|
39
133
|
// Kick off initial fetch (non-blocking — proxy starts immediately)
|
|
40
134
|
void this.refresh().catch(() => { });
|
|
135
|
+
if (onTick)
|
|
136
|
+
void Promise.resolve(onTick()).catch(() => { });
|
|
41
137
|
}
|
|
42
138
|
/** Stop the background refresh timer. */
|
|
43
139
|
stop() {
|
|
@@ -50,6 +146,82 @@ export class PolicyClient {
|
|
|
50
146
|
getRules() {
|
|
51
147
|
return this.rules;
|
|
52
148
|
}
|
|
149
|
+
/** Workspace DLP regex sources from the last successful refresh. */
|
|
150
|
+
/** Workspace-supplied prompt-injection regex sources; empty means the hardcoded floor alone. */
|
|
151
|
+
getInjectionPatterns() {
|
|
152
|
+
return this.injectionPatterns;
|
|
153
|
+
}
|
|
154
|
+
getDlpPatterns() {
|
|
155
|
+
return this.dlpPatterns;
|
|
156
|
+
}
|
|
157
|
+
/** The additive tool allowlist; empty means unrestricted. */
|
|
158
|
+
getAllowedTools() {
|
|
159
|
+
return this.allowedTools;
|
|
160
|
+
}
|
|
161
|
+
/** The additive MCP server allowlist; empty means unrestricted. */
|
|
162
|
+
getAllowedServers() {
|
|
163
|
+
return this.allowedServers;
|
|
164
|
+
}
|
|
165
|
+
/** Operator description overrides for tools/list curation. */
|
|
166
|
+
getToolDescriptionOverrides() {
|
|
167
|
+
return this.toolDescriptionOverrides;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The control-plane-delivered prompt-injection disposition, or `undefined`
|
|
171
|
+
* when the policy source never sent `mcpInjectionAction` — absent means
|
|
172
|
+
* "no override", not "warn". Callers (interceptor.ts) fall back to
|
|
173
|
+
* `config.ts`'s env-derived default in that case, the same "absent means
|
|
174
|
+
* default" convention every other optional policy field in this class
|
|
175
|
+
* follows.
|
|
176
|
+
*/
|
|
177
|
+
getInjectionAction() {
|
|
178
|
+
return this.injectionAction;
|
|
179
|
+
}
|
|
180
|
+
/** The control-plane-delivered anomaly mode, or `undefined` if unset (see field doc). */
|
|
181
|
+
getAnomalyMode() {
|
|
182
|
+
return this.anomalyMode;
|
|
183
|
+
}
|
|
184
|
+
/** Per-detector disposition overrides; always an object, empty means none. */
|
|
185
|
+
getAnomalyOverrides() {
|
|
186
|
+
return this.anomalyOverrides;
|
|
187
|
+
}
|
|
188
|
+
/** Parse the optional curation fields shared by both refresh paths. */
|
|
189
|
+
absorbCuration(source) {
|
|
190
|
+
const allowed = source['allowedTools'] ?? source['mcpAllowedTools'];
|
|
191
|
+
this.allowedTools = Array.isArray(allowed)
|
|
192
|
+
? allowed.filter((t) => typeof t === 'string')
|
|
193
|
+
: [];
|
|
194
|
+
const overrides = source['toolDescriptionOverrides'] ?? source['mcpToolDescriptionOverrides'];
|
|
195
|
+
this.toolDescriptionOverrides = {};
|
|
196
|
+
if (typeof overrides === 'object' && overrides !== null && !Array.isArray(overrides)) {
|
|
197
|
+
for (const [k, v] of Object.entries(overrides)) {
|
|
198
|
+
if (typeof v === 'string')
|
|
199
|
+
this.toolDescriptionOverrides[k] = v;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const allowedServers = source['allowedServers'] ?? source['mcpAllowedServers'];
|
|
203
|
+
this.allowedServers = Array.isArray(allowedServers)
|
|
204
|
+
? allowedServers.filter((s) => typeof s === 'string')
|
|
205
|
+
: [];
|
|
206
|
+
const injectionPatterns = source['mcpInjectionPatterns'];
|
|
207
|
+
this.injectionPatterns = Array.isArray(injectionPatterns)
|
|
208
|
+
? injectionPatterns.filter((p) => typeof p === 'string')
|
|
209
|
+
: [];
|
|
210
|
+
const injectionAction = source['mcpInjectionAction'];
|
|
211
|
+
this.injectionAction =
|
|
212
|
+
injectionAction === 'warn' || injectionAction === 'block' ? injectionAction : undefined;
|
|
213
|
+
const anomalyMode = source['mcpAnomalyMode'];
|
|
214
|
+
this.anomalyMode =
|
|
215
|
+
anomalyMode === 'enforce' || anomalyMode === 'warn' || anomalyMode === 'off' ? anomalyMode : undefined;
|
|
216
|
+
const anomalyOverrides = source['mcpAnomalyOverrides'];
|
|
217
|
+
this.anomalyOverrides = {};
|
|
218
|
+
if (typeof anomalyOverrides === 'object' && anomalyOverrides !== null && !Array.isArray(anomalyOverrides)) {
|
|
219
|
+
for (const [k, v] of Object.entries(anomalyOverrides)) {
|
|
220
|
+
if (v === 'steer' || v === 'reask' || v === 'kill' || v === 'off')
|
|
221
|
+
this.anomalyOverrides[k] = v;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
53
225
|
/** Find the first matching rule for a given tool name + serialized args. */
|
|
54
226
|
matchRule(toolName, toolInputJson) {
|
|
55
227
|
for (const rule of this.rules) {
|
|
@@ -74,16 +246,40 @@ export class PolicyClient {
|
|
|
74
246
|
async refresh() {
|
|
75
247
|
if (this.mcpProxyMode === 'daemon') {
|
|
76
248
|
try {
|
|
77
|
-
|
|
249
|
+
// The socket returns whatever the daemon serialised, so the caller
|
|
250
|
+
// names the shape it expects. `policy.get` is answered by `resolvePolicy`
|
|
251
|
+
// in the daemon's own policyCache, which is where this type comes from —
|
|
252
|
+
// not an assertion, the actual producer's return type.
|
|
253
|
+
const policy = await callDaemonSocket('policy.get', {
|
|
254
|
+
workspaceId: this.workspaceId,
|
|
255
|
+
});
|
|
78
256
|
if (policy) {
|
|
79
|
-
|
|
257
|
+
const candidates = policy.sopRules ?? [];
|
|
258
|
+
// An explicit loop rather than `.filter(isSopRule)`: TypeScript will
|
|
259
|
+
// not narrow `Record<string, unknown>[]` to `SopRule[]` through a type
|
|
260
|
+
// predicate, because an interface has no implicit index signature.
|
|
261
|
+
const accepted = [];
|
|
262
|
+
for (const candidate of candidates) {
|
|
263
|
+
if (isSopRule(candidate))
|
|
264
|
+
accepted.push(candidate);
|
|
265
|
+
}
|
|
266
|
+
this.rules = accepted;
|
|
267
|
+
const dropped = candidates.length - accepted.length;
|
|
268
|
+
if (dropped > 0) {
|
|
269
|
+
log.warn({ action: 'policy_rules_malformed', dropped, kept: this.rules.length }, 'Dropped malformed SOP rules from the daemon — they cannot be enforced');
|
|
270
|
+
}
|
|
271
|
+
this.dlpPatterns = (policy.dlpPatterns ?? []).filter((p) => typeof p === 'string');
|
|
272
|
+
this.absorbCuration(policy);
|
|
80
273
|
this.lastFetchAt = Date.now();
|
|
81
274
|
log.info({ action: 'policy_refreshed_from_daemon', ruleCount: this.rules.length }, 'SOP rules refreshed from daemon');
|
|
82
275
|
return;
|
|
83
276
|
}
|
|
84
277
|
}
|
|
85
278
|
catch (err) {
|
|
86
|
-
log.warn({
|
|
279
|
+
log.warn({
|
|
280
|
+
action: 'policy_daemon_failed',
|
|
281
|
+
err: err instanceof Error ? err.message : String(err),
|
|
282
|
+
}, 'Failed to refresh policy from daemon socket — falling back to HTTP');
|
|
87
283
|
}
|
|
88
284
|
}
|
|
89
285
|
const url = `${this.controlPlaneUrl}/api/v1/sop/rules?workspaceId=${encodeURIComponent(this.workspaceId)}&active=true`;
|
|
@@ -92,6 +288,11 @@ export class PolicyClient {
|
|
|
92
288
|
const parsed = JSON.parse(body);
|
|
93
289
|
const rules = Array.isArray(parsed.rules) ? parsed.rules : [];
|
|
94
290
|
this.rules = rules;
|
|
291
|
+
const dlp = parsed['dlpPatterns'];
|
|
292
|
+
this.dlpPatterns = Array.isArray(dlp)
|
|
293
|
+
? dlp.filter((p) => typeof p === 'string')
|
|
294
|
+
: [];
|
|
295
|
+
this.absorbCuration(parsed);
|
|
95
296
|
this.lastFetchAt = Date.now();
|
|
96
297
|
log.info({ action: 'policy_refreshed', ruleCount: rules.length }, 'SOP rules refreshed');
|
|
97
298
|
}
|
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,UAAU,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,kBAAkB,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
* Harness ↔ [McpGovernanceProxy as MCP Server] ↔ Control Plane REST API
|
|
10
10
|
* Exposes governance tools: intutic_governance_status, intutic_list_sops, intutic_list_incidents.
|
|
11
11
|
*
|
|
12
|
+
* Architecture (remote bridge mode — `--remote-url`, see remoteBridge.ts):
|
|
13
|
+
* Harness stdin → [McpGovernanceProxy] → remote MCP server (HTTP/SSE)
|
|
14
|
+
* Remote MCP server → [McpGovernanceProxy] → Harness stdout
|
|
15
|
+
* The harness still spawns this proxy as an ordinary stdio child process —
|
|
16
|
+
* only the UPSTREAM side changes, from a spawned child's stdin/stdout to an
|
|
17
|
+
* MCP SDK client transport talking HTTP/SSE. `handleHarnessLine` (request
|
|
18
|
+
* direction) and `handleServerLine` (response direction) are the exact same
|
|
19
|
+
* functions proxy mode uses; remoteBridge.ts only supplies a different
|
|
20
|
+
* upstream and a different `forward`.
|
|
21
|
+
*
|
|
12
22
|
* CRITICAL: Never write to process.stdout except for valid JSON-RPC frames.
|
|
13
23
|
* All logging MUST go to process.stderr via @intutic/logger.
|
|
14
24
|
*
|
|
@@ -17,13 +27,187 @@
|
|
|
17
27
|
* @module
|
|
18
28
|
*/
|
|
19
29
|
import type { ProxyConfig } from './config.js';
|
|
30
|
+
import { ToolCallInterceptor } from './interceptor.js';
|
|
31
|
+
import { type InjectionSource } from './injection.js';
|
|
32
|
+
import { SessionState } from './session.js';
|
|
33
|
+
/** What the harness asked for under a given request id, awaiting its response. */
|
|
34
|
+
export interface PendingRequest {
|
|
35
|
+
method: 'tools/call' | 'tools/list' | 'resources/read';
|
|
36
|
+
toolName?: string;
|
|
37
|
+
}
|
|
38
|
+
/** The outcome of inspecting one server→harness line. */
|
|
39
|
+
export interface ServerLineOutcome {
|
|
40
|
+
/** The line to forward — possibly rewritten. */
|
|
41
|
+
line: string;
|
|
42
|
+
/** Set when result content was redacted: the tool (or resource) it came from. */
|
|
43
|
+
redactedTool?: string;
|
|
44
|
+
/** Pattern descriptions redacted out of the result. */
|
|
45
|
+
redactions?: string[];
|
|
46
|
+
/** Set when a tools/list response was filtered or had descriptions overridden. */
|
|
47
|
+
curated?: {
|
|
48
|
+
hidden: number;
|
|
49
|
+
overridden: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The (post-curation) tool array, set whenever this line was a `tools/list`
|
|
53
|
+
* response — regardless of whether curation changed anything — so the
|
|
54
|
+
* caller can run TOFU pinning (tofu.ts) over it. `undefined` for every
|
|
55
|
+
* other line.
|
|
56
|
+
*/
|
|
57
|
+
toolsListTools?: Array<Record<string, unknown>>;
|
|
58
|
+
/** The JSON-RPC id of the `tools/list` response, needed to build a block
|
|
59
|
+
* frame in the caller if TOFU refuses it. Set alongside `toolsListTools`. */
|
|
60
|
+
toolsListMsgId?: string | number | null;
|
|
61
|
+
/**
|
|
62
|
+
* Prompt-injection findings (injection.ts) from this line, one entry per
|
|
63
|
+
* scanned surface that matched — a `tools/call`/`resources/read` result
|
|
64
|
+
* (`tool_result`) yields at most one entry; a `tools/list` response can
|
|
65
|
+
* yield one per tool whose (post-curation) description matched. The
|
|
66
|
+
* caller (`handleServerLine`) emits `injection_detected` (and, on a
|
|
67
|
+
* response-direction block, `tool_blocked`) from these — `processServerLine`
|
|
68
|
+
* itself stays a pure function with no emitter access, the same reason
|
|
69
|
+
* `redactedTool`/`redactions` exist as outcome fields rather than emitting
|
|
70
|
+
* directly.
|
|
71
|
+
*/
|
|
72
|
+
injectionFindings?: Array<{
|
|
73
|
+
source: InjectionSource;
|
|
74
|
+
toolName: string;
|
|
75
|
+
patterns: string[];
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* Set when a response-direction injection match, under `mcpInjectionAction:
|
|
79
|
+
* 'block'`, caused `line` to carry a withheld-result error frame instead of
|
|
80
|
+
* the real result — mirrors `redactedTool` for the injection path, so the
|
|
81
|
+
* caller can log/emit the block distinctly from a plain redaction.
|
|
82
|
+
*/
|
|
83
|
+
injectionBlocked?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Phase 2's `dlp_escalation` finding (anomaly/detectors.ts), set when a
|
|
86
|
+
* `tools/call`/`resources/read` result carried ≥3 distinct DLP-redacted
|
|
87
|
+
* pattern types. Severity-escalation only — see `dlpEscalation`'s own doc
|
|
88
|
+
* comment for why this never becomes a NEW block: the content is already
|
|
89
|
+
* redacted (`redactions` above), and this proxy's DLP scan already blocks
|
|
90
|
+
* any request-direction finding unconditionally, so escalating an
|
|
91
|
+
* already-redacted RESPONSE protects nothing further.
|
|
92
|
+
*/
|
|
93
|
+
dlpEscalationReason?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Phase 2's `tool_poisoning` finding (anomaly/detectors.ts), set on a
|
|
96
|
+
* `tools/list` response whose post-curation tool descriptions matched
|
|
97
|
+
* `toolPoison.ts`'s 7-pattern scanner. Report-only, same as
|
|
98
|
+
* `injectionFindings` on this same response type — never removes or
|
|
99
|
+
* blocks a listing.
|
|
100
|
+
*/
|
|
101
|
+
toolPoisoningReason?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Inspect one line of real-server output before it reaches the harness.
|
|
105
|
+
*
|
|
106
|
+
* This is the seam the byte-for-byte pipe never had: the request direction has
|
|
107
|
+
* been scanned since the interceptor existed, while the real server's stdout —
|
|
108
|
+
* tool results, resource reads, every tools/list — streamed into the agent's
|
|
109
|
+
* context untouched. A result carrying a credential put it straight into
|
|
110
|
+
* model-visible context; a `redact` Decision variant and a `tool_redacted`
|
|
111
|
+
* event existed as declared types with no producer. This function is the
|
|
112
|
+
* producer.
|
|
113
|
+
*
|
|
114
|
+
* Redaction, not blocking, on results: the call already executed, so refusing
|
|
115
|
+
* the response protects nothing — keeping the secret out of the agent's
|
|
116
|
+
* context is the only thing still at stake. If the redacted result no longer
|
|
117
|
+
* parses (a secret that spanned JSON syntax), the whole result is withheld
|
|
118
|
+
* and replaced with an error naming why — forwarding either the original or
|
|
119
|
+
* a broken body would be worse, the same fail-closed reparse rule the Rust
|
|
120
|
+
* proxy applies to non-streaming bodies.
|
|
121
|
+
*
|
|
122
|
+
* tools/list curation (the Uber-gateway mechanism): when the workspace
|
|
123
|
+
* declares an additive allowlist, tools outside it are removed from the
|
|
124
|
+
* listing — an agent that never sees a tool does not hallucinate calls to
|
|
125
|
+
* it, and the call-time block in the interceptor stays as the enforcement
|
|
126
|
+
* backstop. Operator description overrides apply to what remains, which is
|
|
127
|
+
* also the counter to a poisoned upstream description — the pin detects the
|
|
128
|
+
* change; the override controls what the agent actually reads.
|
|
129
|
+
*/
|
|
130
|
+
export declare function processServerLine(raw: string, pending: Map<string | number, PendingRequest>, allowedTools: readonly string[], overrides: Readonly<Record<string, string>>, injectionAction?: 'warn' | 'block', injectionPatterns?: readonly string[]): ServerLineOutcome;
|
|
131
|
+
/**
|
|
132
|
+
* Handle one harness→upstream line — shared, byte-for-byte, between stdio
|
|
133
|
+
* proxy mode (`McpGovernanceProxy.runProxy`, below) and the remote HTTP/SSE
|
|
134
|
+
* bridge (`remoteBridge.ts`'s `runRemoteProxy`): parse JSON-RPC, register any
|
|
135
|
+
* `tools/list`/`resources/read` request so its response gets inspected on the
|
|
136
|
+
* way back (see `processServerLine`), and run every `tools/call` through the
|
|
137
|
+
* SAME `interceptor.decide()` call this proxy has always used. `forward` is
|
|
138
|
+
* the ONLY thing that differs by upstream transport — stdio mode writes to
|
|
139
|
+
* the spawned child process's stdin, remote bridge mode calls the MCP SDK
|
|
140
|
+
* transport's `send()` — so pulling it out as a parameter, rather than
|
|
141
|
+
* forking this whole function, is what keeps governance identical across
|
|
142
|
+
* both upstream transports instead of two copies that could quietly drift.
|
|
143
|
+
*
|
|
144
|
+
* A free function (like `processServerLine`), not a class method, precisely
|
|
145
|
+
* so `remoteBridge.ts` can call it without needing a `McpGovernanceProxy`
|
|
146
|
+
* instance shaped for the stdio child-process lifecycle it doesn't have.
|
|
147
|
+
*/
|
|
148
|
+
export declare function handleHarnessLine(line: string, pending: Map<string | number, PendingRequest>, interceptor: ToolCallInterceptor, forward: (line: string) => void, session?: SessionState): void;
|
|
20
149
|
export declare class McpGovernanceProxy {
|
|
21
150
|
private readonly config;
|
|
22
151
|
private readonly policy;
|
|
23
152
|
private readonly emitter;
|
|
24
153
|
private readonly interceptor;
|
|
154
|
+
/**
|
|
155
|
+
* Phase 2's session state — the tool-call sequence and per-detector reask
|
|
156
|
+
* counters. ONE instance per proxy process, shared with the interceptor
|
|
157
|
+
* (for detection + reask counting), `handleHarnessLine` (for post-decision
|
|
158
|
+
* recording), and this class's own `handleServerLine` (for caching the
|
|
159
|
+
* post-curation tools/list the tool_poisoning detector reads). Since Wave
|
|
160
|
+
* 5.3 (TD-437) it is also shared ACROSS the session's sibling proxy
|
|
161
|
+
* processes through `sessionStore` when a Valkey URL is configured — see
|
|
162
|
+
* session.ts's module doc.
|
|
163
|
+
*/
|
|
164
|
+
private readonly session;
|
|
165
|
+
/** The Valkey-backed shared window, when `config.valkeyUrl` is set and this is not the standalone entry. */
|
|
166
|
+
private readonly sessionStore;
|
|
167
|
+
/**
|
|
168
|
+
* Phase 3's WASM custom-rule runner — owns the one dedicated
|
|
169
|
+
* `worker_threads` Worker and the `~/.intutic/wasm/` directory loader.
|
|
170
|
+
* Rescanned on the SAME 60s policy-tick timer `PolicyClient.start` already
|
|
171
|
+
* runs, not a second one (see `policy.ts`'s `start(onTick)`).
|
|
172
|
+
*/
|
|
173
|
+
private readonly wasmRunner;
|
|
25
174
|
private realServer;
|
|
26
175
|
constructor(config: ProxyConfig);
|
|
176
|
+
/**
|
|
177
|
+
* The tools/call decision engine — exposed so `remoteBridge.ts` can drive
|
|
178
|
+
* `handleHarnessLine` (the exported free function above) against the SAME
|
|
179
|
+
* interceptor instance this class builds from `config` in its constructor,
|
|
180
|
+
* rather than constructing a second one that could drift.
|
|
181
|
+
*/
|
|
182
|
+
getInterceptor(): ToolCallInterceptor;
|
|
183
|
+
/**
|
|
184
|
+
* The shared session state — exposed for the same reason `getInterceptor`
|
|
185
|
+
* is: `remoteBridge.ts` drives `handleHarnessLine` with this exact
|
|
186
|
+
* instance so the sequence detectors read and the sequence
|
|
187
|
+
* `handleHarnessLine` records into are never two different objects.
|
|
188
|
+
*/
|
|
189
|
+
getSessionState(): SessionState;
|
|
190
|
+
/**
|
|
191
|
+
* Start the policy client's background refresh timer. Exposed alongside
|
|
192
|
+
* `stopPolicy` so `remoteBridge.ts` can drive the same policy lifecycle
|
|
193
|
+
* `runProxy` below drives for stdio mode, without reaching into `policy`
|
|
194
|
+
* (private) directly. Also rides this same tick to rescan Phase 3's
|
|
195
|
+
* `~/.intutic/wasm/` directory — see `policy.ts`'s `start(onTick)` doc.
|
|
196
|
+
*/
|
|
197
|
+
startPolicy(): void;
|
|
198
|
+
private wasmWatch;
|
|
199
|
+
/**
|
|
200
|
+
* Hot reload for `~/.intutic/wasm/` (TD-442): a directory watcher, not a
|
|
201
|
+
* second timer, so a dropped-in rule takes effect within a second instead
|
|
202
|
+
* of at the next 60-second tick. Re-armed on every tick because the
|
|
203
|
+
* directory may not exist when the proxy starts.
|
|
204
|
+
*/
|
|
205
|
+
private ensureWasmWatch;
|
|
206
|
+
/**
|
|
207
|
+
* Stop the policy client's background refresh timer, and shut down the
|
|
208
|
+
* WASM worker thread. See `startPolicy`.
|
|
209
|
+
*/
|
|
210
|
+
stopPolicy(): void;
|
|
27
211
|
/**
|
|
28
212
|
* Start the proxy. Dispatches to standalone or proxy mode based on config.
|
|
29
213
|
*/
|
|
@@ -34,6 +218,27 @@ export declare class McpGovernanceProxy {
|
|
|
34
218
|
* Gracefully degrades when the control plane is unreachable.
|
|
35
219
|
*/
|
|
36
220
|
private runStandalone;
|
|
221
|
+
/**
|
|
222
|
+
* Inspects one real-server → harness line (see `processServerLine`) and
|
|
223
|
+
* writes the outcome to stdout. Async, unlike the byte pipe this replaced,
|
|
224
|
+
* because a `tools/list` response also runs server-level TOFU pinning
|
|
225
|
+
* (tofu.ts), which does file I/O — the ONLY case this awaits before
|
|
226
|
+
* writing; every other line is forwarded exactly as fast as before.
|
|
227
|
+
*
|
|
228
|
+
* On a TOFU mismatch, honors `mcpProxyFailBehavior` via `config.failOpen` —
|
|
229
|
+
* the SAME field `interceptor.ts` already reads for its own fail-open/
|
|
230
|
+
* fail-closed branches, not a new mechanism: fail-open logs and forwards
|
|
231
|
+
* the (possibly curated) tools/list response as normal; fail-closed
|
|
232
|
+
* replaces it with a JSON-RPC error naming the server and the setting,
|
|
233
|
+
* mirroring `buildBlockResponse`'s existing wording style.
|
|
234
|
+
*
|
|
235
|
+
* Public (not `private`) so `remoteBridge.ts` can hand it every message
|
|
236
|
+
* `transport.onmessage` receives from a remote upstream, VERBATIM — DLP
|
|
237
|
+
* redaction, TOFU pinning, and event emission on the response direction
|
|
238
|
+
* must apply identically regardless of which upstream transport produced
|
|
239
|
+
* the line, and this is the one place that logic lives.
|
|
240
|
+
*/
|
|
241
|
+
handleServerLine(rawLine: string, pending: Map<string | number, PendingRequest>): Promise<void>;
|
|
37
242
|
/**
|
|
38
243
|
* Start the proxy. Spawns the real MCP server and begins proxying stdin/stdout.
|
|
39
244
|
* Returns a promise that resolves when the real server exits.
|
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|