Skip to content

Enforce localhost-only binding: refuse HOST=0.0.0.0 without an explicit opt-in #1795

Description

@cliffhall

Problem

The web server (both the prod backend clients/web/server/web-server-config.ts and the dev Vite server clients/web/vite.config.ts) passes the HOST env var straight through to its bind call with no validation. Setting HOST=0.0.0.0 (or ::, or empty) binds every network interface, exposing the Inspector's backend — which can spawn local processes and connect to MCP servers on the user's behalf — to the entire local network. That is precisely the surface DNS-rebinding attacks target.

The project's stated intent (specification/v2_scope.md: "Bind to localhost by default … never 0.0.0.0") was only a design note; nothing enforced it.

Proposal

Enshrine the localhost-only default in code, via a single shared guard used by both bind points:

  • Refuse an all-interfaces host (0.0.0.0 / :: / empty) by default, exiting with an actionable error.
  • Allow it only when DANGEROUSLY_BIND_ALL_INTERFACES=true is explicitly set (same DANGEROUSLY_* opt-in pattern as DANGEROUSLY_OMIT_AUTH).
  • The published Docker image is the sanctioned exception (a container must bind 0.0.0.0 to be reachable through -p), so its Dockerfile sets the flag.

Related fix folded into the same PR

Investigation started from a real bug: connecting to a plain stdio server (everything-server) failed with Remote connect failed (403): Invalid origin … DNS rebinding. Root cause: the dev server binds IPv6 loopback [::1], the browser lands on http://[::1]:6274, but the default allowedOrigins was the single literal string http://localhost:6274, so the origin guard 403'd the connect. The same PR expands the default loopback allowedOrigins to all three interchangeable forms (localhost, 127.0.0.1, [::1]) for the port.

Metadata

Metadata

Assignees

Labels

v2Issues and PRs for v2

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions