@integsec/agentic-pentest-proxy 0.3.6 → 0.3.8
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/cloudflare/wrangler.jsonc +1 -1
- package/dist/bin/integsec-agentic-pentest-bridge.js +2 -2
- package/dist/bin/integsec-agentic-pentest-gateway.js +107 -49
- package/dist/bin/integsec-agentic-pentest-gateway.js.map +1 -1
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.d.ts +3 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.d.ts.map +1 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.js +39 -0
- package/dist/bin/integsec-agentic-pentest-scanners-mcp.js.map +1 -0
- package/dist/src/config.js +1 -1
- package/dist/src/gateway/admin.html +1 -1
- package/dist/src/gateway/admin.js +13 -4
- package/dist/src/gateway/docs-home.html +1 -1
- package/dist/src/gateway/local-control.d.ts +27 -0
- package/dist/src/gateway/local-control.d.ts.map +1 -0
- package/dist/src/gateway/local-control.js +176 -0
- package/dist/src/gateway/local-control.js.map +1 -0
- package/dist/src/gateway/runtime.d.ts.map +1 -1
- package/dist/src/gateway/runtime.js +17 -4
- package/dist/src/gateway/runtime.js.map +1 -1
- package/dist/src/gateway/server.d.ts +2 -0
- package/dist/src/gateway/server.d.ts.map +1 -1
- package/dist/src/gateway/server.js +22 -1
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/state.d.ts +1 -0
- package/dist/src/gateway/state.d.ts.map +1 -1
- package/dist/src/gateway/state.js +9 -0
- package/dist/src/gateway/state.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/manifest-schema.d.ts +1 -0
- package/dist/src/manifest-schema.d.ts.map +1 -1
- package/dist/src/manifest-schema.js +1 -0
- package/dist/src/manifest-schema.js.map +1 -1
- package/dist/src/proxy.d.ts +2 -1
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +4 -3
- package/dist/src/proxy.js.map +1 -1
- package/dist/src/scanners/runner.d.ts +4 -0
- package/dist/src/scanners/runner.d.ts.map +1 -0
- package/dist/src/scanners/runner.js +95 -0
- package/dist/src/scanners/runner.js.map +1 -0
- package/dist/src/technique-checker.d.ts.map +1 -1
- package/dist/src/technique-checker.js +6 -1
- package/dist/src/technique-checker.js.map +1 -1
- package/dist/src/tui/DecisionRow.d.ts.map +1 -1
- package/dist/src/tui/DecisionRow.js +2 -1
- package/dist/src/tui/DecisionRow.js.map +1 -1
- package/dist/src/tui/destination.d.ts +4 -0
- package/dist/src/tui/destination.d.ts.map +1 -0
- package/dist/src/tui/destination.js +43 -0
- package/dist/src/tui/destination.js.map +1 -0
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/validator.d.ts +5 -2
- package/dist/src/validator.d.ts.map +1 -1
- package/dist/src/validator.js +209 -5
- package/dist/src/validator.js.map +1 -1
- package/docs/managed-gateway.md +11 -3
- package/docs/security.md +6 -0
- package/docs/tui.md +3 -1
- package/package.json +6 -3
- package/scripts/postinstall.cjs +4 -2
|
@@ -12,9 +12,9 @@ const url = new URL(process.argv[2] ?? process.env.GATEWAY_URL ?? "http://127.0.
|
|
|
12
12
|
if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || url.protocol !== "http:") {
|
|
13
13
|
throw new Error("The local bridge only accepts a loopback HTTP gateway URL");
|
|
14
14
|
}
|
|
15
|
-
const upstream = new Client({ name: "agentic-pentest-bridge", version: "0.3.
|
|
15
|
+
const upstream = new Client({ name: "agentic-pentest-bridge", version: "0.3.8" });
|
|
16
16
|
await upstream.connect(new StreamableHTTPClientTransport(url));
|
|
17
|
-
const bridge = new Server({ name: "agentic-pentest-local", version: "0.3.
|
|
17
|
+
const bridge = new Server({ name: "agentic-pentest-local", version: "0.3.8" }, { capabilities: { tools: {} } });
|
|
18
18
|
bridge.setRequestHandler(ListToolsRequestSchema, async (request) => upstream.listTools(request.params));
|
|
19
19
|
bridge.setRequestHandler(CallToolRequestSchema, async (request) => upstream.callTool(request.params));
|
|
20
20
|
await bridge.connect(new StdioServerTransport());
|
|
@@ -2,15 +2,25 @@
|
|
|
2
2
|
import "dotenv/config";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { createGateway } from "../src/gateway/server.js";
|
|
6
7
|
import { defaultGatewayPaths, migrateLegacyGatewayState } from "../src/gateway/local-paths.js";
|
|
7
|
-
|
|
8
|
+
import { localGatewayStatus, removeLocalControlFile, startLocalGateway, stopLocalGateway } from "../src/gateway/local-control.js";
|
|
9
|
+
const command = process.argv[2] ?? "run";
|
|
10
|
+
if (["--help", "-h", "help"].includes(command)) {
|
|
8
11
|
console.log(`IntegSec MCP Gateway
|
|
9
12
|
|
|
10
13
|
Manage MCP upstreams and engagement scopes in a browser, then connect an MCP client at /mcp.
|
|
11
14
|
|
|
12
|
-
Local
|
|
13
|
-
|
|
15
|
+
Local commands:
|
|
16
|
+
integsec-gateway start Start in the background and print the admin token
|
|
17
|
+
integsec-gateway status Check whether the background gateway is running
|
|
18
|
+
integsec-gateway stop Stop the background gateway
|
|
19
|
+
integsec-gateway run Run in this terminal (Ctrl+C to stop)
|
|
20
|
+
|
|
21
|
+
The longer integsec-agentic-pentest-gateway command works the same way.
|
|
22
|
+
Running without a command still uses foreground mode for compatibility.
|
|
23
|
+
Set GATEWAY_ADMIN_TOKEN (at least 32 characters) for a stable token.
|
|
14
24
|
Optional: GATEWAY_HOST (127.0.0.1), GATEWAY_PORT (9091), GATEWAY_STATE_PATH.
|
|
15
25
|
State and audit logs default to your operating system's per-user data directory.
|
|
16
26
|
Hosted with Cloudflare Access: GATEWAY_PUBLIC_URL, GATEWAY_AUTH_MODE=cloudflare,
|
|
@@ -18,62 +28,110 @@ Hosted with Cloudflare Access: GATEWAY_PUBLIC_URL, GATEWAY_AUTH_MODE=cloudflare,
|
|
|
18
28
|
GATEWAY_BOOTSTRAP_ADMIN_EMAILS (comma-separated).
|
|
19
29
|
Hosted with direct Entra: GATEWAY_PUBLIC_URL, ENTRA_TENANT, ENTRA_AUDIENCE, ENTRA_SCOPE.
|
|
20
30
|
|
|
21
|
-
Run: integsec-agentic-pentest-gateway
|
|
22
31
|
Open: http://127.0.0.1:9091/admin
|
|
23
32
|
`);
|
|
24
|
-
process.exit(0);
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
else if (command === "start") {
|
|
35
|
+
try {
|
|
36
|
+
const result = await startLocalGateway(fileURLToPath(import.meta.url));
|
|
37
|
+
console.log(result.alreadyRunning ? `Gateway already running. Open http://127.0.0.1:${result.port}/admin` : `Gateway started in the background. Open http://127.0.0.1:${result.port}/admin`);
|
|
38
|
+
if (result.adminToken)
|
|
39
|
+
console.log(`Admin token (paste into the page): ${result.adminToken}`);
|
|
40
|
+
else if (!result.alreadyRunning)
|
|
41
|
+
console.log("Use the GATEWAY_ADMIN_TOKEN you configured to sign in.");
|
|
42
|
+
console.log("Check: integsec-gateway status Stop: integsec-gateway stop");
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`[gateway] ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
process.exitCode = 1;
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
else if (command === "status") {
|
|
50
|
+
try {
|
|
51
|
+
const result = await localGatewayStatus();
|
|
52
|
+
console.log(result.running ? `Gateway running. Open http://127.0.0.1:${result.record.port}/admin` : result.starting ? "Gateway is starting. Try status again shortly." : result.foreground ? `A gateway is running in another terminal on port ${result.port}. Stop it there with Ctrl+C.` : "No local gateway is running.");
|
|
53
|
+
process.exitCode = result.running || result.foreground ? 0 : 1;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`[gateway] ${error instanceof Error ? error.message : String(error)}`);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
}
|
|
41
59
|
}
|
|
42
|
-
|
|
43
|
-
host,
|
|
44
|
-
port: Number(process.env.GATEWAY_PORT ?? "9091"),
|
|
45
|
-
statePath,
|
|
46
|
-
adminToken: generatedToken ?? process.env.GATEWAY_ADMIN_TOKEN ?? "",
|
|
47
|
-
bootstrapAdminEmails: (process.env.GATEWAY_BOOTSTRAP_ADMIN_EMAILS ?? "").split(",").map((value) => value.trim()).filter(Boolean),
|
|
48
|
-
publicUrl: process.env.GATEWAY_PUBLIC_URL,
|
|
49
|
-
entraTenant: process.env.ENTRA_TENANT,
|
|
50
|
-
entraAudience: process.env.ENTRA_AUDIENCE,
|
|
51
|
-
entraScope: process.env.ENTRA_SCOPE,
|
|
52
|
-
authMode: process.env.GATEWAY_AUTH_MODE === "cloudflare" ? "cloudflare" : "entra",
|
|
53
|
-
cloudflareTeamDomain: process.env.CLOUDFLARE_TEAM_DOMAIN,
|
|
54
|
-
cloudflareAccessAud: process.env.CLOUDFLARE_ACCESS_AUD,
|
|
55
|
-
internalToken: process.env.GATEWAY_INTERNAL_TOKEN,
|
|
56
|
-
auditWebhookUrl: process.env.AUDIT_WEBHOOK_URL,
|
|
57
|
-
auditWebhookToken: process.env.AUDIT_WEBHOOK_TOKEN,
|
|
58
|
-
});
|
|
59
|
-
if (local) {
|
|
60
|
+
else if (command === "stop") {
|
|
60
61
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (await stopLocalGateway())
|
|
63
|
+
console.log("Gateway stopped.");
|
|
64
|
+
else {
|
|
65
|
+
const result = await localGatewayStatus();
|
|
66
|
+
console.log(result.foreground ? `This gateway is running in another terminal on port ${result.port}. Stop it there with Ctrl+C.` : "No managed background gateway is running.");
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
catch (error) {
|
|
65
|
-
console.error(`[gateway]
|
|
70
|
+
console.error(`[gateway] ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (command !== "run") {
|
|
75
|
+
console.error(`Unknown command: ${command}. Run integsec-gateway --help.`);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (process.env.GATEWAY_CONTROL_TOKEN && process.env.GATEWAY_CONTROL_FILE)
|
|
80
|
+
process.on("exit", removeLocalControlFile);
|
|
81
|
+
const host = process.env.GATEWAY_HOST ?? "127.0.0.1";
|
|
82
|
+
const local = !process.env.GATEWAY_PUBLIC_URL && (host === "127.0.0.1" || host === "::1");
|
|
83
|
+
const generatedToken = local && !process.env.GATEWAY_ADMIN_TOKEN ? randomBytes(32).toString("hex") : undefined;
|
|
84
|
+
const defaults = defaultGatewayPaths();
|
|
85
|
+
const statePath = process.env.GATEWAY_STATE_PATH || defaults.statePath;
|
|
86
|
+
const auditDir = process.env.AUDIT_LOG_PATH || defaults.auditDir;
|
|
87
|
+
process.env.AUDIT_LOG_PATH = auditDir;
|
|
88
|
+
try {
|
|
89
|
+
if (!process.env.GATEWAY_STATE_PATH && migrateLegacyGatewayState(statePath)) {
|
|
90
|
+
console.error("[gateway] Copied existing local configuration into the per-user data directory.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error(`[gateway] Cannot move existing gateway data into your per-user directory: ${error instanceof Error ? error.message : String(error)}. Set GATEWAY_STATE_PATH to a writable location, then restart.`);
|
|
66
95
|
process.exit(1);
|
|
67
96
|
}
|
|
97
|
+
const gateway = createGateway({
|
|
98
|
+
host,
|
|
99
|
+
port: Number(process.env.GATEWAY_PORT ?? "9091"),
|
|
100
|
+
statePath,
|
|
101
|
+
adminToken: generatedToken ?? process.env.GATEWAY_ADMIN_TOKEN ?? "",
|
|
102
|
+
bootstrapAdminEmails: (process.env.GATEWAY_BOOTSTRAP_ADMIN_EMAILS ?? "").split(",").map((value) => value.trim()).filter(Boolean),
|
|
103
|
+
publicUrl: process.env.GATEWAY_PUBLIC_URL,
|
|
104
|
+
entraTenant: process.env.ENTRA_TENANT,
|
|
105
|
+
entraAudience: process.env.ENTRA_AUDIENCE,
|
|
106
|
+
entraScope: process.env.ENTRA_SCOPE,
|
|
107
|
+
authMode: process.env.GATEWAY_AUTH_MODE === "cloudflare" ? "cloudflare" : "entra",
|
|
108
|
+
cloudflareTeamDomain: process.env.CLOUDFLARE_TEAM_DOMAIN,
|
|
109
|
+
cloudflareAccessAud: process.env.CLOUDFLARE_ACCESS_AUD,
|
|
110
|
+
internalToken: process.env.GATEWAY_INTERNAL_TOKEN,
|
|
111
|
+
localControlToken: process.env.GATEWAY_CONTROL_TOKEN,
|
|
112
|
+
onLocalControlStop: () => { process.exitCode = 0; },
|
|
113
|
+
auditWebhookUrl: process.env.AUDIT_WEBHOOK_URL,
|
|
114
|
+
auditWebhookToken: process.env.AUDIT_WEBHOOK_TOKEN,
|
|
115
|
+
});
|
|
116
|
+
if (local) {
|
|
117
|
+
try {
|
|
118
|
+
gateway.store.replace(gateway.store.snapshot());
|
|
119
|
+
mkdirSync(auditDir, { recursive: true, mode: 0o700 });
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error(`[gateway] Cannot save local gateway data: ${error instanceof Error ? error.message : String(error)}. Set GATEWAY_STATE_PATH and AUDIT_LOG_PATH to writable locations, then restart.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
await gateway.listen();
|
|
127
|
+
console.error(`[gateway] Listening on ${process.env.GATEWAY_HOST ?? "127.0.0.1"}:${process.env.GATEWAY_PORT ?? "9091"}`);
|
|
128
|
+
console.error("[gateway] Admin UI: /admin; MCP: /mcp");
|
|
129
|
+
if (local)
|
|
130
|
+
console.error("[gateway] Audit monitor: integsec-agentic-pentest-tui --gateway");
|
|
131
|
+
if (generatedToken)
|
|
132
|
+
console.error(`[gateway] Local admin token (paste into /admin): ${generatedToken}`);
|
|
133
|
+
const stop = async () => { await gateway.close(); process.exitCode = 0; };
|
|
134
|
+
process.on("SIGINT", () => void stop());
|
|
135
|
+
process.on("SIGTERM", () => void stop());
|
|
68
136
|
}
|
|
69
|
-
await gateway.listen();
|
|
70
|
-
console.error(`[gateway] Listening on ${process.env.GATEWAY_HOST ?? "127.0.0.1"}:${process.env.GATEWAY_PORT ?? "9091"}`);
|
|
71
|
-
console.error("[gateway] Admin UI: /admin; MCP: /mcp");
|
|
72
|
-
if (local)
|
|
73
|
-
console.error("[gateway] Audit monitor: integsec-agentic-pentest-tui --gateway");
|
|
74
|
-
if (generatedToken)
|
|
75
|
-
console.error(`[gateway] Local admin token (paste into /admin): ${generatedToken}`);
|
|
76
|
-
const stop = async () => { await gateway.close(); process.exit(0); };
|
|
77
|
-
process.on("SIGINT", () => void stop());
|
|
78
|
-
process.on("SIGTERM", () => void stop());
|
|
79
137
|
//# sourceMappingURL=integsec-agentic-pentest-gateway.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integsec-agentic-pentest-gateway.js","sourceRoot":"","sources":["../../bin/integsec-agentic-pentest-gateway.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"integsec-agentic-pentest-gateway.js","sourceRoot":"","sources":["../../bin/integsec-agentic-pentest-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integsec-agentic-pentest-scanners-mcp.d.ts","sourceRoot":"","sources":["../../bin/integsec-agentic-pentest-scanners-mcp.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { nmapArgs, nucleiArgs, runScanner } from "../src/scanners/runner.js";
|
|
6
|
+
const mode = process.argv[2];
|
|
7
|
+
if (mode !== "nmap" && mode !== "nuclei") {
|
|
8
|
+
console.error("Usage: integsec-agentic-pentest-scanners-mcp <nmap|nuclei>");
|
|
9
|
+
process.exit(2);
|
|
10
|
+
}
|
|
11
|
+
const tool = mode === "nmap" ? {
|
|
12
|
+
name: "nmap_scan", description: "Scan one approved host with Nmap TCP connect scanning. Limited to 100 ports and two minutes.",
|
|
13
|
+
inputSchema: { type: "object", properties: {
|
|
14
|
+
target: { type: "string", description: "One authorized IP address or hostname." },
|
|
15
|
+
ports: { type: "string", description: "Up to 100 TCP ports, e.g. 80,443,8000-8010. Defaults to 80,443." },
|
|
16
|
+
serviceDetection: { type: "boolean", description: "Probe open ports for service versions." },
|
|
17
|
+
}, required: ["target"], additionalProperties: false },
|
|
18
|
+
} : {
|
|
19
|
+
name: "nuclei_scan", description: "Run a bounded HTTP Nuclei template scan against one approved URL. Disables OAST and automatic updates.",
|
|
20
|
+
inputSchema: { type: "object", properties: {
|
|
21
|
+
url: { type: "string", description: "One authorized HTTP or HTTPS URL." },
|
|
22
|
+
}, required: ["url"], additionalProperties: false },
|
|
23
|
+
};
|
|
24
|
+
const server = new Server({ name: `integsec-${mode}-mcp`, version: "0.3.8" }, { capabilities: { tools: {} } });
|
|
25
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [tool] }));
|
|
26
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
27
|
+
if (request.params.name !== tool.name)
|
|
28
|
+
return { content: [{ type: "text", text: "Unknown scanner tool." }], isError: true };
|
|
29
|
+
try {
|
|
30
|
+
const input = request.params.arguments ?? {};
|
|
31
|
+
const args = mode === "nmap" ? nmapArgs(input) : nucleiArgs(input);
|
|
32
|
+
return { content: [{ type: "text", text: await runScanner(mode, args) }] };
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
await server.connect(new StdioServerTransport());
|
|
39
|
+
//# sourceMappingURL=integsec-agentic-pentest-scanners-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integsec-agentic-pentest-scanners-mcp.js","sourceRoot":"","sources":["../../bin/integsec-agentic-pentest-scanners-mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/src/config.js
CHANGED
|
@@ -50,7 +50,7 @@ export function loadConfig() {
|
|
|
50
50
|
techniqueMapPath: process.env.TECHNIQUE_MAP_PATH,
|
|
51
51
|
dnsCacheTtl: parseInt(process.env.DNS_CACHE_TTL || "60", 10),
|
|
52
52
|
logLevel: process.env.LOG_LEVEL || "INFO",
|
|
53
|
-
proxyVersion: process.env.PROXY_VERSION || "0.3.
|
|
53
|
+
proxyVersion: process.env.PROXY_VERSION || "0.3.8",
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
//# sourceMappingURL=config.js.map
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<h3>Use Burp or another tool on a tester’s computer</h3>
|
|
26
26
|
<p>The hosted gateway cannot reach software on a laptop. Run a separate local gateway on that computer:</p>
|
|
27
27
|
<ol><li>On the same computer as Burp, enable Burp’s MCP extension. Install Node.js 22 or newer.</li><li>Copy these commands into PowerShell:<pre><code id="local-command">npm install -g @integsec/agentic-pentest-proxy
|
|
28
|
-
integsec-
|
|
28
|
+
integsec-gateway start</code></pre><button id="copy-local-command" class="secondary">Copy commands</button><p>The gateway starts in the background and prints an admin token. Keep it private. Use <code>integsec-gateway status</code> and <code>integsec-gateway stop</code> to check or stop it.</p></li><li>Open <code>http://127.0.0.1:9091/admin</code> on that computer and paste the printed token. Click <strong>Add server (off)</strong> beside Burp, then <strong>Turn on</strong>.</li><li>Download your approved engagement policy JSON from this team dashboard. On the laptop, import it, review and approve it, then select it under My engagement. Cloud changes do not automatically update the laptop.</li><li>Connect the MCP client to the local gateway. Refresh its tools after changes. See the <a href="/docs/managed-gateway#connect-mcp-clients">Claude, ChatGPT, and local model instructions</a>.</li></ol>
|
|
29
29
|
</div>
|
|
30
30
|
<div id="local-presets" hidden><h3>Local presets</h3><p>Adding a preset creates an entry in this local gateway. Install its software separately; new entries start off.</p><div id="presets"></div></div>
|
|
31
31
|
<div id="add-server-panel" hidden>
|
|
@@ -19,6 +19,8 @@ const presets = [
|
|
|
19
19
|
{ name: 'burp', label: 'Burp Suite', transport: 'sse', url: 'http://127.0.0.1:9876', local: true, source: 'https://github.com/PortSwigger/mcp-server', note: 'Enable the official MCP extension inside the tester’s local Burp.' },
|
|
20
20
|
{ name: 'semgrep', label: 'Semgrep', transport: 'stdio', command: 'semgrep', args: ['mcp'], local: true, source: 'https://github.com/semgrep/semgrep', note: 'Install and configure the official Semgrep CLI first.' },
|
|
21
21
|
{ name: 'trivy', label: 'Trivy', transport: 'stdio', command: 'trivy', args: ['mcp'], local: true, source: 'https://github.com/aquasecurity/trivy-mcp', note: 'Install Trivy and its official MCP plugin first; pin and review your version.' },
|
|
22
|
+
{ name: 'nmap', label: 'Nmap', transport: 'stdio', command: 'integsec-agentic-pentest-scanners-mcp', args: ['nmap'], local: true, source: 'https://nmap.org/book/man.html', note: 'Install Nmap locally first. The bundled MCP adapter offers one-host, rate-limited TCP scans.' },
|
|
23
|
+
{ name: 'nuclei', label: 'Nuclei', transport: 'stdio', command: 'integsec-agentic-pentest-scanners-mcp', args: ['nuclei'], local: true, source: 'https://github.com/projectdiscovery/nuclei', note: 'Install ProjectDiscovery Nuclei locally first. The bundled MCP adapter limits HTTP scans and disables OAST.' },
|
|
22
24
|
{ name: 'snyk', label: 'Snyk', transport: 'stdio', command: 'snyk', args: ['mcp', '-t', 'stdio'], local: true, source: 'https://github.com/snyk/studio-mcp', note: 'Install the Snyk CLI and authenticate locally first.' },
|
|
23
25
|
{ label: 'OWASP ZAP', source: 'https://www.zaproxy.org/docs/desktop/addons/mcp-integration/mcp-options/', note: 'Reference only: its MCP security key needs authenticated upstream support.' },
|
|
24
26
|
{ label: 'PortSwigger DAST', source: 'https://portswigger.net/burp/documentation/dast/user-guide/using-ai/mcp-server', note: 'Reference only: its hosted MCP endpoint requires an API key.' },
|
|
@@ -75,6 +77,13 @@ async function api(path, method = 'GET', data) {
|
|
|
75
77
|
}
|
|
76
78
|
const lines = (id) => $(id).value.split(/\r?\n/).map((value) => value.trim()).filter(Boolean);
|
|
77
79
|
const setLines = (id, values) => { $(id).value = values.join('\n'); };
|
|
80
|
+
const localPathField = document.createElement('label');
|
|
81
|
+
localPathField.textContent = 'Approved local code folders (Semgrep and Trivy; one absolute path per line)';
|
|
82
|
+
const localPathInput = document.createElement('textarea');
|
|
83
|
+
localPathInput.id = 'local-paths';
|
|
84
|
+
localPathInput.placeholder = 'Absolute folder path containing authorized source code';
|
|
85
|
+
localPathField.append(localPathInput);
|
|
86
|
+
$('hosts').closest('label').after(localPathField);
|
|
78
87
|
const make = (tag, value) => { const node = document.createElement(tag); node.textContent = value; return node; };
|
|
79
88
|
function button(label, action, style = 'secondary') {
|
|
80
89
|
const node = make('button', label); node.className = style;
|
|
@@ -102,13 +111,13 @@ function draftFromForm() {
|
|
|
102
111
|
return { profile: $('profile').value, allowedHosts: lines('hosts'), manifest: {
|
|
103
112
|
engagement_id: $('engagement').value.trim(), client: $('client').value.trim(), operator: $('operator').value.trim(),
|
|
104
113
|
authorized_targets: { ip_ranges: lines('ips'), domains: lines('domains'), urls: lines('urls'), cloud_accounts: lines('cloud') },
|
|
105
|
-
excluded_targets: lines('excluded-targets'), authorized_techniques: checked(authorizedChoices),
|
|
114
|
+
excluded_targets: lines('excluded-targets'), authorized_local_paths: lines('local-paths'), authorized_techniques: checked(authorizedChoices),
|
|
106
115
|
excluded_techniques: checked(excludedChoices), engagement_window: { start: start.toISOString(), end: end.toISOString() },
|
|
107
116
|
} };
|
|
108
117
|
}
|
|
109
118
|
function summarize(scope) {
|
|
110
119
|
const m = scope.manifest;
|
|
111
|
-
return `Engagement: ${m.engagement_id || '(missing)'}\nClient: ${m.client || '(missing)'} · Operator: ${m.operator || '(missing)'}\nWindow: ${m.engagement_window.start} → ${m.engagement_window.end}\nProfile: ${scope.profile}\nTargets: ${Object.values(m.authorized_targets).flat().join(', ') || '(none)'}\nExcluded targets: ${m.excluded_targets.join(', ') || '(none)'}\nAuthorized techniques: ${m.authorized_techniques.join(', ') || '(none)'}\nExcluded techniques: ${m.excluded_techniques.join(', ') || '(none)'}\nAllowed MCP hosts: ${scope.allowedHosts.join(', ') || '(none)'}`;
|
|
120
|
+
return `Engagement: ${m.engagement_id || '(missing)'}\nClient: ${m.client || '(missing)'} · Operator: ${m.operator || '(missing)'}\nWindow: ${m.engagement_window.start} → ${m.engagement_window.end}\nProfile: ${scope.profile}\nTargets: ${Object.values(m.authorized_targets).flat().join(', ') || '(none)'}\nExcluded targets: ${m.excluded_targets.join(', ') || '(none)'}\nLocal code folders: ${(m.authorized_local_paths || []).join(', ') || '(none)'}\nAuthorized techniques: ${m.authorized_techniques.join(', ') || '(none)'}\nExcluded techniques: ${m.excluded_techniques.join(', ') || '(none)'}\nAllowed MCP hosts: ${scope.allowedHosts.join(', ') || '(none)'}`;
|
|
112
121
|
}
|
|
113
122
|
function downloadPolicy(scope) {
|
|
114
123
|
const policy = { ...scope.manifest, gateway: { profile: scope.profile, allowed_hosts: scope.allowedHosts || [] } };
|
|
@@ -249,7 +258,7 @@ function loadScope(scope) {
|
|
|
249
258
|
$('starts').value = localDate(m.engagement_window.start); $('ends').value = localDate(m.engagement_window.end);
|
|
250
259
|
$('profile').value = scope.profile; setLines('ips', m.authorized_targets.ip_ranges); setLines('domains', m.authorized_targets.domains);
|
|
251
260
|
setLines('urls', m.authorized_targets.urls); setLines('cloud', m.authorized_targets.cloud_accounts);
|
|
252
|
-
setLines('excluded-targets', m.excluded_targets); setLines('hosts', scope.allowedHosts);
|
|
261
|
+
setLines('excluded-targets', m.excluded_targets); setLines('hosts', scope.allowedHosts); setLines('local-paths', m.authorized_local_paths || []);
|
|
253
262
|
setChecked(authorizedChoices, m.authorized_techniques); setChecked(excludedChoices, m.excluded_techniques);
|
|
254
263
|
for (const field of $('editor-card').querySelectorAll('input,select,textarea')) field.disabled = scope.status !== 'draft';
|
|
255
264
|
$('save-scope').disabled = scope.status !== 'draft';
|
|
@@ -258,7 +267,7 @@ function loadScope(scope) {
|
|
|
258
267
|
}
|
|
259
268
|
function clearEditor() {
|
|
260
269
|
editing = null; viewingScopeId = null; dirty = false;
|
|
261
|
-
for (const id of ['engagement', 'client', 'operator', 'starts', 'ends', 'ips', 'domains', 'urls', 'cloud', 'excluded-targets', 'hosts']) $(id).value = '';
|
|
270
|
+
for (const id of ['engagement', 'client', 'operator', 'starts', 'ends', 'ips', 'domains', 'urls', 'cloud', 'excluded-targets', 'hosts', 'local-paths']) $(id).value = '';
|
|
262
271
|
$('profile').value = mode === 'access' ? 'connected' : 'isolated'; setChecked(authorizedChoices, []); setChecked(excludedChoices, []);
|
|
263
272
|
for (const field of $('editor-card').querySelectorAll('input,select,textarea')) field.disabled = false;
|
|
264
273
|
$('save-scope').disabled = false; $('editor-help').textContent = 'New scope. Enter the client, exact authorized targets, window and techniques, then Save.'; reviewForm();
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<figure class="flow hosted"><h3>Hosted path: remote HTTPS MCP tool</h3><ol><li>AI app<span>ChatGPT or Claude connects to the team URL</span></li><li>Cloudflare Access<span>Signs in and checks who may connect</span></li><li>Cloudflare gateway<span>Checks per-user engagement and records decisions</span></li><li>Remote MCP tool<span>A reachable HTTPS service</span></li><li>Authorized target<span>Traffic leaves from the remote tool’s host</span></li></ol><figcaption class="caption">The hosted gateway cannot start Burp or reach a listener on a tester’s laptop. It does not give the remote tool a Cloudflare source IP for its target traffic.</figcaption></figure>
|
|
12
12
|
<div class="note"><strong>The practical rule:</strong> Connect your AI app to the gateway beside the tool. The laptop gateway is for laptop tools. The hosted gateway is for remote HTTPS tools and shared team administration. If you need both kinds of tools, add both connections.</div>
|
|
13
13
|
<h2 id="local-start">Start with Burp on your laptop</h2>
|
|
14
|
-
<ol class="steps"><li>Enable Burp’s official MCP extension. Install Node.js 22 or newer.</li><li>Run <code>npm install -g @integsec/agentic-pentest-proxy</code>, then <code>integsec-
|
|
14
|
+
<ol class="steps"><li>Enable Burp’s official MCP extension. Install Node.js 22 or newer.</li><li>Run <code>npm install -g @integsec/agentic-pentest-proxy</code>, then <code>integsec-gateway start</code> in a terminal on that same laptop. It starts in the background and prints your admin token. Use <code>integsec-gateway status</code> to check it and <code>integsec-gateway stop</code> to stop it. Your configuration is saved in your per-user data folder.</li><li>Open <code>http://127.0.0.1:9091/admin</code>. Paste the admin token printed in the terminal. Add the Burp preset and turn it on.</li><li>Import the approved engagement JSON, review and approve it locally, and select it under <strong>My engagement</strong>. This step is manual for now.</li><li>Connect Claude Desktop through the local bridge, ChatGPT through a secure tunnel, or a local model chat UI directly to the local MCP endpoint. Refresh the client’s tool list after changing the scope or upgrading the gateway.</li></ol>
|
|
15
15
|
<a class="button-link" href="/docs/managed-gateway#connect-mcp-clients">Client connection steps</a><a class="button-link secondary" href="/docs/tui">Terminal dashboard guide</a>
|
|
16
16
|
<h2 id="hosted-start">Start with a team connection</h2>
|
|
17
17
|
<ol class="steps"><li>Sign in to the <a href="/admin">team dashboard</a> through Cloudflare Access.</li><li>An admin adds your exact email, approves an engagement, assigns it to you, and turns on a compatible remote HTTPS MCP server.</li><li>Choose that engagement under <strong>My engagement</strong>. Your choice applies to your own MCP calls.</li><li>Connect ChatGPT or Claude to <code>https://agentic-proxy.pentestprepper.com/mcp</code> through its supported team connector.</li></ol>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface ControlRecord {
|
|
2
|
+
host: "127.0.0.1" | "::1";
|
|
3
|
+
port: number;
|
|
4
|
+
token: string;
|
|
5
|
+
createdAt: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function controlFiles(): {
|
|
8
|
+
statusFile: string;
|
|
9
|
+
logFile: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function localGatewayStatus(): Promise<{
|
|
12
|
+
running: boolean;
|
|
13
|
+
starting: boolean;
|
|
14
|
+
foreground: boolean;
|
|
15
|
+
port?: number;
|
|
16
|
+
record?: ControlRecord;
|
|
17
|
+
}>;
|
|
18
|
+
export declare function startLocalGateway(entryPath: string): Promise<{
|
|
19
|
+
alreadyRunning: boolean;
|
|
20
|
+
port: number;
|
|
21
|
+
adminToken?: string;
|
|
22
|
+
logFile: string;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function stopLocalGateway(): Promise<boolean>;
|
|
25
|
+
export declare function removeLocalControlFile(): void;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=local-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-control.d.ts","sourceRoot":"","sources":["../../../src/gateway/local-control.ts"],"names":[],"mappings":"AAMA,UAAU,aAAa;IAAG,IAAI,EAAE,WAAW,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAGrG,wBAAgB,YAAY,IAAI;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAGtE;AA2CD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC,CAOvJ;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA4CnJ;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAiBzD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { defaultGatewayPaths } from "./local-paths.js";
|
|
6
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
export function controlFiles() {
|
|
8
|
+
const directory = dirname(defaultGatewayPaths().statePath);
|
|
9
|
+
return { statusFile: join(directory, "control.json"), logFile: join(directory, "gateway.log") };
|
|
10
|
+
}
|
|
11
|
+
function readRecord(file) {
|
|
12
|
+
try {
|
|
13
|
+
const input = JSON.parse(readFileSync(file, "utf8"));
|
|
14
|
+
if (!input || !["127.0.0.1", "::1"].includes(input.host ?? "") || !Number.isInteger(input.port) || input.port < 1 || input.port > 65535 || !/^[a-f0-9]{64}$/.test(input.token ?? "") || !Number.isFinite(input.createdAt))
|
|
15
|
+
return undefined;
|
|
16
|
+
return input;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function removeRecord(file, token) {
|
|
23
|
+
if (readRecord(file)?.token === token) {
|
|
24
|
+
try {
|
|
25
|
+
unlinkSync(file);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code !== "ENOENT")
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function endpoint(record) {
|
|
34
|
+
return `http://${record.host === "::1" ? "[::1]" : record.host}:${record.port}/__local/control`;
|
|
35
|
+
}
|
|
36
|
+
async function probe(record) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(endpoint(record), { headers: { Authorization: `Bearer ${record.token}` }, signal: AbortSignal.timeout(1000), redirect: "error" });
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
await response.body?.cancel();
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const result = await response.json();
|
|
44
|
+
return result.status === "running";
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function foregroundGateway() {
|
|
51
|
+
if (process.env.GATEWAY_PUBLIC_URL)
|
|
52
|
+
return undefined;
|
|
53
|
+
const host = process.env.GATEWAY_HOST ?? "127.0.0.1";
|
|
54
|
+
const port = Number(process.env.GATEWAY_PORT ?? "9091");
|
|
55
|
+
if (!["127.0.0.1", "::1"].includes(host) || !Number.isInteger(port) || port < 1 || port > 65535)
|
|
56
|
+
return undefined;
|
|
57
|
+
const base = `http://${host === "::1" ? "[::1]" : host}:${port}`;
|
|
58
|
+
try {
|
|
59
|
+
const health = await fetch(`${base}/healthz`, { signal: AbortSignal.timeout(800), redirect: "error" });
|
|
60
|
+
if (!health.ok || (await health.json()).status !== "ok")
|
|
61
|
+
return undefined;
|
|
62
|
+
const session = await fetch(`${base}/api/session`, { signal: AbortSignal.timeout(800), redirect: "error" });
|
|
63
|
+
return session.ok && (await session.json()).mode === "token" ? port : undefined;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function localGatewayStatus() {
|
|
70
|
+
const { statusFile } = controlFiles();
|
|
71
|
+
const record = readRecord(statusFile);
|
|
72
|
+
if (record && await probe(record))
|
|
73
|
+
return { running: true, starting: false, foreground: false, port: record.port, record };
|
|
74
|
+
const unmanagedPort = await foregroundGateway();
|
|
75
|
+
const starting = record ? Date.now() - record.createdAt < 30_000 : existsSync(statusFile) && Date.now() - statSync(statusFile).mtimeMs < 30_000;
|
|
76
|
+
return { running: false, starting, foreground: unmanagedPort !== undefined, port: unmanagedPort, record };
|
|
77
|
+
}
|
|
78
|
+
export async function startLocalGateway(entryPath) {
|
|
79
|
+
if (process.env.GATEWAY_PUBLIC_URL)
|
|
80
|
+
throw new Error("Background start is for the local gateway. Run the hosted gateway in foreground mode.");
|
|
81
|
+
const host = process.env.GATEWAY_HOST ?? "127.0.0.1";
|
|
82
|
+
if (host !== "127.0.0.1" && host !== "::1")
|
|
83
|
+
throw new Error("Background start requires loopback GATEWAY_HOST.");
|
|
84
|
+
const port = Number(process.env.GATEWAY_PORT ?? "9091");
|
|
85
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
86
|
+
throw new Error("GATEWAY_PORT must be between 1 and 65535.");
|
|
87
|
+
if (process.env.GATEWAY_ADMIN_TOKEN && process.env.GATEWAY_ADMIN_TOKEN.length < 32)
|
|
88
|
+
throw new Error("GATEWAY_ADMIN_TOKEN must be at least 32 characters.");
|
|
89
|
+
const { statusFile, logFile } = controlFiles();
|
|
90
|
+
mkdirSync(dirname(statusFile), { recursive: true, mode: 0o700 });
|
|
91
|
+
const prior = await localGatewayStatus();
|
|
92
|
+
if (prior.running)
|
|
93
|
+
return { alreadyRunning: true, port: prior.record.port, logFile };
|
|
94
|
+
if (prior.foreground)
|
|
95
|
+
throw new Error(`A gateway is already running in another terminal on port ${prior.port}. Stop it there with Ctrl+C before background start.`);
|
|
96
|
+
if (prior.starting)
|
|
97
|
+
throw new Error("The local gateway is still starting. Run status again shortly.");
|
|
98
|
+
if (existsSync(statusFile))
|
|
99
|
+
unlinkSync(statusFile);
|
|
100
|
+
const record = { host, port, token: randomBytes(32).toString("hex"), createdAt: Date.now() };
|
|
101
|
+
try {
|
|
102
|
+
writeFileSync(statusFile, JSON.stringify(record), { flag: "wx", mode: 0o600 });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error.code === "EEXIST")
|
|
106
|
+
throw new Error("Another gateway start is in progress.");
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
const adminToken = process.env.GATEWAY_ADMIN_TOKEN || randomBytes(32).toString("hex");
|
|
110
|
+
let logHandle;
|
|
111
|
+
let child;
|
|
112
|
+
try {
|
|
113
|
+
logHandle = openSync(logFile, "a", 0o600);
|
|
114
|
+
child = spawn(process.execPath, [entryPath, "run"], {
|
|
115
|
+
detached: true, windowsHide: true, stdio: ["ignore", logHandle, logHandle],
|
|
116
|
+
env: { ...process.env, GATEWAY_HOST: host, GATEWAY_PORT: String(port), GATEWAY_ADMIN_TOKEN: adminToken, GATEWAY_CONTROL_TOKEN: record.token, GATEWAY_CONTROL_FILE: statusFile },
|
|
117
|
+
});
|
|
118
|
+
closeSync(logHandle);
|
|
119
|
+
logHandle = undefined;
|
|
120
|
+
let spawnError;
|
|
121
|
+
let exitCode = null;
|
|
122
|
+
child.on("error", (error) => { spawnError = error; });
|
|
123
|
+
child.on("exit", (code) => { exitCode = code ?? 1; });
|
|
124
|
+
child.unref();
|
|
125
|
+
for (let attempt = 0; attempt < 75; attempt++) {
|
|
126
|
+
if (await probe(record))
|
|
127
|
+
return { alreadyRunning: false, port, adminToken: process.env.GATEWAY_ADMIN_TOKEN ? undefined : adminToken, logFile };
|
|
128
|
+
if (spawnError || exitCode !== null)
|
|
129
|
+
break;
|
|
130
|
+
await wait(200);
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`The gateway did not start. Check the gateway log and whether port ${port} is already in use.${spawnError ? ` ${spawnError.message}` : ""}`);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
try {
|
|
136
|
+
if (child?.exitCode === null)
|
|
137
|
+
child.kill();
|
|
138
|
+
}
|
|
139
|
+
catch { /* Startup already failed. */ }
|
|
140
|
+
if (logHandle !== undefined)
|
|
141
|
+
closeSync(logHandle);
|
|
142
|
+
removeRecord(statusFile, record.token);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export async function stopLocalGateway() {
|
|
147
|
+
const { statusFile } = controlFiles();
|
|
148
|
+
const record = readRecord(statusFile);
|
|
149
|
+
if (!record)
|
|
150
|
+
return false;
|
|
151
|
+
if (!(await probe(record))) {
|
|
152
|
+
if (Date.now() - record.createdAt < 30_000)
|
|
153
|
+
throw new Error("The local gateway is still starting. Try stop again shortly.");
|
|
154
|
+
removeRecord(statusFile, record.token);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const response = await fetch(endpoint(record), { method: "POST", headers: { Authorization: `Bearer ${record.token}` }, signal: AbortSignal.timeout(3000), redirect: "error" });
|
|
158
|
+
await response.body?.cancel();
|
|
159
|
+
if (response.status !== 202)
|
|
160
|
+
throw new Error(`Gateway stop failed (HTTP ${response.status}).`);
|
|
161
|
+
for (let attempt = 0; attempt < 50; attempt++) {
|
|
162
|
+
if (!(await probe(record))) {
|
|
163
|
+
removeRecord(statusFile, record.token);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
await wait(200);
|
|
167
|
+
}
|
|
168
|
+
throw new Error("Gateway stop timed out. Check the gateway log.");
|
|
169
|
+
}
|
|
170
|
+
export function removeLocalControlFile() {
|
|
171
|
+
const token = process.env.GATEWAY_CONTROL_TOKEN;
|
|
172
|
+
const file = process.env.GATEWAY_CONTROL_FILE;
|
|
173
|
+
if (token && file)
|
|
174
|
+
removeRecord(file, token);
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=local-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-control.js","sourceRoot":"","sources":["../../../src/gateway/local-control.ts"],"names":[],"mappings":"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"}
|