@provos/ironcurtain 0.1.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/LICENSE +202 -0
- package/README.md +311 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.js +71 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompts.d.ts +5 -0
- package/dist/agent/prompts.js +26 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.js +51 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/constitution.md +16 -0
- package/dist/config/generated/compiled-policy.json +236 -0
- package/dist/config/generated/test-scenarios.json +765 -0
- package/dist/config/generated/tool-annotations.json +955 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +151 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp-servers.json +22 -0
- package/dist/config/model-provider.d.ts +49 -0
- package/dist/config/model-provider.js +78 -0
- package/dist/config/model-provider.js.map +1 -0
- package/dist/config/paths.d.ts +59 -0
- package/dist/config/paths.js +96 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/types.d.ts +89 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/user-config.d.ts +93 -0
- package/dist/config/user-config.js +309 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/hash.d.ts +17 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +93 -0
- package/dist/logger.js.map +1 -0
- package/dist/pipeline/annotate.d.ts +9 -0
- package/dist/pipeline/annotate.js +136 -0
- package/dist/pipeline/annotate.js.map +1 -0
- package/dist/pipeline/compile.d.ts +23 -0
- package/dist/pipeline/compile.js +386 -0
- package/dist/pipeline/compile.js.map +1 -0
- package/dist/pipeline/constitution-compiler.d.ts +22 -0
- package/dist/pipeline/constitution-compiler.js +197 -0
- package/dist/pipeline/constitution-compiler.js.map +1 -0
- package/dist/pipeline/generate-with-repair.d.ts +22 -0
- package/dist/pipeline/generate-with-repair.js +64 -0
- package/dist/pipeline/generate-with-repair.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
- package/dist/pipeline/handwritten-scenarios.js +321 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -0
- package/dist/pipeline/llm-logger.d.ts +42 -0
- package/dist/pipeline/llm-logger.js +78 -0
- package/dist/pipeline/llm-logger.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +47 -0
- package/dist/pipeline/pipeline-shared.js +145 -0
- package/dist/pipeline/pipeline-shared.js.map +1 -0
- package/dist/pipeline/policy-verifier.d.ts +46 -0
- package/dist/pipeline/policy-verifier.js +277 -0
- package/dist/pipeline/policy-verifier.js.map +1 -0
- package/dist/pipeline/scenario-generator.d.ts +11 -0
- package/dist/pipeline/scenario-generator.js +128 -0
- package/dist/pipeline/scenario-generator.js.map +1 -0
- package/dist/pipeline/tool-annotator.d.ts +24 -0
- package/dist/pipeline/tool-annotator.js +201 -0
- package/dist/pipeline/tool-annotator.js.map +1 -0
- package/dist/pipeline/types.d.ts +122 -0
- package/dist/pipeline/types.js +10 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/sandbox/index.d.ts +39 -0
- package/dist/sandbox/index.js +178 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/session/agent-session.d.ts +83 -0
- package/dist/session/agent-session.js +382 -0
- package/dist/session/agent-session.js.map +1 -0
- package/dist/session/cli-transport.d.ts +61 -0
- package/dist/session/cli-transport.js +320 -0
- package/dist/session/cli-transport.js.map +1 -0
- package/dist/session/errors.d.ts +19 -0
- package/dist/session/errors.js +33 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/index.d.ts +29 -0
- package/dist/session/index.js +104 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/message-compactor.d.ts +32 -0
- package/dist/session/message-compactor.js +81 -0
- package/dist/session/message-compactor.js.map +1 -0
- package/dist/session/prompts.d.ts +5 -0
- package/dist/session/prompts.js +62 -0
- package/dist/session/prompts.js.map +1 -0
- package/dist/session/resource-budget-tracker.d.ts +124 -0
- package/dist/session/resource-budget-tracker.js +327 -0
- package/dist/session/resource-budget-tracker.js.map +1 -0
- package/dist/session/step-loop-detector.d.ts +63 -0
- package/dist/session/step-loop-detector.js +136 -0
- package/dist/session/step-loop-detector.js.map +1 -0
- package/dist/session/transport.d.ts +24 -0
- package/dist/session/transport.js +2 -0
- package/dist/session/transport.js.map +1 -0
- package/dist/session/truncate-result.d.ts +35 -0
- package/dist/session/truncate-result.js +71 -0
- package/dist/session/truncate-result.js.map +1 -0
- package/dist/session/types.d.ts +220 -0
- package/dist/session/types.js +6 -0
- package/dist/session/types.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +7 -0
- package/dist/trusted-process/audit-log.js +21 -0
- package/dist/trusted-process/audit-log.js.map +1 -0
- package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
- package/dist/trusted-process/call-circuit-breaker.js +61 -0
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +7 -0
- package/dist/trusted-process/escalation.js +38 -0
- package/dist/trusted-process/escalation.js.map +1 -0
- package/dist/trusted-process/index.d.ts +32 -0
- package/dist/trusted-process/index.js +151 -0
- package/dist/trusted-process/index.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
- package/dist/trusted-process/mcp-client-manager.js +90 -0
- package/dist/trusted-process/mcp-client-manager.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
- package/dist/trusted-process/mcp-proxy-server.js +451 -0
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
- package/dist/trusted-process/path-utils.d.ts +50 -0
- package/dist/trusted-process/path-utils.js +158 -0
- package/dist/trusted-process/path-utils.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +88 -0
- package/dist/trusted-process/policy-engine.js +523 -0
- package/dist/trusted-process/policy-engine.js.map +1 -0
- package/dist/trusted-process/policy-roots.d.ts +50 -0
- package/dist/trusted-process/policy-roots.js +67 -0
- package/dist/trusted-process/policy-roots.js.map +1 -0
- package/dist/trusted-process/policy-types.d.ts +6 -0
- package/dist/trusted-process/policy-types.js +2 -0
- package/dist/trusted-process/policy-types.js.map +1 -0
- package/dist/trusted-process/sandbox-integration.d.ts +92 -0
- package/dist/trusted-process/sandbox-integration.js +184 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -0
- package/dist/types/argument-roles.d.ts +112 -0
- package/dist/types/argument-roles.js +344 -0
- package/dist/types/argument-roles.js.map +1 -0
- package/dist/types/audit.d.ts +18 -0
- package/dist/types/audit.js +2 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/mcp.d.ts +20 -0
- package/dist/types/mcp.js +2 -0
- package/dist/types/mcp.js.map +1 -0
- package/package.json +83 -0
- package/src/config/constitution.md +16 -0
- package/src/config/generated/compiled-policy.json +236 -0
- package/src/config/generated/test-scenarios.json +765 -0
- package/src/config/generated/tool-annotations.json +955 -0
- package/src/config/mcp-servers.json +22 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CallCircuitBreaker -- Proxy-level rate limiter for MCP tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Protects against runaway sandbox code that hammers the same tool
|
|
5
|
+
* with identical arguments. Uses a sliding-window approach: if the
|
|
6
|
+
* same (tool, argsHash) pair appears more than `threshold` times
|
|
7
|
+
* within `windowMs`, the call is denied.
|
|
8
|
+
*
|
|
9
|
+
* Runs AFTER policy evaluation so every call is always audited.
|
|
10
|
+
*/
|
|
11
|
+
import { computeHash } from '../hash.js';
|
|
12
|
+
const DEFAULT_CONFIG = {
|
|
13
|
+
windowMs: 60_000,
|
|
14
|
+
threshold: 20,
|
|
15
|
+
};
|
|
16
|
+
export class CallCircuitBreaker {
|
|
17
|
+
config;
|
|
18
|
+
windows = new Map();
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check whether a tool call should be allowed.
|
|
24
|
+
*
|
|
25
|
+
* @returns `{ allowed: true }` or `{ allowed: false, reason: string }`
|
|
26
|
+
*/
|
|
27
|
+
check(toolName, args) {
|
|
28
|
+
const argsHash = computeHash(args);
|
|
29
|
+
const key = `${toolName}:${argsHash}`;
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const cutoff = now - this.config.windowMs;
|
|
32
|
+
let timestamps = this.windows.get(key);
|
|
33
|
+
if (!timestamps) {
|
|
34
|
+
timestamps = [];
|
|
35
|
+
this.windows.set(key, timestamps);
|
|
36
|
+
}
|
|
37
|
+
// Prune entries older than the window
|
|
38
|
+
const firstValid = timestamps.findIndex((t) => t > cutoff);
|
|
39
|
+
if (firstValid > 0) {
|
|
40
|
+
timestamps.splice(0, firstValid);
|
|
41
|
+
}
|
|
42
|
+
else if (firstValid === -1) {
|
|
43
|
+
timestamps.length = 0;
|
|
44
|
+
}
|
|
45
|
+
if (timestamps.length >= this.config.threshold) {
|
|
46
|
+
return {
|
|
47
|
+
allowed: false,
|
|
48
|
+
reason: `CIRCUIT BREAKER: Tool '${toolName}' called ${this.config.threshold} times ` +
|
|
49
|
+
`with identical arguments within ${this.config.windowMs / 1000}s window. ` +
|
|
50
|
+
'Try a different approach.',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
timestamps.push(now);
|
|
54
|
+
return { allowed: true };
|
|
55
|
+
}
|
|
56
|
+
/** Reset all state. */
|
|
57
|
+
reset() {
|
|
58
|
+
this.windows.clear();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=call-circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-circuit-breaker.js","sourceRoot":"","sources":["../../src/trusted-process/call-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOzC,MAAM,cAAc,GAAyB;IAC3C,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,EAAE;CACd,CAAC;AAMF,MAAM,OAAO,kBAAkB;IACZ,MAAM,CAAuB;IACtC,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C,YAAY,MAAsC;QAChD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACH,KAAK,CACH,QAAgB,EAChB,IAA6B;QAE7B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAE1C,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACpC,CAAC;QAED,sCAAsC;QACtC,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAC3D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC/C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EACJ,0BAA0B,QAAQ,YAAY,IAAI,CAAC,MAAM,CAAC,SAAS,SAAS;oBAC5E,mCAAmC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,YAAY;oBAC1E,2BAA2B;aAC9B,CAAC;QACJ,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,uBAAuB;IACvB,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
export class EscalationHandler {
|
|
3
|
+
rl = null;
|
|
4
|
+
getReadline() {
|
|
5
|
+
if (!this.rl) {
|
|
6
|
+
this.rl = createInterface({
|
|
7
|
+
input: process.stdin,
|
|
8
|
+
output: process.stderr,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return this.rl;
|
|
12
|
+
}
|
|
13
|
+
async prompt(request, reason) {
|
|
14
|
+
const rl = this.getReadline();
|
|
15
|
+
process.stderr.write('\n========================================\n');
|
|
16
|
+
process.stderr.write(' ESCALATION: Human approval required\n');
|
|
17
|
+
process.stderr.write('========================================\n');
|
|
18
|
+
process.stderr.write(` Tool: ${request.serverName}/${request.toolName}\n`);
|
|
19
|
+
process.stderr.write(` Arguments: ${JSON.stringify(request.arguments, null, 2)}\n`);
|
|
20
|
+
process.stderr.write(` Reason: ${reason}\n`);
|
|
21
|
+
process.stderr.write('========================================\n');
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
rl.question(' Approve? (y/N): ', (answer) => {
|
|
24
|
+
const approved = answer.trim().toLowerCase() === 'y';
|
|
25
|
+
process.stderr.write(approved ? ' -> APPROVED\n' : ' -> DENIED\n');
|
|
26
|
+
process.stderr.write('========================================\n\n');
|
|
27
|
+
resolve(approved ? 'approved' : 'denied');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
close() {
|
|
32
|
+
if (this.rl) {
|
|
33
|
+
this.rl.close();
|
|
34
|
+
this.rl = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=escalation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escalation.js","sourceRoot":"","sources":["../../src/trusted-process/escalation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAC;AAGhE,MAAM,OAAO,iBAAiB;IACpB,EAAE,GAAqB,IAAI,CAAC;IAE5B,WAAW;QACjB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,CAAC,EAAE,GAAG,eAAe,CAAC;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAwB,EAAE,MAAc;QACnD,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAE9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;QACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,IAAI,CAAC,CAAC;QACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAEnE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,EAAE,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC;gBACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;gBACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBACrE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IronCurtainConfig } from '../config/types.js';
|
|
2
|
+
import type { ToolCallRequest, ToolCallResult } from '../types/mcp.js';
|
|
3
|
+
export type EscalationPromptFn = (request: ToolCallRequest, reason: string) => Promise<'approved' | 'denied'>;
|
|
4
|
+
export interface TrustedProcessOptions {
|
|
5
|
+
onEscalation?: EscalationPromptFn;
|
|
6
|
+
}
|
|
7
|
+
export declare class TrustedProcess {
|
|
8
|
+
private config;
|
|
9
|
+
private policyEngine;
|
|
10
|
+
private mcpManager;
|
|
11
|
+
private mcpRoots;
|
|
12
|
+
private auditLog;
|
|
13
|
+
private escalation;
|
|
14
|
+
private onEscalation?;
|
|
15
|
+
constructor(config: IronCurtainConfig, options?: TrustedProcessOptions);
|
|
16
|
+
initialize(): Promise<void>;
|
|
17
|
+
listTools(serverName: string): Promise<{
|
|
18
|
+
name: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
inputSchema: unknown;
|
|
21
|
+
}[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Handles a tool call request through the full trusted process lifecycle:
|
|
24
|
+
* 1. Evaluate policy (allow / deny / escalate)
|
|
25
|
+
* 2. If escalated, prompt the human for approval
|
|
26
|
+
* 3. If allowed, forward to the real MCP server
|
|
27
|
+
* 4. Log the complete decision trace to the audit log
|
|
28
|
+
* 5. Return the result to the caller
|
|
29
|
+
*/
|
|
30
|
+
handleToolCall(request: ToolCallRequest): Promise<ToolCallResult>;
|
|
31
|
+
shutdown(): Promise<void>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { loadGeneratedPolicy, extractServerDomainAllowlists } from '../config/index.js';
|
|
2
|
+
import { PolicyEngine } from './policy-engine.js';
|
|
3
|
+
import { MCPClientManager } from './mcp-client-manager.js';
|
|
4
|
+
import { AuditLog } from './audit-log.js';
|
|
5
|
+
import { EscalationHandler } from './escalation.js';
|
|
6
|
+
import { prepareToolArgs } from './path-utils.js';
|
|
7
|
+
import { extractPolicyRoots, toMcpRoots, directoryForPath } from './policy-roots.js';
|
|
8
|
+
import * as logger from '../logger.js';
|
|
9
|
+
export class TrustedProcess {
|
|
10
|
+
config;
|
|
11
|
+
policyEngine;
|
|
12
|
+
mcpManager;
|
|
13
|
+
mcpRoots;
|
|
14
|
+
auditLog;
|
|
15
|
+
escalation;
|
|
16
|
+
onEscalation;
|
|
17
|
+
constructor(config, options) {
|
|
18
|
+
this.config = config;
|
|
19
|
+
const { compiledPolicy, toolAnnotations } = loadGeneratedPolicy(config.generatedDir);
|
|
20
|
+
const serverDomainAllowlists = extractServerDomainAllowlists(config.mcpServers);
|
|
21
|
+
this.policyEngine = new PolicyEngine(compiledPolicy, toolAnnotations, config.protectedPaths, config.allowedDirectory, serverDomainAllowlists);
|
|
22
|
+
const policyRoots = extractPolicyRoots(compiledPolicy, config.allowedDirectory);
|
|
23
|
+
this.mcpRoots = toMcpRoots(policyRoots);
|
|
24
|
+
this.mcpManager = new MCPClientManager();
|
|
25
|
+
this.auditLog = new AuditLog(config.auditLogPath);
|
|
26
|
+
this.escalation = new EscalationHandler();
|
|
27
|
+
this.onEscalation = options?.onEscalation;
|
|
28
|
+
}
|
|
29
|
+
async initialize() {
|
|
30
|
+
for (const [name, serverConfig] of Object.entries(this.config.mcpServers)) {
|
|
31
|
+
logger.info(`Connecting to MCP server: ${name}...`);
|
|
32
|
+
await this.mcpManager.connect(name, serverConfig, this.mcpRoots);
|
|
33
|
+
logger.info(`Connected to MCP server: ${name}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async listTools(serverName) {
|
|
37
|
+
return this.mcpManager.listTools(serverName);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Handles a tool call request through the full trusted process lifecycle:
|
|
41
|
+
* 1. Evaluate policy (allow / deny / escalate)
|
|
42
|
+
* 2. If escalated, prompt the human for approval
|
|
43
|
+
* 3. If allowed, forward to the real MCP server
|
|
44
|
+
* 4. Log the complete decision trace to the audit log
|
|
45
|
+
* 5. Return the result to the caller
|
|
46
|
+
*/
|
|
47
|
+
async handleToolCall(request) {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
// Annotation-driven normalization: split into transport vs policy args
|
|
50
|
+
const annotation = this.policyEngine.getAnnotation(request.serverName, request.toolName);
|
|
51
|
+
const { argsForTransport, argsForPolicy } = prepareToolArgs(request.arguments, annotation, this.config.allowedDirectory);
|
|
52
|
+
const policyRequest = { ...request, arguments: argsForPolicy };
|
|
53
|
+
const transportRequest = { ...request, arguments: argsForTransport };
|
|
54
|
+
// Step 1: Evaluate request against the policy rule chain
|
|
55
|
+
const evaluation = this.policyEngine.evaluate(policyRequest);
|
|
56
|
+
const policyDecision = {
|
|
57
|
+
status: evaluation.decision,
|
|
58
|
+
rule: evaluation.rule,
|
|
59
|
+
reason: evaluation.reason,
|
|
60
|
+
};
|
|
61
|
+
let escalationResult;
|
|
62
|
+
let resultContent;
|
|
63
|
+
let resultStatus;
|
|
64
|
+
let resultError;
|
|
65
|
+
try {
|
|
66
|
+
// Step 2: Handle escalation via human approval
|
|
67
|
+
if (evaluation.decision === 'escalate') {
|
|
68
|
+
escalationResult = this.onEscalation
|
|
69
|
+
? await this.onEscalation(transportRequest, evaluation.reason)
|
|
70
|
+
: await this.escalation.prompt(transportRequest, evaluation.reason);
|
|
71
|
+
if (escalationResult === 'approved') {
|
|
72
|
+
policyDecision.status = 'allow';
|
|
73
|
+
policyDecision.reason = 'Approved by human during escalation';
|
|
74
|
+
// Expand roots to include target directories so the filesystem
|
|
75
|
+
// server accepts the forwarded call.
|
|
76
|
+
const paths = Object.values(transportRequest.arguments).filter((v) => typeof v === 'string');
|
|
77
|
+
for (const p of paths) {
|
|
78
|
+
const dir = directoryForPath(p);
|
|
79
|
+
await this.mcpManager.addRoot(transportRequest.serverName, {
|
|
80
|
+
uri: `file://${dir}`,
|
|
81
|
+
name: 'escalation-approved',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
policyDecision.status = 'deny';
|
|
87
|
+
policyDecision.reason = 'Denied by human during escalation';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Step 3: Forward to MCP server or deny (using transport args)
|
|
91
|
+
if (policyDecision.status === 'allow') {
|
|
92
|
+
const mcpResult = await this.mcpManager.callTool(transportRequest.serverName, transportRequest.toolName, transportRequest.arguments);
|
|
93
|
+
resultContent = mcpResult;
|
|
94
|
+
if (mcpResult.isError) {
|
|
95
|
+
resultStatus = 'error';
|
|
96
|
+
const content = mcpResult.content;
|
|
97
|
+
if (Array.isArray(content)) {
|
|
98
|
+
resultError = content
|
|
99
|
+
.filter((c) => c.type === 'text' && typeof c.text === 'string')
|
|
100
|
+
.map((c) => c.text)
|
|
101
|
+
.join('\n');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
resultStatus = 'success';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
resultContent = { denied: true, reason: policyDecision.reason };
|
|
110
|
+
resultStatus = 'denied';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
resultStatus = 'error';
|
|
115
|
+
resultError = err instanceof Error ? err.message : String(err);
|
|
116
|
+
resultContent = { error: resultError };
|
|
117
|
+
}
|
|
118
|
+
const durationMs = Date.now() - startTime;
|
|
119
|
+
// Step 4: Append-only audit log (records argsForTransport -- what was sent to MCP server)
|
|
120
|
+
const auditEntry = {
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
requestId: transportRequest.requestId,
|
|
123
|
+
serverName: transportRequest.serverName,
|
|
124
|
+
toolName: transportRequest.toolName,
|
|
125
|
+
arguments: transportRequest.arguments,
|
|
126
|
+
policyDecision,
|
|
127
|
+
escalationResult,
|
|
128
|
+
result: {
|
|
129
|
+
status: resultStatus,
|
|
130
|
+
content: resultStatus === 'success' ? resultContent : undefined,
|
|
131
|
+
error: resultError,
|
|
132
|
+
},
|
|
133
|
+
durationMs,
|
|
134
|
+
};
|
|
135
|
+
this.auditLog.log(auditEntry);
|
|
136
|
+
// Step 5: Return result to caller
|
|
137
|
+
return {
|
|
138
|
+
requestId: transportRequest.requestId,
|
|
139
|
+
status: resultStatus,
|
|
140
|
+
content: resultContent,
|
|
141
|
+
policyDecision,
|
|
142
|
+
durationMs,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async shutdown() {
|
|
146
|
+
this.escalation.close();
|
|
147
|
+
await this.mcpManager.closeAll();
|
|
148
|
+
await this.auditLog.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trusted-process/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MCPServerConfig } from '../config/types.js';
|
|
2
|
+
export interface McpRoot {
|
|
3
|
+
uri: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class MCPClientManager {
|
|
7
|
+
private servers;
|
|
8
|
+
connect(name: string, config: MCPServerConfig, roots?: McpRoot[]): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Adds a root directory to a connected server and waits for the
|
|
11
|
+
* server to fetch the updated root list. This ensures the server's
|
|
12
|
+
* allowed directories include the new root before any tool call
|
|
13
|
+
* that depends on it is forwarded.
|
|
14
|
+
*
|
|
15
|
+
* No-op if the root URI is already present.
|
|
16
|
+
*/
|
|
17
|
+
addRoot(serverName: string, root: McpRoot): Promise<void>;
|
|
18
|
+
listTools(serverName: string): Promise<{
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
inputSchema: unknown;
|
|
22
|
+
}[]>;
|
|
23
|
+
callTool(serverName: string, toolName: string, args: Record<string, unknown>): Promise<unknown>;
|
|
24
|
+
closeAll(): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { CompatibilityCallToolResultSchema, ListRootsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import * as logger from '../logger.js';
|
|
5
|
+
export class MCPClientManager {
|
|
6
|
+
servers = new Map();
|
|
7
|
+
async connect(name, config, roots) {
|
|
8
|
+
const transport = new StdioClientTransport({
|
|
9
|
+
command: config.command,
|
|
10
|
+
args: config.args,
|
|
11
|
+
env: config.env ? { ...process.env, ...config.env } : undefined,
|
|
12
|
+
});
|
|
13
|
+
const client = new Client({ name: 'ironcurtain', version: '0.1.0' }, roots ? { capabilities: { roots: { listChanged: true } } } : {});
|
|
14
|
+
// Mutable copy -- addRoot() pushes to this array.
|
|
15
|
+
const mutableRoots = roots ? [...roots] : undefined;
|
|
16
|
+
const managed = { client, transport, roots: mutableRoots };
|
|
17
|
+
// When the server asks for roots, return the current set.
|
|
18
|
+
// If a rootsRefreshed callback is registered (from addRoot),
|
|
19
|
+
// resolve it so the caller knows the server has the latest roots.
|
|
20
|
+
if (mutableRoots) {
|
|
21
|
+
client.setRequestHandler(ListRootsRequestSchema, async () => {
|
|
22
|
+
if (managed.rootsRefreshed) {
|
|
23
|
+
managed.rootsRefreshed();
|
|
24
|
+
managed.rootsRefreshed = undefined;
|
|
25
|
+
}
|
|
26
|
+
return { roots: mutableRoots };
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
await client.connect(transport);
|
|
30
|
+
this.servers.set(name, managed);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Adds a root directory to a connected server and waits for the
|
|
34
|
+
* server to fetch the updated root list. This ensures the server's
|
|
35
|
+
* allowed directories include the new root before any tool call
|
|
36
|
+
* that depends on it is forwarded.
|
|
37
|
+
*
|
|
38
|
+
* No-op if the root URI is already present.
|
|
39
|
+
*/
|
|
40
|
+
async addRoot(serverName, root) {
|
|
41
|
+
const server = this.servers.get(serverName);
|
|
42
|
+
if (!server?.roots)
|
|
43
|
+
return;
|
|
44
|
+
// Deduplicate
|
|
45
|
+
if (server.roots.some(r => r.uri === root.uri))
|
|
46
|
+
return;
|
|
47
|
+
server.roots.push(root);
|
|
48
|
+
// Wait for the server to call roots/list after we notify it.
|
|
49
|
+
const refreshed = new Promise(resolve => {
|
|
50
|
+
server.rootsRefreshed = resolve;
|
|
51
|
+
});
|
|
52
|
+
await server.client.sendRootsListChanged();
|
|
53
|
+
await refreshed;
|
|
54
|
+
}
|
|
55
|
+
async listTools(serverName) {
|
|
56
|
+
const server = this.servers.get(serverName);
|
|
57
|
+
if (!server) {
|
|
58
|
+
throw new Error(`MCP server "${serverName}" not connected`);
|
|
59
|
+
}
|
|
60
|
+
const result = await server.client.listTools();
|
|
61
|
+
return result.tools.map(tool => ({
|
|
62
|
+
name: tool.name,
|
|
63
|
+
description: tool.description,
|
|
64
|
+
inputSchema: tool.inputSchema,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
async callTool(serverName, toolName, args) {
|
|
68
|
+
const server = this.servers.get(serverName);
|
|
69
|
+
if (!server) {
|
|
70
|
+
throw new Error(`MCP server "${serverName}" not connected`);
|
|
71
|
+
}
|
|
72
|
+
// TODO(workaround): Remove once @cyanheads/git-mcp-server fixes outputSchema declarations.
|
|
73
|
+
// See the detailed comment in mcp-proxy-server.ts for full context.
|
|
74
|
+
// CompatibilityCallToolResultSchema bypasses client-side output schema validation that
|
|
75
|
+
// fails when servers declare outputSchema but return non-conforming structuredContent.
|
|
76
|
+
return server.client.callTool({ name: toolName, arguments: args }, CompatibilityCallToolResultSchema);
|
|
77
|
+
}
|
|
78
|
+
async closeAll() {
|
|
79
|
+
for (const [name, server] of this.servers) {
|
|
80
|
+
try {
|
|
81
|
+
await server.client.close();
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
logger.error(`Error closing MCP server "${name}": ${err}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.servers.clear();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=mcp-client-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client-manager.js","sourceRoot":"","sources":["../../src/trusted-process/mcp-client-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Proxy Server -- The trusted process running as a standalone MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Code Mode spawns this as a child process via stdio transport. It acts
|
|
5
|
+
* as the security boundary between the sandbox and real MCP servers:
|
|
6
|
+
*
|
|
7
|
+
* 1. Connects to real MCP servers as a client
|
|
8
|
+
* 2. Exposes their tools with passthrough schemas
|
|
9
|
+
* 3. Evaluates every tool call against the policy engine
|
|
10
|
+
* 4. Forwards allowed calls to real servers, denies or escalates others
|
|
11
|
+
* 5. Logs every request and decision to the append-only audit log
|
|
12
|
+
*
|
|
13
|
+
* Configuration via environment variables:
|
|
14
|
+
* AUDIT_LOG_PATH -- path to the audit log file
|
|
15
|
+
* MCP_SERVERS_CONFIG -- JSON string of MCP server configs to proxy
|
|
16
|
+
* GENERATED_DIR -- path to the generated artifacts directory
|
|
17
|
+
* PROTECTED_PATHS -- JSON array of protected paths
|
|
18
|
+
* ALLOWED_DIRECTORY -- (optional) sandbox directory for structural containment check
|
|
19
|
+
* ESCALATION_DIR -- (optional) directory for file-based escalation IPC
|
|
20
|
+
* SESSION_LOG_PATH -- (optional) path for capturing child process stderr
|
|
21
|
+
* SANDBOX_POLICY -- (optional) "enforce" | "warn" (default: "warn")
|
|
22
|
+
* SERVER_FILTER -- (optional) when set, only connect to this single server name
|
|
23
|
+
*/
|
|
24
|
+
export {};
|