@integsec/agentic-pentest-proxy 0.3.8 → 0.3.9
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-scanners-mcp.js +1 -1
- package/dist/src/config.js +1 -1
- package/dist/src/gateway/admin.js +124 -18
- package/dist/src/gateway/command.d.ts +20 -0
- package/dist/src/gateway/command.d.ts.map +1 -0
- package/dist/src/gateway/command.js +80 -0
- package/dist/src/gateway/command.js.map +1 -0
- package/dist/src/gateway/runtime.d.ts +9 -0
- package/dist/src/gateway/runtime.d.ts.map +1 -1
- package/dist/src/gateway/runtime.js +102 -8
- package/dist/src/gateway/runtime.js.map +1 -1
- package/dist/src/gateway/server.d.ts.map +1 -1
- package/dist/src/gateway/server.js +13 -1
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/state.d.ts +15 -1
- package/dist/src/gateway/state.d.ts.map +1 -1
- package/dist/src/gateway/state.js +20 -6
- 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/proxy.d.ts +2 -1
- package/dist/src/proxy.d.ts.map +1 -1
- package/dist/src/proxy.js +7 -2
- package/dist/src/proxy.js.map +1 -1
- package/dist/src/validator.d.ts.map +1 -1
- package/dist/src/validator.js +3 -2
- package/dist/src/validator.js.map +1 -1
- package/docs/managed-gateway.md +8 -2
- package/package.json +1 -1
|
@@ -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.9" });
|
|
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.9" }, { 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());
|
|
@@ -21,7 +21,7 @@ const tool = mode === "nmap" ? {
|
|
|
21
21
|
url: { type: "string", description: "One authorized HTTP or HTTPS URL." },
|
|
22
22
|
}, required: ["url"], additionalProperties: false },
|
|
23
23
|
};
|
|
24
|
-
const server = new Server({ name: `integsec-${mode}-mcp`, version: "0.3.
|
|
24
|
+
const server = new Server({ name: `integsec-${mode}-mcp`, version: "0.3.9" }, { capabilities: { tools: {} } });
|
|
25
25
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [tool] }));
|
|
26
26
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
27
27
|
if (request.params.name !== tool.name)
|
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.9",
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
//# sourceMappingURL=config.js.map
|
|
@@ -9,6 +9,44 @@ let editing = null;
|
|
|
9
9
|
let viewingScopeId = null;
|
|
10
10
|
let dirty = false;
|
|
11
11
|
let bootstrapAdmins = new Set();
|
|
12
|
+
let upstreamStatuses = {};
|
|
13
|
+
const osNames = { windows: 'Windows', macos: 'macOS', linux: 'Linux (Debian/Ubuntu)' };
|
|
14
|
+
const platform = navigator.userAgentData?.platform || navigator.platform || navigator.userAgent || '';
|
|
15
|
+
const detectedOS = /win/i.test(platform) ? 'windows' : /mac/i.test(platform) ? 'macos' : 'linux';
|
|
16
|
+
let selectedOS = localStorage.getItem('gateway-docs-os');
|
|
17
|
+
if (!osNames[selectedOS]) selectedOS = detectedOS;
|
|
18
|
+
const installHelp = {
|
|
19
|
+
burp: {
|
|
20
|
+
windows: { command: "Start-Process 'https://portswigger.net/burp/releases'", note: 'Open the official installer, then enable MCP inside Burp.' },
|
|
21
|
+
macos: { command: "open 'https://portswigger.net/burp/releases'", note: 'Open the official installer, then enable MCP inside Burp.' },
|
|
22
|
+
linux: { command: "xdg-open 'https://portswigger.net/burp/releases'", note: 'Open the official installer, then enable MCP inside Burp.' },
|
|
23
|
+
},
|
|
24
|
+
semgrep: {
|
|
25
|
+
windows: { command: 'py -m pip install --user semgrep\nsemgrep --version', note: 'Requires Python and pip; confirm the Scripts folder is on PATH.' },
|
|
26
|
+
macos: { command: 'brew install semgrep\nsemgrep --version', note: 'Requires Homebrew.' },
|
|
27
|
+
linux: { command: 'pipx install semgrep\nsemgrep --version', note: 'Requires pipx; install it through your distribution first.' },
|
|
28
|
+
},
|
|
29
|
+
trivy: {
|
|
30
|
+
windows: { command: "Start-Process 'https://github.com/aquasecurity/trivy/releases/latest'", note: 'Install the official Windows archive, put trivy on PATH, then run trivy plugin install mcp.' },
|
|
31
|
+
macos: { command: 'brew install trivy\ntrivy plugin install mcp\ntrivy --version', note: 'Requires Homebrew.' },
|
|
32
|
+
linux: { command: "sudo apt-get update\nsudo apt-get install -y wget gnupg\nwget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null\necho 'deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main' | sudo tee /etc/apt/sources.list.d/trivy.list\nsudo apt-get update\nsudo apt-get install -y trivy\ntrivy plugin install mcp\ntrivy --version", note: 'Debian and Ubuntu. For other distributions, follow the official Trivy install guide.' },
|
|
33
|
+
},
|
|
34
|
+
nmap: {
|
|
35
|
+
windows: { command: "Start-Process 'https://nmap.org/download.html'", note: 'Run the official Windows installer. The MCP adapter is already in the gateway package.' },
|
|
36
|
+
macos: { command: "open 'https://nmap.org/download.html'", note: 'Run the official macOS installer. The MCP adapter is already in the gateway package.' },
|
|
37
|
+
linux: { command: 'sudo apt-get update\nsudo apt-get install nmap\nnmap --version', note: 'Debian and Ubuntu. Other distributions: use the official Nmap download page.' },
|
|
38
|
+
},
|
|
39
|
+
nuclei: {
|
|
40
|
+
windows: { command: 'go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest\nnuclei -version', note: 'Requires Go; add the Go bin folder to PATH. The MCP adapter is in the gateway package.' },
|
|
41
|
+
macos: { command: 'brew install nuclei\nnuclei -version', note: 'Requires Homebrew. The MCP adapter is in the gateway package.' },
|
|
42
|
+
linux: { command: 'go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest\nnuclei -version', note: 'Requires Go; add the Go bin folder to PATH. The MCP adapter is in the gateway package.' },
|
|
43
|
+
},
|
|
44
|
+
snyk: {
|
|
45
|
+
windows: { command: 'npm install -g snyk\nsnyk auth', note: 'Requires Node.js and a Snyk account.' },
|
|
46
|
+
macos: { command: 'npm install -g snyk\nsnyk auth', note: 'Requires Node.js and a Snyk account.' },
|
|
47
|
+
linux: { command: 'npm install -g snyk\nsnyk auth', note: 'Requires Node.js and a Snyk account.' },
|
|
48
|
+
},
|
|
49
|
+
};
|
|
12
50
|
const techniques = [
|
|
13
51
|
['recon', 'Reconnaissance and discovery'], ['web_app', 'Web and API testing'],
|
|
14
52
|
['exploitation', 'Exploitation'], ['cloud_audit', 'Cloud assessment'],
|
|
@@ -16,12 +54,12 @@ const techniques = [
|
|
|
16
54
|
['social_engineering', 'Social engineering'],
|
|
17
55
|
];
|
|
18
56
|
const presets = [
|
|
19
|
-
{ name: 'burp', label: 'Burp Suite', transport: 'sse', url: 'http://127.0.0.1:9876', local: true, source: 'https://
|
|
20
|
-
{ name: 'semgrep', label: 'Semgrep', transport: 'stdio', command: 'semgrep', args: ['mcp'], local: true, source: 'https://
|
|
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/
|
|
23
|
-
{ name: 'nuclei', label: 'Nuclei', transport: 'stdio', command: 'integsec-agentic-pentest-scanners-mcp', args: ['nuclei'], local: true, source: 'https://
|
|
24
|
-
{ name: 'snyk', label: 'Snyk', transport: 'stdio', command: 'snyk', args: ['mcp', '-t', 'stdio'], local: true, source: 'https://
|
|
57
|
+
{ name: 'burp', label: 'Burp Suite', transport: 'sse', url: 'http://127.0.0.1:9876', local: true, source: 'https://portswigger.net/bappstore/9952290f04ed4f628e624d0aa9dccebc', note: 'Enable the official MCP extension inside the tester’s local Burp.' },
|
|
58
|
+
{ name: 'semgrep', label: 'Semgrep', transport: 'stdio', command: 'semgrep', args: ['mcp'], local: true, source: 'https://semgrep.dev/products/community-edition/', note: 'Install and configure the official Semgrep CLI first.' },
|
|
59
|
+
{ name: 'trivy', label: 'Trivy', transport: 'stdio', command: 'trivy', args: ['mcp'], local: true, source: 'https://github.com/aquasecurity/trivy-mcp/blob/main/docs/quickstart.md', note: 'Install Trivy and its official MCP plugin first; pin and review your version.' },
|
|
60
|
+
{ name: 'nmap', label: 'Nmap', transport: 'stdio', command: 'integsec-agentic-pentest-scanners-mcp', args: ['nmap'], local: true, source: 'https://nmap.org/download.html', note: 'Install Nmap locally first. The bundled MCP adapter offers one-host, rate-limited TCP scans.' },
|
|
61
|
+
{ name: 'nuclei', label: 'Nuclei', transport: 'stdio', command: 'integsec-agentic-pentest-scanners-mcp', args: ['nuclei'], local: true, source: 'https://docs.projectdiscovery.io/opensource/nuclei/install', note: 'Install ProjectDiscovery Nuclei locally first. The bundled MCP adapter limits HTTP scans and disables OAST.' },
|
|
62
|
+
{ name: 'snyk', label: 'Snyk', transport: 'stdio', command: 'snyk', args: ['mcp', '-t', 'stdio'], local: true, source: 'https://docs.snyk.io/developer-tools/snyk-cli/install-the-snyk-cli', note: 'Install the Snyk CLI and authenticate locally first.' },
|
|
25
63
|
{ 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.' },
|
|
26
64
|
{ 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.' },
|
|
27
65
|
{ label: 'Prowler Cloud', source: 'https://prowler.com/prowler-cloud', note: 'Reference only: its hosted MCP endpoint requires an API key.' },
|
|
@@ -56,6 +94,7 @@ function configureServerUI() {
|
|
|
56
94
|
: 'Use the command or URL from the MCP server’s own setup instructions. Adding an entry does not install the tool.';
|
|
57
95
|
$('hosted-server-choice').hidden = !hosted;
|
|
58
96
|
$('local-presets').hidden = hosted;
|
|
97
|
+
$('command-panel').hidden = hosted;
|
|
59
98
|
$('local-setup').hidden = !hosted;
|
|
60
99
|
$('add-server-panel').hidden = hosted;
|
|
61
100
|
if (hosted) chooseServerPath('local');
|
|
@@ -84,12 +123,60 @@ localPathInput.id = 'local-paths';
|
|
|
84
123
|
localPathInput.placeholder = 'Absolute folder path containing authorized source code';
|
|
85
124
|
localPathField.append(localPathInput);
|
|
86
125
|
$('hosts').closest('label').after(localPathField);
|
|
126
|
+
const allLocalLabel = document.createElement('label'); allLocalLabel.style.cssText = 'display:flex;align-items:center;gap:8px';
|
|
127
|
+
const allLocalInput = document.createElement('input'); allLocalInput.type = 'checkbox'; allLocalInput.id = 'all-local-paths';
|
|
128
|
+
allLocalLabel.append(allLocalInput, document.createTextNode('Allow all local code paths (explicit administrator approval; may expose unrelated files)'));
|
|
129
|
+
localPathField.after(allLocalLabel);
|
|
130
|
+
allLocalInput.onchange = () => { localPathInput.disabled = allLocalInput.checked; };
|
|
87
131
|
const make = (tag, value) => { const node = document.createElement(tag); node.textContent = value; return node; };
|
|
132
|
+
const osBar = make('div', ''); osBar.className = 'help'; osBar.style.cssText = 'margin:0;padding:10px 5vw;display:flex;align-items:center;gap:12px;flex-wrap:wrap';
|
|
133
|
+
const osLabel = make('label', 'Install commands for '); osLabel.style.cssText = 'display:flex;align-items:center;gap:8px';
|
|
134
|
+
const osChoice = document.createElement('select'); osChoice.id = 'docs-os'; osChoice.style.cssText = 'width:auto;margin:0';
|
|
135
|
+
for (const [value, label] of Object.entries(osNames)) { const option = make('option', label); option.value = value; osChoice.append(option); }
|
|
136
|
+
osChoice.value = selectedOS; osLabel.append(osChoice); osBar.append(osLabel, make('span', 'Choose the tester’s operating system. The browser picked a starting value.'));
|
|
137
|
+
document.querySelector('header').after(osBar);
|
|
138
|
+
const toolStyles = document.createElement('style');
|
|
139
|
+
toolStyles.textContent = '#upstreams-card{grid-column:1/-1}#presets,#local-tool-guide-items{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;margin-top:12px}#presets .row,#local-tool-guide-items .row{display:block;border:1px solid #294454;border-radius:9px;padding:14px}#presets .row>*:first-child,#local-tool-guide-items .row>*:first-child{min-width:0}#presets pre,#local-tool-guide-items pre{font-size:13px}';
|
|
140
|
+
document.head.append(toolStyles);
|
|
141
|
+
const localGuide = make('section', ''); localGuide.id = 'local-tool-guide'; localGuide.className = 'card wide'; localGuide.hidden = true;
|
|
142
|
+
localGuide.append(make('h2', 'Local MCP tools'), make('p', 'Install these on the tester’s computer, then add and turn them on in that computer’s local gateway. The hosted gateway cannot run laptop tools.'));
|
|
143
|
+
const localGuideItems = make('div', ''); localGuideItems.id = 'local-tool-guide-items'; localGuide.append(localGuideItems);
|
|
144
|
+
$('my-card').after(localGuide);
|
|
145
|
+
function updateInstallOS() {
|
|
146
|
+
const shell = selectedOS === 'windows' ? 'PowerShell' : 'a terminal';
|
|
147
|
+
$('local-command').textContent = 'npm install -g @integsec/agentic-pentest-proxy\nintegsec-gateway start';
|
|
148
|
+
$('local-command').closest('li').firstChild.textContent = `Copy these commands into ${shell}:`;
|
|
149
|
+
renderPresets();
|
|
150
|
+
}
|
|
151
|
+
osChoice.onchange = () => { selectedOS = osChoice.value; localStorage.setItem('gateway-docs-os', selectedOS); updateInstallOS(); };
|
|
88
152
|
function button(label, action, style = 'secondary') {
|
|
89
153
|
const node = make('button', label); node.className = style;
|
|
90
154
|
node.addEventListener('click', () => Promise.resolve().then(action).catch((error) => status(error.message, true)));
|
|
91
155
|
return node;
|
|
92
156
|
}
|
|
157
|
+
const commandPanel = make('section', ''); commandPanel.id = 'command-panel'; commandPanel.hidden = true;
|
|
158
|
+
commandPanel.append(make('h3', 'Add a local command as an MCP tool'),
|
|
159
|
+
make('p', 'Use an installed executable and fixed arguments. Put {target} on one argument line. The AI can supply one approved host or URL; it cannot supply flags. Review the command and its configuration before turning it on.'));
|
|
160
|
+
const commandFields = make('div', ''); commandFields.className = 'grid';
|
|
161
|
+
const commandNameLabel = make('label', 'Short name'); const commandName = document.createElement('input'); commandName.id = 'cli-name'; commandName.placeholder = 'my_scanner'; commandNameLabel.append(commandName);
|
|
162
|
+
const commandExeLabel = make('label', 'Installed executable'); const commandExe = document.createElement('input'); commandExe.id = 'cli-command'; commandExe.placeholder = 'scanner'; commandExeLabel.append(commandExe);
|
|
163
|
+
const commandTargetLabel = make('label', 'Target type'); const commandTarget = document.createElement('select'); commandTarget.id = 'cli-target';
|
|
164
|
+
for (const [value, label] of [['host', 'Hostname or IP'], ['url', 'HTTP or HTTPS URL']]) { const option = make('option', label); option.value = value; commandTarget.append(option); }
|
|
165
|
+
commandTargetLabel.append(commandTarget);
|
|
166
|
+
const commandTechniqueLabel = make('label', 'Technique category'); const commandTechnique = document.createElement('select'); commandTechnique.id = 'cli-technique';
|
|
167
|
+
for (const [value, label] of techniques) { const option = make('option', label); option.value = value; commandTechnique.append(option); }
|
|
168
|
+
commandTechniqueLabel.append(commandTechnique);
|
|
169
|
+
commandFields.append(commandNameLabel, commandExeLabel, commandTargetLabel, commandTechniqueLabel); commandPanel.append(commandFields);
|
|
170
|
+
const commandArgsLabel = make('label', 'Fixed arguments, one per line'); const commandArgs = document.createElement('textarea'); commandArgs.id = 'cli-args'; commandArgs.placeholder = '-u\n{target}\n-json'; commandArgsLabel.append(commandArgs); commandPanel.append(commandArgsLabel);
|
|
171
|
+
commandPanel.append(button('Add command (off)', async () => {
|
|
172
|
+
const name = commandName.value.trim(); const executable = commandExe.value.trim(); const args = commandArgs.value.split(/\r?\n/).map((arg) => arg.trim()).filter(Boolean);
|
|
173
|
+
if (!/^[a-z][a-z0-9_-]{0,31}$/.test(name)) throw new Error('Use a short lowercase command name starting with a letter.');
|
|
174
|
+
if (!executable) throw new Error('Enter the installed executable name or path.');
|
|
175
|
+
if (args.filter((arg) => arg === '{target}').length !== 1) throw new Error('Put {target} on exactly one argument line.');
|
|
176
|
+
await api('/api/upstreams', 'POST', { name, enabled: false, transport: 'cli', command: executable, args, targetKind: commandTarget.value, technique: commandTechnique.value });
|
|
177
|
+
commandName.value = ''; commandExe.value = ''; commandArgs.value = ''; await refresh(); status('Local command saved and off. Review it, then turn it on.');
|
|
178
|
+
}));
|
|
179
|
+
$('local-presets').after(commandPanel);
|
|
93
180
|
function choiceList(container, name) {
|
|
94
181
|
const result = [];
|
|
95
182
|
for (const [value, label] of techniques) {
|
|
@@ -111,13 +198,13 @@ function draftFromForm() {
|
|
|
111
198
|
return { profile: $('profile').value, allowedHosts: lines('hosts'), manifest: {
|
|
112
199
|
engagement_id: $('engagement').value.trim(), client: $('client').value.trim(), operator: $('operator').value.trim(),
|
|
113
200
|
authorized_targets: { ip_ranges: lines('ips'), domains: lines('domains'), urls: lines('urls'), cloud_accounts: lines('cloud') },
|
|
114
|
-
excluded_targets: lines('excluded-targets'), authorized_local_paths: lines('local-paths'), authorized_techniques: checked(authorizedChoices),
|
|
201
|
+
excluded_targets: lines('excluded-targets'), authorized_local_paths: allLocalInput.checked ? ['*'] : lines('local-paths'), authorized_techniques: checked(authorizedChoices),
|
|
115
202
|
excluded_techniques: checked(excludedChoices), engagement_window: { start: start.toISOString(), end: end.toISOString() },
|
|
116
203
|
} };
|
|
117
204
|
}
|
|
118
205
|
function summarize(scope) {
|
|
119
206
|
const m = scope.manifest;
|
|
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)'}`;
|
|
207
|
+
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?.includes('*') ? 'ALL accessible local paths' : (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)'}`;
|
|
121
208
|
}
|
|
122
209
|
function downloadPolicy(scope) {
|
|
123
210
|
const policy = { ...scope.manifest, gateway: { profile: scope.profile, allowed_hosts: scope.allowedHosts || [] } };
|
|
@@ -152,6 +239,7 @@ function reviewForm() {
|
|
|
152
239
|
if (m.authorized_targets.domains.some((value) => value.includes('example.'))) warnings.push('Replace documentation domains with real authorized domains.');
|
|
153
240
|
if (m.authorized_targets.urls.some((value) => value.includes('example.'))) warnings.push('Replace documentation URLs with real authorized URLs.');
|
|
154
241
|
if (Date.parse(m.engagement_window.end) <= Date.now()) warnings.push('Choose an end time in the future.');
|
|
242
|
+
if (m.authorized_local_paths.includes('*')) warnings.push('All accessible local paths are allowed for Semgrep and Trivy. Review this broad access before approval.');
|
|
155
243
|
$('scope-review').textContent = `${warnings.length ? `Before approval: ${warnings.join(' ')}\n\n` : ''}${summarize(scope)}`;
|
|
156
244
|
} catch (error) { $('scope-review').textContent = error.message; }
|
|
157
245
|
updateEditorActions();
|
|
@@ -181,8 +269,11 @@ async function refresh() {
|
|
|
181
269
|
$('connection').textContent = `MCP endpoint: ${location.origin}/mcp. ${selected ? `Current engagement: ${selected.manifest.engagement_id}.` : 'Select an engagement before using tools.'}`;
|
|
182
270
|
if (role === 'admin') {
|
|
183
271
|
state = await api('/api/state');
|
|
184
|
-
|
|
272
|
+
upstreamStatuses = await api('/api/upstreams/status');
|
|
273
|
+
renderUsers(); renderUpstreams(); renderScopes();
|
|
274
|
+
for (const card of document.querySelectorAll('.admin-only')) card.hidden = card.id === 'users-card' && mode !== 'access';
|
|
185
275
|
}
|
|
276
|
+
renderPresets();
|
|
186
277
|
status('Gateway state loaded.');
|
|
187
278
|
}
|
|
188
279
|
function renderUsers() {
|
|
@@ -202,15 +293,20 @@ function renderUpstreams() {
|
|
|
202
293
|
const container = $('upstreams'); container.replaceChildren();
|
|
203
294
|
for (const item of state.upstreams) {
|
|
204
295
|
const row = make('div', ''); row.className = 'row';
|
|
205
|
-
|
|
206
|
-
|
|
296
|
+
const check = upstreamStatuses[item.id] || { state: item.enabled ? 'not_checked' : 'off' };
|
|
297
|
+
const connection = check.state === 'connected' ? `Connected · ${check.toolCount} tools found` : check.state === 'error' ? 'Connection failed' : check.state === 'configured' ? 'Runs on demand' : check.state === 'not_checked' ? 'Not checked' : 'Off';
|
|
298
|
+
row.append(make('span', `${item.name} · ${item.transport === 'cli' ? 'local command' : item.transport} · ${item.enabled ? 'On' : 'Off'} · ${connection} · ${['stdio', 'cli'].includes(item.transport) ? item.command : item.url}`));
|
|
299
|
+
if (check.detail && item.enabled) row.append(make('span', check.detail));
|
|
300
|
+
row.append(button(item.enabled ? 'Turn off' : 'Turn on', async () => { await api(`/api/upstreams/${item.id}`, 'PATCH', { enabled: !item.enabled }); await refresh(); status(`${item.name} turned ${item.enabled ? 'off' : 'on'} immediately. Refresh the tool list in a client that caches it.`); }));
|
|
301
|
+
if (item.enabled && item.transport !== 'cli') row.append(button('Check connection', async () => { await api(`/api/upstreams/${item.id}/check`, 'POST'); await refresh(); }));
|
|
207
302
|
row.append(button('Remove', async () => { if (confirm(`Remove server ${item.name}?`)) { await api(`/api/upstreams/${item.id}`, 'DELETE'); await refresh(); } }, 'danger'));
|
|
208
303
|
container.append(row);
|
|
209
304
|
}
|
|
210
305
|
if (!state.upstreams.length) container.append(make('p', 'No MCP servers added yet. Choose a connection path below.'));
|
|
211
306
|
}
|
|
212
307
|
function renderPresets() {
|
|
213
|
-
const container = $('presets'); container.replaceChildren();
|
|
308
|
+
const container = mode === 'access' ? localGuideItems : $('presets'); container.replaceChildren();
|
|
309
|
+
localGuide.hidden = mode !== 'access';
|
|
214
310
|
const references = $('references'); references.replaceChildren();
|
|
215
311
|
for (const preset of presets) {
|
|
216
312
|
if (!preset.name) {
|
|
@@ -219,12 +315,17 @@ function renderPresets() {
|
|
|
219
315
|
const link = make('a', 'Official information'); link.href = preset.source; link.target = '_blank'; link.rel = 'noopener noreferrer'; row.append(link);
|
|
220
316
|
references.append(row); continue;
|
|
221
317
|
}
|
|
222
|
-
if (mode === 'access') continue;
|
|
223
318
|
const row = make('div', ''); row.className = 'row'; const detail = make('div', `${preset.label} · ${preset.note} `);
|
|
224
319
|
const link = make('a', 'Official information'); link.href = preset.source; link.target = '_blank'; link.rel = 'noopener noreferrer'; detail.append(link);
|
|
320
|
+
const install = installHelp[preset.name]?.[selectedOS];
|
|
321
|
+
if (install) {
|
|
322
|
+
detail.append(make('p', install.note));
|
|
323
|
+
const code = make('code', install.command); const pre = make('pre', ''); pre.append(code); detail.append(pre);
|
|
324
|
+
detail.append(button('Copy command', async () => { await navigator.clipboard.writeText(install.command); status(`${preset.label} ${osNames[selectedOS]} command copied.`); }));
|
|
325
|
+
}
|
|
225
326
|
row.append(detail);
|
|
226
|
-
if (state
|
|
227
|
-
else row.append(button('Add server (off)', async () => { await api('/api/upstreams', 'POST', { name: preset.name, enabled: false, transport: preset.transport, command: preset.command, args: preset.args || [], url: preset.url }); await refresh(); status(`${preset.label} added and off. Turn it on when ready.`); }));
|
|
327
|
+
if (mode !== 'access' && state?.upstreams.some((item) => item.name === preset.name)) row.append(make('span', 'Added'));
|
|
328
|
+
else if (mode !== 'access') row.append(button('Add server (off)', async () => { await api('/api/upstreams', 'POST', { name: preset.name, enabled: false, transport: preset.transport, command: preset.command, args: preset.args || [], url: preset.url }); await refresh(); status(`${preset.label} added and off. Turn it on when ready.`); }));
|
|
228
329
|
container.append(row);
|
|
229
330
|
}
|
|
230
331
|
}
|
|
@@ -258,9 +359,11 @@ function loadScope(scope) {
|
|
|
258
359
|
$('starts').value = localDate(m.engagement_window.start); $('ends').value = localDate(m.engagement_window.end);
|
|
259
360
|
$('profile').value = scope.profile; setLines('ips', m.authorized_targets.ip_ranges); setLines('domains', m.authorized_targets.domains);
|
|
260
361
|
setLines('urls', m.authorized_targets.urls); setLines('cloud', m.authorized_targets.cloud_accounts);
|
|
261
|
-
setLines('excluded-targets', m.excluded_targets); setLines('hosts', scope.allowedHosts); setLines('local-paths', m.authorized_local_paths || []);
|
|
362
|
+
setLines('excluded-targets', m.excluded_targets); setLines('hosts', scope.allowedHosts); setLines('local-paths', (m.authorized_local_paths || []).filter((path) => path !== '*'));
|
|
363
|
+
allLocalInput.checked = m.authorized_local_paths?.includes('*') || false;
|
|
262
364
|
setChecked(authorizedChoices, m.authorized_techniques); setChecked(excludedChoices, m.excluded_techniques);
|
|
263
365
|
for (const field of $('editor-card').querySelectorAll('input,select,textarea')) field.disabled = scope.status !== 'draft';
|
|
366
|
+
localPathInput.disabled = allLocalInput.checked || scope.status !== 'draft';
|
|
264
367
|
$('save-scope').disabled = scope.status !== 'draft';
|
|
265
368
|
$('editor-help').textContent = scope.status === 'draft' ? 'Editing a saved scope. Replace any example details and choose a future end date before approval.' : 'Viewing an immutable scope. Choose Duplicate to create an editable scope.';
|
|
266
369
|
reviewForm(); $('editor-card').scrollIntoView({ behavior: 'smooth' });
|
|
@@ -268,6 +371,7 @@ function loadScope(scope) {
|
|
|
268
371
|
function clearEditor() {
|
|
269
372
|
editing = null; viewingScopeId = null; dirty = false;
|
|
270
373
|
for (const id of ['engagement', 'client', 'operator', 'starts', 'ends', 'ips', 'domains', 'urls', 'cloud', 'excluded-targets', 'hosts', 'local-paths']) $(id).value = '';
|
|
374
|
+
allLocalInput.checked = false;
|
|
271
375
|
$('profile').value = mode === 'access' ? 'connected' : 'isolated'; setChecked(authorizedChoices, []); setChecked(excludedChoices, []);
|
|
272
376
|
for (const field of $('editor-card').querySelectorAll('input,select,textarea')) field.disabled = false;
|
|
273
377
|
$('save-scope').disabled = false; $('editor-help').textContent = 'New scope. Enter the client, exact authorized targets, window and techniques, then Save.'; reviewForm();
|
|
@@ -303,7 +407,7 @@ $('clear-selection').onclick = () => api('/api/my-scope', 'POST', { scopeId: nul
|
|
|
303
407
|
$('choose-local').onclick = () => chooseServerPath('local');
|
|
304
408
|
$('choose-remote').onclick = () => chooseServerPath('remote');
|
|
305
409
|
$('copy-local-command').onclick = async () => { try {
|
|
306
|
-
await navigator.clipboard.writeText($('local-command').textContent.trim()); status(
|
|
410
|
+
await navigator.clipboard.writeText($('local-command').textContent.trim()); status(`Local setup commands copied. Paste them into ${selectedOS === 'windows' ? 'PowerShell' : 'a terminal'} on the tester’s computer.`);
|
|
307
411
|
} catch { status('Copy failed. Select the commands above and copy them manually.', true); } };
|
|
308
412
|
$('up-transport').onchange = updateTransportFields;
|
|
309
413
|
$('add-user').onclick = async () => { try { await api('/api/users', 'POST', { email: $('user-email').value.trim(), role: $('user-role').value }); $('user-email').value = ''; await refresh(); } catch (error) { status(error.message, true); } };
|
|
@@ -347,8 +451,10 @@ async function initialize() {
|
|
|
347
451
|
bootstrapAdmins = new Set(session.bootstrapAdminEmails || []);
|
|
348
452
|
$('identity').textContent = email ? `${email} · ${role}` : 'Local gateway';
|
|
349
453
|
$('token-card').hidden = mode !== 'token';
|
|
350
|
-
for (const card of document.querySelectorAll('.admin-only')) card.hidden =
|
|
454
|
+
for (const card of document.querySelectorAll('.admin-only')) card.hidden = true;
|
|
351
455
|
configureServerUI();
|
|
456
|
+
$('token-card').querySelector('p').textContent = 'Paste the admin token printed when the gateway started. It stays in this browser tab only.';
|
|
457
|
+
updateInstallOS();
|
|
352
458
|
clearEditor();
|
|
353
459
|
if (mode === 'access' || token.value) await refresh();
|
|
354
460
|
reviewForm();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Upstream } from "./state.js";
|
|
2
|
+
export declare const commandToolName = "run_local_command";
|
|
3
|
+
export declare function commandTool(upstream: Upstream): {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object";
|
|
8
|
+
properties: {
|
|
9
|
+
target: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
required: string[];
|
|
15
|
+
additionalProperties: boolean;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare function commandTarget(upstream: Upstream, input: Record<string, unknown>): string;
|
|
19
|
+
export declare function runCommand(upstream: Upstream, target: string, signal?: AbortSignal): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/gateway/command.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ;;;;;;;;;;;;;;EAQ7C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAmBxF;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC1G"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { isIP } from "node:net";
|
|
3
|
+
const MAX_OUTPUT = 64 * 1024;
|
|
4
|
+
const MAX_DURATION = 120_000;
|
|
5
|
+
export const commandToolName = "run_local_command";
|
|
6
|
+
export function commandTool(upstream) {
|
|
7
|
+
return {
|
|
8
|
+
name: `${upstream.name}__${commandToolName}`,
|
|
9
|
+
description: `Run the administrator-configured ${upstream.name} command against one approved ${upstream.targetKind === "url" ? "HTTP URL" : "host or IP"}. Fixed arguments only; two-minute limit.`,
|
|
10
|
+
inputSchema: { type: "object", properties: {
|
|
11
|
+
target: { type: "string", description: upstream.targetKind === "url" ? "One authorized HTTP or HTTPS URL." : "One authorized hostname or IP address." },
|
|
12
|
+
}, required: ["target"], additionalProperties: false },
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function commandTarget(upstream, input) {
|
|
16
|
+
if (Object.keys(input).length !== 1 || typeof input.target !== "string" || !input.target ||
|
|
17
|
+
input.target !== input.target.trim() || input.target.length > 2048 || /[\x00-\x20\x7f]/.test(input.target))
|
|
18
|
+
throw new Error("Enter one target; extra options and control characters are not accepted.");
|
|
19
|
+
const target = input.target;
|
|
20
|
+
if (upstream.targetKind === "host") {
|
|
21
|
+
if (isIP(target))
|
|
22
|
+
return target;
|
|
23
|
+
if (!/^(?=.{1,253}$)(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)+[a-z](?:[a-z\d-]{0,61}[a-z\d])?$/i.test(target))
|
|
24
|
+
throw new Error("Enter one hostname or IP without a URL, range, or command options.");
|
|
25
|
+
return target;
|
|
26
|
+
}
|
|
27
|
+
if (upstream.targetKind === "url") {
|
|
28
|
+
let url;
|
|
29
|
+
try {
|
|
30
|
+
url = new URL(target);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error("Enter one HTTP or HTTPS URL.");
|
|
34
|
+
}
|
|
35
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || !url.hostname)
|
|
36
|
+
throw new Error("URL must be HTTP or HTTPS without credentials, a query, or a fragment.");
|
|
37
|
+
return url.href;
|
|
38
|
+
}
|
|
39
|
+
throw new Error("Command target type is missing.");
|
|
40
|
+
}
|
|
41
|
+
export async function runCommand(upstream, target, signal) {
|
|
42
|
+
if (upstream.transport !== "cli" || !upstream.command || upstream.args.filter((arg) => arg === "{target}").length !== 1)
|
|
43
|
+
throw new Error("Invalid command configuration.");
|
|
44
|
+
const args = upstream.args.map((arg) => arg === "{target}" ? target : arg);
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const child = spawn(upstream.command, args, { shell: false, windowsHide: true, stdio: ["ignore", "pipe", "pipe"], signal });
|
|
47
|
+
let output = "";
|
|
48
|
+
let size = 0;
|
|
49
|
+
let finished = false;
|
|
50
|
+
const fail = (message) => {
|
|
51
|
+
if (finished)
|
|
52
|
+
return;
|
|
53
|
+
finished = true;
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
child.kill();
|
|
56
|
+
reject(new Error(message));
|
|
57
|
+
};
|
|
58
|
+
const timer = setTimeout(() => fail("Command exceeded the two-minute limit."), MAX_DURATION);
|
|
59
|
+
const collect = (chunk) => {
|
|
60
|
+
size += chunk.length;
|
|
61
|
+
if (size > MAX_OUTPUT)
|
|
62
|
+
return fail("Command output exceeded 64 KiB; narrow the request.");
|
|
63
|
+
output += chunk.toString("utf8");
|
|
64
|
+
};
|
|
65
|
+
child.stdout.on("data", collect);
|
|
66
|
+
child.stderr.on("data", collect);
|
|
67
|
+
child.on("error", () => fail(signal?.aborted ? "Command stopped because its server was turned off." : "Command could not start. Check the executable name and PATH on the local gateway computer."));
|
|
68
|
+
child.on("close", (code) => {
|
|
69
|
+
if (finished)
|
|
70
|
+
return;
|
|
71
|
+
finished = true;
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
if (code !== 0)
|
|
74
|
+
reject(new Error(`Command exited with code ${code}. ${output.slice(0, 2000)}`));
|
|
75
|
+
else
|
|
76
|
+
resolve(output || "Command completed with no output.");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/gateway/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7B,MAAM,YAAY,GAAG,OAAO,CAAC;AAE7B,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAEnD,MAAM,UAAU,WAAW,CAAC,QAAkB;IAC5C,OAAO;QACL,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,KAAK,eAAe,EAAE;QAC5C,WAAW,EAAE,oCAAoC,QAAQ,CAAC,IAAI,iCAAiC,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,2CAA2C;QACnM,WAAW,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,UAAU,EAAE;gBAClD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC,wCAAwC,EAAE;aACxJ,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,oBAAoB,EAAE,KAAK,EAAE;KACvD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAkB,EAAE,KAA8B;IAC9E,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM;QACpF,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC5G,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,QAAQ,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QAChC,IAAI,CAAC,yFAAyF,CAAC,IAAI,CAAC,MAAM,CAAC;YACzG,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QAClC,IAAI,GAAQ,CAAC;QACb,IAAI,CAAC;YAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ;YACxH,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAkB,EAAE,MAAc,EAAE,MAAoB;IACvF,IAAI,QAAQ,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC;QACrH,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7H,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE;YAC/B,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,CAAC,IAAI,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,wCAAwC,CAAC,EAAE,YAAY,CAAC,CAAC;QAC7F,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE;YAChC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,IAAI,IAAI,GAAG,UAAU;gBAAE,OAAO,IAAI,CAAC,qDAAqD,CAAC,CAAC;YAC1F,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,4FAA4F,CAAC,CAAC,CAAC;QACrM,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,IAAI,KAAK,CAAC;gBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;;gBAC3F,OAAO,CAAC,MAAM,IAAI,mCAAmC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -9,8 +9,17 @@ export declare class GatewayRuntime {
|
|
|
9
9
|
private clients;
|
|
10
10
|
private pending;
|
|
11
11
|
private proxies;
|
|
12
|
+
private statuses;
|
|
13
|
+
private runningCommands;
|
|
12
14
|
constructor(store: GatewayStore, hosted: boolean, requiredAuditSink?: ((entry: AuditEntry) => Promise<void>) | undefined, isActorAllowed?: ((email?: string) => boolean) | undefined);
|
|
13
15
|
sync(): Promise<void>;
|
|
16
|
+
upstreamStatuses(): Record<string, {
|
|
17
|
+
state: "off" | "not_checked" | "connected" | "error" | "configured";
|
|
18
|
+
toolCount?: number;
|
|
19
|
+
checkedAt?: string;
|
|
20
|
+
detail?: string;
|
|
21
|
+
}>;
|
|
22
|
+
checkUpstream(id: string): Promise<void>;
|
|
14
23
|
private scopeProxy;
|
|
15
24
|
private clientFor;
|
|
16
25
|
private available;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/gateway/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAiE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/gateway/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAiE,MAAM,YAAY,CAAC;AAWzG,qBAAa,cAAc;IAOb,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;IANvL,OAAO,CAAC,OAAO,CAA4D;IAC3E,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,QAAQ,CAA+G;IAC/H,OAAO,CAAC,eAAe,CAA8E;gBAEhF,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;IAqB3B,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,KAAK,GAAG,aAAa,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAK9J,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9C,OAAO,CAAC,UAAU;YAYJ,SAAS;IA2BvB,OAAO,CAAC,SAAS;IAOjB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IA6I9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
|