@integsec/agentic-pentest-proxy 0.2.9 → 0.3.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 +2 -0
- package/cloudflare/package-lock.json +1553 -0
- package/cloudflare/package.json +15 -0
- package/cloudflare/src/worker.mjs +125 -0
- package/cloudflare/wrangler.jsonc +26 -0
- package/dist/bin/integsec-agentic-pentest-bridge.d.ts +3 -0
- package/dist/bin/integsec-agentic-pentest-bridge.d.ts.map +1 -0
- package/dist/bin/integsec-agentic-pentest-bridge.js +23 -0
- package/dist/bin/integsec-agentic-pentest-bridge.js.map +1 -0
- package/dist/bin/integsec-agentic-pentest-gateway.d.ts +3 -0
- package/dist/bin/integsec-agentic-pentest-gateway.d.ts.map +1 -0
- package/dist/bin/integsec-agentic-pentest-gateway.js +44 -0
- package/dist/bin/integsec-agentic-pentest-gateway.js.map +1 -0
- package/dist/bin/integsec-agentic-pentest-proxy.js +6 -0
- package/dist/bin/integsec-agentic-pentest-proxy.js.map +1 -1
- package/dist/src/config.js +1 -1
- package/dist/src/gateway/admin.html +22 -0
- package/dist/src/gateway/admin.js +109 -0
- package/dist/src/gateway/auth.d.ts +22 -0
- package/dist/src/gateway/auth.d.ts.map +1 -0
- package/dist/src/gateway/auth.js +121 -0
- package/dist/src/gateway/auth.js.map +1 -0
- package/dist/src/gateway/runtime.d.ts +21 -0
- package/dist/src/gateway/runtime.d.ts.map +1 -0
- package/dist/src/gateway/runtime.js +175 -0
- package/dist/src/gateway/runtime.js.map +1 -0
- package/dist/src/gateway/server.d.ts +28 -0
- package/dist/src/gateway/server.d.ts.map +1 -0
- package/dist/src/gateway/server.js +316 -0
- package/dist/src/gateway/server.js.map +1 -0
- package/dist/src/gateway/state.d.ts +77 -0
- package/dist/src/gateway/state.d.ts.map +1 -0
- package/dist/src/gateway/state.js +154 -0
- package/dist/src/gateway/state.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/proxy.d.ts +4 -3
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +22 -9
- package/dist/src/proxy.js.map +1 -1
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.d.ts.map +1 -1
- package/docs/managed-gateway.md +90 -0
- package/package.json +11 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import type { AuditEntry } from "../types.js";
|
|
3
|
+
import { GatewayStore } from "./state.js";
|
|
4
|
+
export declare class GatewayRuntime {
|
|
5
|
+
readonly store: GatewayStore;
|
|
6
|
+
private readonly hosted;
|
|
7
|
+
private readonly requiredAuditSink?;
|
|
8
|
+
private readonly isActorAllowed?;
|
|
9
|
+
private clients;
|
|
10
|
+
private pending;
|
|
11
|
+
private proxies;
|
|
12
|
+
private proxyScope?;
|
|
13
|
+
constructor(store: GatewayStore, hosted: boolean, requiredAuditSink?: ((entry: AuditEntry) => Promise<void>) | undefined, isActorAllowed?: ((email?: string) => boolean) | undefined);
|
|
14
|
+
sync(): Promise<void>;
|
|
15
|
+
private scopeProxy;
|
|
16
|
+
private clientFor;
|
|
17
|
+
private available;
|
|
18
|
+
createServer(actor?: string, email?: string): Server;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/gateway/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAiE,MAAM,YAAY,CAAC;AAIzG,qBAAa,cAAc;IAMb,QAAQ,CAAC,KAAK,EAAE,YAAY;IAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAW,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IAAwC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IALvL,OAAO,CAAC,OAAO,CAA4D;IAC3E,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,UAAU,CAAC,CAAc;gBAEZ,KAAK,EAAE,YAAY,EAAmB,MAAM,EAAE,OAAO,EAAmB,iBAAiB,CAAC,GAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,aAAA,EAAmB,cAAc,CAAC,GAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,aAAA;IAE9M,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,OAAO,CAAC,UAAU;YAcJ,SAAS;IA2BvB,OAAO,CAAC,SAAS;IAOjB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IAmE9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { ScopeEnforcementProxy } from "../proxy.js";
|
|
8
|
+
import { loadConfig } from "../config.js";
|
|
9
|
+
import { activeScope, permitsUpstream } from "./state.js";
|
|
10
|
+
const toolError = (message) => ({ content: [{ type: "text", text: message }], isError: true });
|
|
11
|
+
export class GatewayRuntime {
|
|
12
|
+
store;
|
|
13
|
+
hosted;
|
|
14
|
+
requiredAuditSink;
|
|
15
|
+
isActorAllowed;
|
|
16
|
+
clients = new Map();
|
|
17
|
+
pending = new Map();
|
|
18
|
+
proxies = new Map();
|
|
19
|
+
proxyScope;
|
|
20
|
+
constructor(store, hosted, requiredAuditSink, isActorAllowed) {
|
|
21
|
+
this.store = store;
|
|
22
|
+
this.hosted = hosted;
|
|
23
|
+
this.requiredAuditSink = requiredAuditSink;
|
|
24
|
+
this.isActorAllowed = isActorAllowed;
|
|
25
|
+
}
|
|
26
|
+
async sync() {
|
|
27
|
+
const state = this.store.snapshot();
|
|
28
|
+
for (const [id, cached] of this.clients) {
|
|
29
|
+
const current = state.upstreams.find((item) => item.id === id);
|
|
30
|
+
if (!current || !current.enabled || JSON.stringify(current) !== cached.signature) {
|
|
31
|
+
this.clients.delete(id);
|
|
32
|
+
await cached.client.close().catch(() => undefined);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this.proxies.clear();
|
|
36
|
+
this.proxyScope = undefined;
|
|
37
|
+
}
|
|
38
|
+
scopeProxy(scope, serverName) {
|
|
39
|
+
if (this.proxyScope?.id !== scope.id || this.proxyScope.updatedAt !== scope.updatedAt) {
|
|
40
|
+
this.proxies.clear();
|
|
41
|
+
this.proxyScope = scope;
|
|
42
|
+
}
|
|
43
|
+
let proxy = this.proxies.get(serverName);
|
|
44
|
+
if (!proxy) {
|
|
45
|
+
proxy = new ScopeEnforcementProxy(scope.manifest, loadConfig(), this.requiredAuditSink);
|
|
46
|
+
proxy.setServerName(serverName);
|
|
47
|
+
this.proxies.set(serverName, proxy);
|
|
48
|
+
}
|
|
49
|
+
return proxy;
|
|
50
|
+
}
|
|
51
|
+
async clientFor(upstream) {
|
|
52
|
+
const signature = JSON.stringify(upstream);
|
|
53
|
+
const cached = this.clients.get(upstream.id);
|
|
54
|
+
if (cached?.signature === signature)
|
|
55
|
+
return cached.client;
|
|
56
|
+
const inFlight = this.pending.get(upstream.id);
|
|
57
|
+
if (inFlight)
|
|
58
|
+
return inFlight;
|
|
59
|
+
const connection = (async () => {
|
|
60
|
+
if (cached)
|
|
61
|
+
await cached.client.close().catch(() => undefined);
|
|
62
|
+
const client = new Client({ name: "agentic-pentest-gateway", version: "0.3.1" });
|
|
63
|
+
const transport = upstream.transport === "stdio"
|
|
64
|
+
? new StdioClientTransport({ command: upstream.command, args: upstream.args, stderr: "pipe" })
|
|
65
|
+
: upstream.transport === "sse"
|
|
66
|
+
? new SSEClientTransport(new URL(upstream.url))
|
|
67
|
+
: new StreamableHTTPClientTransport(new URL(upstream.url));
|
|
68
|
+
try {
|
|
69
|
+
await client.connect(transport, { timeout: 10_000 });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
await client.close().catch(() => undefined);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
this.clients.set(upstream.id, { client, signature });
|
|
76
|
+
return client;
|
|
77
|
+
})();
|
|
78
|
+
this.pending.set(upstream.id, connection);
|
|
79
|
+
try {
|
|
80
|
+
return await connection;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
this.pending.delete(upstream.id);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
available(scope) {
|
|
87
|
+
return this.store.snapshot().upstreams.filter((upstream) => {
|
|
88
|
+
if (this.hosted && upstream.transport === "stdio")
|
|
89
|
+
return false;
|
|
90
|
+
return permitsUpstream(scope, upstream);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
createServer(actor, email) {
|
|
94
|
+
const server = new Server({ name: "agentic-pentest-gateway", version: "0.3.1" }, { capabilities: { tools: { listChanged: true } } });
|
|
95
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
96
|
+
if (this.isActorAllowed && !this.isActorAllowed(email))
|
|
97
|
+
return { tools: [] };
|
|
98
|
+
const scope = activeScope(this.store.snapshot());
|
|
99
|
+
if (!scope)
|
|
100
|
+
return { tools: [] };
|
|
101
|
+
const groups = await Promise.all(this.available(scope).map(async (upstream) => {
|
|
102
|
+
const tools = [];
|
|
103
|
+
try {
|
|
104
|
+
const client = await this.clientFor(upstream);
|
|
105
|
+
let cursor;
|
|
106
|
+
do {
|
|
107
|
+
const page = await client.listTools(cursor ? { cursor } : undefined, { timeout: 10_000 });
|
|
108
|
+
for (const tool of page.tools)
|
|
109
|
+
tools.push({
|
|
110
|
+
name: `${upstream.name}__${tool.name}`,
|
|
111
|
+
description: tool.description,
|
|
112
|
+
inputSchema: tool.inputSchema,
|
|
113
|
+
});
|
|
114
|
+
cursor = page.nextCursor;
|
|
115
|
+
} while (cursor);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error(`[gateway] ${upstream.name} tool discovery failed:`, error);
|
|
119
|
+
}
|
|
120
|
+
return tools;
|
|
121
|
+
}));
|
|
122
|
+
return { tools: groups.flat() };
|
|
123
|
+
});
|
|
124
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
125
|
+
if (this.isActorAllowed && !this.isActorAllowed(email))
|
|
126
|
+
return toolError("User access was revoked.");
|
|
127
|
+
const scope = activeScope(this.store.snapshot());
|
|
128
|
+
if (!scope)
|
|
129
|
+
return toolError("No active scope. Ask an administrator to activate one.");
|
|
130
|
+
const upstream = this.available(scope).find((item) => request.params.name.startsWith(`${item.name}__`));
|
|
131
|
+
if (!upstream)
|
|
132
|
+
return toolError("Tool server is disabled or prohibited by the active execution profile.");
|
|
133
|
+
const toolName = request.params.name.slice(upstream.name.length + 2);
|
|
134
|
+
if (!toolName)
|
|
135
|
+
return toolError("Invalid tool name.");
|
|
136
|
+
const proxy = this.scopeProxy(scope, upstream.name);
|
|
137
|
+
const decision = await proxy.handleMessage({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: toolName, arguments: request.params.arguments ?? {} } }, actor);
|
|
138
|
+
if (!decision.forward) {
|
|
139
|
+
const response = decision.response;
|
|
140
|
+
return toolError(response.error?.message ?? "Blocked by the active scope.");
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const client = await this.clientFor(upstream);
|
|
144
|
+
let cursor;
|
|
145
|
+
let found = false;
|
|
146
|
+
do {
|
|
147
|
+
const page = await client.listTools(cursor ? { cursor } : undefined, { timeout: 10_000 });
|
|
148
|
+
found ||= page.tools.some((tool) => tool.name === toolName);
|
|
149
|
+
cursor = page.nextCursor;
|
|
150
|
+
} while (!found && cursor);
|
|
151
|
+
if (!found)
|
|
152
|
+
return toolError("Tool is no longer advertised by the upstream server.");
|
|
153
|
+
const latest = this.store.snapshot();
|
|
154
|
+
const latestScope = activeScope(latest);
|
|
155
|
+
const latestUpstream = latest.upstreams.find((item) => item.id === upstream.id);
|
|
156
|
+
if (!latestScope || latestScope.id !== scope.id || latestScope.updatedAt !== scope.updatedAt || !latestUpstream || JSON.stringify(latestUpstream) !== JSON.stringify(upstream) || !this.available(latestScope).some((item) => item.id === latestUpstream.id)) {
|
|
157
|
+
return toolError("Scope or tool configuration changed before dispatch.");
|
|
158
|
+
}
|
|
159
|
+
if (this.isActorAllowed && !this.isActorAllowed(email))
|
|
160
|
+
return toolError("User access was revoked before dispatch.");
|
|
161
|
+
return await client.callTool({ name: toolName, arguments: request.params.arguments ?? {} }, undefined, { timeout: 300_000 });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error(`[gateway] ${upstream.name} tool call failed:`, error);
|
|
165
|
+
return toolError("Upstream tool call failed. See gateway logs.");
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return server;
|
|
169
|
+
}
|
|
170
|
+
async close() {
|
|
171
|
+
await Promise.all([...this.clients.values()].map(({ client }) => client.close().catch(() => undefined)));
|
|
172
|
+
this.clients.clear();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/gateway/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
|
+
import { GatewayRuntime } from "./runtime.js";
|
|
3
|
+
import { GatewayStore } from "./state.js";
|
|
4
|
+
export interface GatewayOptions {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
statePath: string;
|
|
8
|
+
adminToken?: string;
|
|
9
|
+
bootstrapAdminEmails?: string[];
|
|
10
|
+
publicUrl?: string;
|
|
11
|
+
entraTenant?: string;
|
|
12
|
+
entraAudience?: string;
|
|
13
|
+
entraScope?: string;
|
|
14
|
+
authMode?: "entra" | "cloudflare";
|
|
15
|
+
cloudflareTeamDomain?: string;
|
|
16
|
+
cloudflareAccessAud?: string;
|
|
17
|
+
internalToken?: string;
|
|
18
|
+
auditWebhookUrl?: string;
|
|
19
|
+
auditWebhookToken?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function createGateway(options: GatewayOptions): {
|
|
22
|
+
server: import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
23
|
+
store: GatewayStore;
|
|
24
|
+
runtime: GatewayRuntime;
|
|
25
|
+
listen: () => Promise<void>;
|
|
26
|
+
close: () => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/gateway/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAOpF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAqD,MAAM,YAAY,CAAC;AAO7F,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AA0BD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc;;;;;;EAwLpD"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { timingSafeEqual } from "node:crypto";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import { GatewayAuth } from "./auth.js";
|
|
8
|
+
import { GatewayRuntime } from "./runtime.js";
|
|
9
|
+
import { GatewayStore, activeScope, parseScope, parseUpstream, parseUser } from "./state.js";
|
|
10
|
+
const MAX_BODY = 1024 * 1024;
|
|
11
|
+
const html = readFileSync(fileURLToPath(new URL("./admin.html", import.meta.url)), "utf8");
|
|
12
|
+
const js = readFileSync(fileURLToPath(new URL("./admin.js", import.meta.url)), "utf8");
|
|
13
|
+
function json(res, code, body) {
|
|
14
|
+
res.writeHead(code, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
15
|
+
res.end(JSON.stringify(body));
|
|
16
|
+
}
|
|
17
|
+
async function body(req) {
|
|
18
|
+
const chunks = [];
|
|
19
|
+
let size = 0;
|
|
20
|
+
for await (const chunk of req) {
|
|
21
|
+
size += chunk.length;
|
|
22
|
+
if (size > MAX_BODY)
|
|
23
|
+
throw new Error("Request exceeds 1 MiB");
|
|
24
|
+
chunks.push(chunk);
|
|
25
|
+
}
|
|
26
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
27
|
+
}
|
|
28
|
+
function authorizedAdmin(req, token) {
|
|
29
|
+
if (!token)
|
|
30
|
+
return false;
|
|
31
|
+
const supplied = /^Bearer (.+)$/i.exec(req.headers.authorization ?? "")?.[1] ?? "";
|
|
32
|
+
const a = Buffer.from(supplied);
|
|
33
|
+
const b = Buffer.from(token);
|
|
34
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
35
|
+
}
|
|
36
|
+
export function createGateway(options) {
|
|
37
|
+
const accessMode = Boolean(options.publicUrl && options.authMode === "cloudflare");
|
|
38
|
+
if (!accessMode && (options.adminToken?.length ?? 0) < 32)
|
|
39
|
+
throw new Error("GATEWAY_ADMIN_TOKEN must be at least 32 characters");
|
|
40
|
+
if (accessMode && !options.bootstrapAdminEmails?.length)
|
|
41
|
+
throw new Error("Cloudflare mode requires at least one GATEWAY_BOOTSTRAP_ADMIN_EMAILS entry");
|
|
42
|
+
if (options.host !== "127.0.0.1" && options.host !== "::1" && !options.publicUrl)
|
|
43
|
+
throw new Error("Non-loopback gateway requires GATEWAY_PUBLIC_URL and authentication configuration");
|
|
44
|
+
if (Boolean(options.auditWebhookUrl) !== Boolean(options.auditWebhookToken))
|
|
45
|
+
throw new Error("AUDIT_WEBHOOK_URL and AUDIT_WEBHOOK_TOKEN must be set together");
|
|
46
|
+
if (options.auditWebhookUrl && (new URL(options.auditWebhookUrl).protocol !== "https:" || options.auditWebhookToken.length < 32))
|
|
47
|
+
throw new Error("Durable audit webhook requires HTTPS and a token of at least 32 characters");
|
|
48
|
+
const store = new GatewayStore(options.statePath, options.bootstrapAdminEmails);
|
|
49
|
+
const requiredAuditSink = options.auditWebhookUrl ? async (entry) => {
|
|
50
|
+
const response = await fetch(options.auditWebhookUrl, {
|
|
51
|
+
method: "POST", headers: { Authorization: `Bearer ${options.auditWebhookToken}`, "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify(entry), signal: AbortSignal.timeout(10_000), redirect: "error",
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok)
|
|
55
|
+
throw new Error(`Audit webhook returned HTTP ${response.status}`);
|
|
56
|
+
await response.body?.cancel();
|
|
57
|
+
} : undefined;
|
|
58
|
+
const isAllowed = (email) => Boolean(email && store.snapshot().users.some((user) => user.email === email));
|
|
59
|
+
const runtime = new GatewayRuntime(store, Boolean(options.publicUrl), requiredAuditSink, accessMode ? isAllowed : undefined);
|
|
60
|
+
const auth = new GatewayAuth(options.publicUrl, options.entraTenant, options.authMode === "cloudflare" ? options.cloudflareAccessAud : options.entraAudience, options.entraScope, options.authMode, options.cloudflareTeamDomain);
|
|
61
|
+
const authenticate = async (req, res) => {
|
|
62
|
+
const identity = await auth.authorize(req, res);
|
|
63
|
+
if (!identity)
|
|
64
|
+
return false;
|
|
65
|
+
if (!accessMode)
|
|
66
|
+
return { identity, role: undefined };
|
|
67
|
+
const user = store.snapshot().users.find((item) => item.email === identity.email);
|
|
68
|
+
if (!user) {
|
|
69
|
+
json(res, 403, { error: "User is not allowed by the gateway" });
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return { identity, role: user.role };
|
|
73
|
+
};
|
|
74
|
+
const server = createServer(async (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
77
|
+
const requestHost = new URL(`http://${req.headers.host ?? "invalid"}`).hostname;
|
|
78
|
+
const publicHost = options.publicUrl ? new URL(options.publicUrl).hostname : undefined;
|
|
79
|
+
if (requestHost !== publicHost && !["127.0.0.1", "localhost", "[::1]"].includes(requestHost)) {
|
|
80
|
+
json(res, 421, { error: "invalid host" });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const pathname = url.pathname;
|
|
84
|
+
if (pathname === "/healthz") {
|
|
85
|
+
json(res, 200, { status: "ok" });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (pathname === "/__internal/state" && options.internalToken && authorizedAdmin(req, options.internalToken)) {
|
|
89
|
+
if (req.method === "GET") {
|
|
90
|
+
json(res, 200, store.snapshot());
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (req.method === "PUT") {
|
|
94
|
+
const state = store.replace(await body(req));
|
|
95
|
+
await runtime.sync();
|
|
96
|
+
json(res, 200, state);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (pathname === "/.well-known/oauth-protected-resource" || pathname === "/.well-known/oauth-protected-resource/mcp") {
|
|
101
|
+
if (!options.publicUrl || options.authMode === "cloudflare") {
|
|
102
|
+
json(res, 404, { error: "OAuth metadata is served by Cloudflare Access or not configured" });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
json(res, 200, await auth.protectedResource());
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (pathname === "/mcp") {
|
|
109
|
+
const session = await authenticate(req, res);
|
|
110
|
+
if (!session)
|
|
111
|
+
return;
|
|
112
|
+
if (options.publicUrl && activeScope(store.snapshot())?.profile === "isolated") {
|
|
113
|
+
json(res, 403, { error: "Isolated scopes cannot use the public MCP endpoint" });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (req.method !== "POST") {
|
|
117
|
+
res.writeHead(405, { Allow: "POST" });
|
|
118
|
+
res.end();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (Number(req.headers["content-length"] ?? 0) > MAX_BODY) {
|
|
122
|
+
json(res, 413, { error: "Request exceeds 1 MiB" });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const parsed = await body(req);
|
|
126
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
127
|
+
const mcp = runtime.createServer(session.identity.actor, session.identity.email);
|
|
128
|
+
try {
|
|
129
|
+
await mcp.connect(transport);
|
|
130
|
+
await transport.handleRequest(req, res, parsed);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await mcp.close();
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (pathname === "/admin" && req.method === "GET") {
|
|
138
|
+
if (accessMode) {
|
|
139
|
+
const session = await authenticate(req, res);
|
|
140
|
+
if (!session)
|
|
141
|
+
return;
|
|
142
|
+
if (session.role !== "admin") {
|
|
143
|
+
json(res, 403, { error: "Administrator access required" });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Content-Security-Policy": "default-src 'none'; script-src 'self'; style-src 'unsafe-inline'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'", "X-Content-Type-Options": "nosniff", "Referrer-Policy": "no-referrer" });
|
|
148
|
+
res.end(html);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (pathname === "/admin.js" && req.method === "GET") {
|
|
152
|
+
if (accessMode) {
|
|
153
|
+
const session = await authenticate(req, res);
|
|
154
|
+
if (!session)
|
|
155
|
+
return;
|
|
156
|
+
if (session.role !== "admin") {
|
|
157
|
+
json(res, 403, { error: "Administrator access required" });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
res.writeHead(200, { "Content-Type": "text/javascript; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
162
|
+
res.end(js);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (!pathname.startsWith("/api/")) {
|
|
166
|
+
json(res, 404, { error: "not found" });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const origin = req.headers.origin;
|
|
170
|
+
if (origin && origin !== `http://${req.headers.host}` && origin !== options.publicUrl) {
|
|
171
|
+
json(res, 403, { error: "invalid origin" });
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (accessMode) {
|
|
175
|
+
const session = await authenticate(req, res);
|
|
176
|
+
if (!session)
|
|
177
|
+
return;
|
|
178
|
+
if (session.role !== "admin") {
|
|
179
|
+
json(res, 403, { error: "Administrator access required" });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (pathname === "/api/session" && req.method === "GET") {
|
|
183
|
+
json(res, 200, { mode: "access", email: session.identity.email, bootstrapAdminEmails: [...store.bootstrapAdminEmails] });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (pathname === "/api/session" && req.method === "GET") {
|
|
188
|
+
json(res, 200, { mode: "token", bootstrapAdminEmails: [] });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (!accessMode && !authorizedAdmin(req, options.adminToken)) {
|
|
192
|
+
json(res, 401, { error: "admin token required" });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (pathname === "/api/state" && req.method === "GET") {
|
|
196
|
+
json(res, 200, store.snapshot());
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (accessMode && pathname === "/api/users" && req.method === "POST") {
|
|
200
|
+
const user = parseUser(await body(req));
|
|
201
|
+
store.update((draft) => { draft.users.push(user); });
|
|
202
|
+
json(res, 201, user);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const userMatch = /^\/api\/users\/([0-9a-f-]{36})$/.exec(pathname);
|
|
206
|
+
if (accessMode && userMatch && req.method === "PATCH") {
|
|
207
|
+
const input = z.object({ role: z.enum(["admin", "tester"]) }).parse(await body(req));
|
|
208
|
+
const updated = store.update((draft) => {
|
|
209
|
+
const user = draft.users.find((item) => item.id === userMatch[1]);
|
|
210
|
+
if (!user)
|
|
211
|
+
throw new Error("User not found");
|
|
212
|
+
if (store.bootstrapAdminEmails.has(user.email))
|
|
213
|
+
throw new Error("Bootstrap administrator cannot be changed");
|
|
214
|
+
user.role = input.role;
|
|
215
|
+
});
|
|
216
|
+
json(res, 200, updated);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (accessMode && userMatch && req.method === "DELETE") {
|
|
220
|
+
const updated = store.update((draft) => {
|
|
221
|
+
const user = draft.users.find((item) => item.id === userMatch[1]);
|
|
222
|
+
if (!user)
|
|
223
|
+
throw new Error("User not found");
|
|
224
|
+
if (store.bootstrapAdminEmails.has(user.email))
|
|
225
|
+
throw new Error("Bootstrap administrator cannot be removed");
|
|
226
|
+
draft.users = draft.users.filter((item) => item.id !== user.id);
|
|
227
|
+
});
|
|
228
|
+
json(res, 200, updated);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (pathname === "/api/upstreams" && req.method === "POST") {
|
|
232
|
+
const upstream = parseUpstream(await body(req));
|
|
233
|
+
if (options.publicUrl && upstream.transport === "stdio") {
|
|
234
|
+
json(res, 400, { error: "Hosted gateway cannot spawn local processes" });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
store.update((draft) => { draft.upstreams.push(upstream); });
|
|
238
|
+
await runtime.sync();
|
|
239
|
+
json(res, 201, upstream);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const upstreamMatch = /^\/api\/upstreams\/([0-9a-f-]{36})$/.exec(pathname);
|
|
243
|
+
if (upstreamMatch && req.method === "PATCH") {
|
|
244
|
+
const input = z.object({ enabled: z.boolean() }).parse(await body(req));
|
|
245
|
+
const updated = store.update((draft) => {
|
|
246
|
+
const item = draft.upstreams.find((value) => value.id === upstreamMatch[1]);
|
|
247
|
+
if (!item)
|
|
248
|
+
throw new Error("Upstream not found");
|
|
249
|
+
item.enabled = input.enabled;
|
|
250
|
+
});
|
|
251
|
+
await runtime.sync();
|
|
252
|
+
json(res, 200, updated);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (upstreamMatch && req.method === "DELETE") {
|
|
256
|
+
const updated = store.update((draft) => { draft.upstreams = draft.upstreams.filter((value) => value.id !== upstreamMatch[1]); });
|
|
257
|
+
await runtime.sync();
|
|
258
|
+
json(res, 200, updated);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (pathname === "/api/scopes" && req.method === "POST") {
|
|
262
|
+
const scope = parseScope(await body(req));
|
|
263
|
+
store.update((draft) => { draft.scopes.push(scope); });
|
|
264
|
+
json(res, 201, scope);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const scopeMatch = /^\/api\/scopes\/([0-9a-f-]{36})$/.exec(pathname);
|
|
268
|
+
if (scopeMatch && req.method === "PUT") {
|
|
269
|
+
const scope = parseScope(await body(req), scopeMatch[1]);
|
|
270
|
+
const updated = store.update((draft) => {
|
|
271
|
+
if (draft.activeScopeId === scope.id)
|
|
272
|
+
throw new Error("Active scope cannot be edited; save a new draft");
|
|
273
|
+
const index = draft.scopes.findIndex((item) => item.id === scope.id);
|
|
274
|
+
if (index < 0)
|
|
275
|
+
throw new Error("Scope not found");
|
|
276
|
+
draft.scopes[index] = scope;
|
|
277
|
+
});
|
|
278
|
+
json(res, 200, updated);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (scopeMatch && req.method === "DELETE") {
|
|
282
|
+
const updated = store.update((draft) => {
|
|
283
|
+
if (draft.activeScopeId === scopeMatch[1])
|
|
284
|
+
throw new Error("Active scope cannot be deleted");
|
|
285
|
+
draft.scopes = draft.scopes.filter((item) => item.id !== scopeMatch[1]);
|
|
286
|
+
});
|
|
287
|
+
json(res, 200, updated);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const activateMatch = /^\/api\/scopes\/([0-9a-f-]{36})\/activate$/.exec(pathname);
|
|
291
|
+
if (activateMatch && req.method === "POST") {
|
|
292
|
+
const updated = store.update((draft) => {
|
|
293
|
+
const scope = draft.scopes.find((item) => item.id === activateMatch[1]);
|
|
294
|
+
if (!scope)
|
|
295
|
+
throw new Error("Scope not found");
|
|
296
|
+
if (Date.now() < Date.parse(scope.manifest.engagement_window.start) || Date.now() > Date.parse(scope.manifest.engagement_window.end))
|
|
297
|
+
throw new Error("Scope window is not active");
|
|
298
|
+
draft.activeScopeId = scope.id;
|
|
299
|
+
});
|
|
300
|
+
await runtime.sync();
|
|
301
|
+
json(res, 200, updated);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
json(res, 404, { error: "not found" });
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
const message = error instanceof Error ? error.message : "Request failed";
|
|
308
|
+
json(res, error instanceof z.ZodError || error instanceof SyntaxError ? 400 : 409, { error: message });
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
return { server, store, runtime, listen: () => new Promise((resolve, reject) => {
|
|
312
|
+
server.once("error", reject);
|
|
313
|
+
server.listen(options.port, options.host, () => { server.off("error", reject); resolve(); });
|
|
314
|
+
}), close: async () => { await new Promise((resolve) => server.close(() => resolve())); await runtime.close(); } };
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/gateway/server.ts"],"names":[],"mappings":"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"}
|