MEDIUM: Webview proxy allows proxying to any container port without restriction #31

Closed
opened 2026-05-08 23:51:39 +00:00 by Mark · 2 comments
Owner

Issue

The webview proxy endpoints (/sessions/{id}/webview/{port}) allow proxying HTTP requests to ANY port inside a session container. There is no port whitelist or restriction, meaning any internal service is accessible via the proxy.

Location

  • services/api/app/main.py:1476-1891 — All proxy_webview_* endpoints

Current Implementation

The port parameter is an arbitrary integer — the API will proxy requests to localhost:{port} inside the container, regardless of what service is there. It simply does:

cmd = f"curl -sS -D - -o - --path-as-is 'http://localhost:{port}{full_path}'"
exit_code, output = container.exec_run(["bash", "-lc", cmd], workdir="/workspace")

Risk

  • Users can scan for internal services running on any port inside their container
  • If malicious code is running in the container, it can be accessed via the proxy
  • Database ports (5432, 6379), SSH (22), or other services become accessible via the API
  • No rate limiting on internal proxied requests

Proposed Fix

  1. Maintain a whitelist of allowed ports for webview (3000, 5173, 8080, 8765, etc.)
  2. Restrict to ports that are explicitly exposed in the container definition
  3. Add authentication check at the Docker level, not just the API level

Priority: Medium
Type: Security

## Issue The webview proxy endpoints (`/sessions/{id}/webview/{port}`) allow proxying HTTP requests to ANY port inside a session container. There is no port whitelist or restriction, meaning any internal service is accessible via the proxy. ## Location - `services/api/app/main.py:1476-1891` — All proxy_webview_* endpoints ## Current Implementation The `port` parameter is an arbitrary integer — the API will proxy requests to `localhost:{port}` inside the container, regardless of what service is there. It simply does: ```python cmd = f"curl -sS -D - -o - --path-as-is 'http://localhost:{port}{full_path}'" exit_code, output = container.exec_run(["bash", "-lc", cmd], workdir="/workspace") ``` ## Risk - Users can scan for internal services running on any port inside their container - If malicious code is running in the container, it can be accessed via the proxy - Database ports (5432, 6379), SSH (22), or other services become accessible via the API - No rate limiting on internal proxied requests ## Proposed Fix 1. Maintain a whitelist of allowed ports for webview (3000, 5173, 8080, 8765, etc.) 2. Restrict to ports that are explicitly exposed in the container definition 3. Add authentication check at the Docker level, not just the API level **Priority:** Medium **Type:** Security
Author
Owner

GSD VERIFY phase failed after 3 attempts.
═══ Gate: verify-passed (devforge#31) ═══
Mode: Tier 1 (functional only)

GATE OK: All gates up to 'implement' pass
── Tier 1: Functional ────────────────────────────────
Running tests...
❌ Tests failed (exit 1)
 FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > decodeTerminalMessage > should return empty Uint8Array for Blob input in sync decoder
 FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should return false when debug is not enabled
 FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should return true when debug is enabled
 FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should disable debug mode
ReferenceError: localStorage is not defined
 ❯ services/web/src/utils/terminalUtils.test.ts:12:5
 10|  beforeEach(() => {
 11|  vi.restoreAllMocks();
 12|  localStorage.clear();
 |  ^
 13|  });
 14| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/9]⎯

 Test Files  4 failed | 1 passed (5)
 Tests  6 failed | 4 passed (10)
 Start at  19:43:13
 Duration  715ms (transform 508ms, setup 0ms, import 141ms, tests 12ms, environment 1ms)
Running linter...
✅ Lint clean
⚠️ No type checker discovered
Previous test count: unknown

❌ GATE FAIL: verify-passed
- Tests failed with exit code 1
GATE FAIL: verify — blocked at 2026-05-21T02:43:15Z
Class: test-failure
Evidence: Verification failed: 1 failures
Git tag: devforge#31-gate-verify-fail-01e7293e8dbd (use gate-retry after fixing)

GSD VERIFY phase failed after 3 attempts. ═══ Gate: verify-passed (devforge#31) ═══ Mode: Tier 1 (functional only) GATE OK: All gates up to 'implement' pass ── Tier 1: Functional ──────────────────────────────── Running tests... ❌ Tests failed (exit 1)  FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > decodeTerminalMessage > should return empty Uint8Array for Blob input in sync decoder  FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should return false when debug is not enabled  FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should return true when debug is enabled  FAIL  services/web/src/utils/terminalUtils.test.ts > terminalUtils > debug mode > should disable debug mode ReferenceError: localStorage is not defined  ❯ services/web/src/utils/terminalUtils.test.ts:12:5  10|  beforeEach(() => {  11|  vi.restoreAllMocks();  12|  localStorage.clear();  |  ^  13|  });  14|  ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/9]⎯  Test Files  4 failed | 1 passed (5)  Tests  6 failed | 4 passed (10)  Start at  19:43:13  Duration  715ms (transform 508ms, setup 0ms, import 141ms, tests 12ms, environment 1ms) Running linter... ✅ Lint clean ⚠️ No type checker discovered Previous test count: unknown ❌ GATE FAIL: verify-passed - Tests failed with exit code 1 GATE FAIL: verify — blocked at 2026-05-21T02:43:15Z Class: test-failure Evidence: Verification failed: 1 failures Git tag: devforge#31-gate-verify-fail-01e7293e8dbd (use gate-retry after fixing)
Author
Owner

GSD PLAN: response missing GATE: line.

GSD PLAN: response missing GATE: line.
Mark closed this issue 2026-06-09 01:15:17 +00:00
Sign in to join this conversation.
No description provided.