@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.
@@ -10,7 +10,7 @@
10
10
  "containers": [
11
11
  {
12
12
  "class_name": "PentestGatewayContainer",
13
- "image": "docker.io/integsec/agentic-pentest-mcp-proxy:0.3.8",
13
+ "image": "docker.io/integsec/agentic-pentest-mcp-proxy:0.3.9",
14
14
  "instance_type": "basic",
15
15
  "max_instances": 1
16
16
  }
@@ -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.8" });
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.8" }, { capabilities: { tools: {} } });
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.8" }, { capabilities: { tools: {} } });
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)
@@ -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.8",
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://github.com/PortSwigger/mcp-server', note: 'Enable the official MCP extension inside the tester’s local Burp.' },
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
- { 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.' },
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.' },
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
- renderUsers(); renderUpstreams(); renderPresets(); renderScopes();
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
- row.append(make('span', `${item.name} · ${item.transport} · ${item.enabled ? 'On' : 'Off'} · ${item.transport === 'stdio' ? item.command : item.url}`));
206
- row.append(button(item.enabled ? 'Turn off' : 'Turn on', async () => { await api(`/api/upstreams/${item.id}`, 'PATCH', { enabled: !item.enabled }); await refresh(); }));
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.upstreams.some((item) => item.name === preset.name)) row.append(make('span', 'Added'));
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('Local setup commands copied. Paste them into PowerShell on the tester’s computer.');
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 = role !== 'admin' || (card.id === 'users-card' && mode !== 'access');
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":"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"}
@@ -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;AAUzG,qBAAa,cAAc;IAKb,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;IAJvL,OAAO,CAAC,OAAO,CAA4D;IAC3E,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,OAAO,CAA4C;gBAEtC,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;IAY3B,OAAO,CAAC,UAAU;YAWJ,SAAS;IA2BvB,OAAO,CAAC,SAAS;IAOjB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM;IA8G9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
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"}