fix(authz): restrict tunnel process routes locally (#11531)

Validated in a combined 10-PR batch worktree off release/v3.8.51 tip.
- Focused tests: tests/unit/authz/route-guard-tunnel-processes-local-only.test.ts + tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts — 6+ pass
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity gates — all OK
- Full-repo lint: 503 pre-existing problems confirmed identical on the pure release/v3.8.51 tip — unrelated to this diff

⚠️ base-red inherited: #11449

Thanks for closing the process-spawning tunnel routes to local-only (Hard Rule #15/#17 territory) while preserving authenticated remote read access to status endpoints.
This commit is contained in:
Paco Cartones
2026-08-25 18:12:34 +02:00
committed by GitHub
parent 30026b2e96
commit e48ffd38d3
5 changed files with 77 additions and 2 deletions

View File

@@ -40,6 +40,12 @@ export const LOCAL_ONLY_API_PREFIXES: ReadonlyArray<string> = [
"/api/cli-tools/jcode-settings", // spawns via getCliRuntimeStatus() to detect the `jcode` CLI install (Hard Rules #15 + #17, #7263)
"/api/cli-tools/qwen-settings", // GET probes the local `qwen` binary; writes target ~/.qwen config files (Hard Rules #15 + #17)
"/api/services/", // T-10: embedded service lifecycle (spawn child processes)
"/api/tunnels/cloudflared", // POST installs/starts/stops cloudflared; safe methods are exempted below
"/api/tunnels/tailscale/disable", // stops Funnel and may stop tailscaled/Tailscale service
"/api/tunnels/tailscale/enable", // starts tailscaled/login/funnel subprocesses
"/api/tunnels/tailscale/install", // downloads/installs Tailscale and starts its daemon
"/api/tunnels/tailscale/login", // spawns `tailscale up`
"/api/tunnels/tailscale/start-daemon", // starts tailscaled/Tailscale service
"/dashboard/providers/services/", // T-07: reverse proxy to embedded service UIs
"/api/copilot/", // unauthenticated LLM driver — CLI-only by default; admins can opt-in to remote access via manage-scope bypass
"/api/tools/agent-bridge/", // AgentBridge: spawns MITM server + DNS edits (Hard Rules #15 + #17)
@@ -213,7 +219,10 @@ export function isPrivateLanHost(hostHeader: string | null): boolean {
* triggers the auto-update flow (spawns git checkout + npm install + pm2).
* Hard Rules #15/#17 still apply to POST.
*/
export const LOCAL_ONLY_API_GET_EXEMPTIONS: ReadonlySet<string> = new Set(["/api/system/version"]);
export const LOCAL_ONLY_API_GET_EXEMPTIONS: ReadonlySet<string> = new Set([
"/api/system/version",
"/api/tunnels/cloudflared",
]);
/** Safe HTTP methods that can be exempted for read-only paths. */
const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);

View File

@@ -27,6 +27,12 @@ export const SPAWN_CAPABLE_PREFIXES: ReadonlyArray<string> = [
"/api/cli-tools/runtime/",
"/api/cli-tools/qwen-settings", // GET probes the Qwen Code binary; the route also mutates local ~/.qwen files
"/api/services/", // T-10: can run npm install + spawn node processes
"/api/tunnels/cloudflared", // POST installs/starts/stops cloudflared; safe methods remain read-only exempt
"/api/tunnels/tailscale/disable", // stops Funnel and may stop tailscaled/Tailscale service
"/api/tunnels/tailscale/enable", // starts tailscaled/login/funnel subprocesses
"/api/tunnels/tailscale/install", // downloads/installs Tailscale and starts its daemon
"/api/tunnels/tailscale/login", // spawns `tailscale up`
"/api/tunnels/tailscale/start-daemon", // starts tailscaled/Tailscale service
"/api/tools/agent-bridge/", // start/stop MITM server + DNS edits (Hard Rules #15 + #17)
"/api/settings/mitm", // installs a system trusted root CA + /etc/hosts DNS overrides via src/mitm/* — must never be whitelistable via manage-scope bypass (GHSA-x7vm-hp44-9p79, Hard Rules #15 + #17)
"/api/cli-tools/antigravity-mitm", // same privileged CA-trust + DNS surface as /api/settings/mitm (GHSA-x7vm-hp44-9p79, Hard Rules #15 + #17)