Skip to content

Default SimulationServer to loopback-only, add test coverage - #284

Open
jaisinha77777 wants to merge 1 commit into
google-deepmind:mainfrom
jaisinha77777:fix-simulation-server-bind-all-interfaces
Open

Default SimulationServer to loopback-only, add test coverage#284
jaisinha77777 wants to merge 1 commit into
google-deepmind:mainfrom
jaisinha77777:fix-simulation-server-bind-all-interfaces

Conversation

@jaisinha77777

Copy link
Copy Markdown

Summary

`concordia/utils/simulation_server.py` (a debug/visualization HTTP server for stepping through a running simulation) starts with:

self._server = socketserver.ThreadingTCPServer(('', self._port), handler)

An empty host string binds to all network interfaces, not just loopback. That's a real exposure here specifically because every endpoint is unauthenticated:

  • `POST /cmd/set_component_state` overwrites arbitrary component state on any entity in the running simulation (`server.simulation.set_component_dynamic_state(...)` with attacker-controlled `entity_name`/`component_name`/`key`/`value`).
  • `/play`, `/pause`, `/step`, `/stop` give full remote control over simulation execution.
  • Every JSON response is sent with `Access-Control-Allow-Origin: *`.

None of that is gated by any credential or token. Binding to `0.0.0.0` by default means anyone else on the same network segment (same WiFi, same cloud VPC, same container network) can reach all of this, not just the person running the simulation on their own machine.

(Side note: I found this while adding test coverage — this module currently has zero test files, part of the ongoing #205 effort. It also isn't imported anywhere else in the repo, so nothing currently instantiates it with the old default; this is a library-level fix to what it does when someone does use it.)

Fix

Adds a `host` constructor parameter, defaulting to `'127.0.0.1'` (loopback-only) — matching the secure-by-default convention used by comparable local dev servers (Jupyter, TensorBoard, Flask's/Django's dev servers). Anyone who actually wants LAN/remote access can still opt in explicitly with `host='0.0.0.0'`.

Also added `host`, `port`, and `bound_port` properties (the last exposes the OS-assigned port when starting with `port=0`, used by the new tests).

Test plan

New `concordia/utils/simulation_server_test.py` (previously no coverage at all):

  • Default host is `127.0.0.1`; `host` is configurable; `port`/`bound_port` properties behave as documented.
  • `broadcast_step`/`broadcast_entity_info` behavior, including that a full SSE queue is dropped rather than blocking or raising.
  • End-to-end HTTP tests against a live server started on an OS-assigned port (`port=0`): `/`, `/status`, `/play`, `/pause`, both the POST and GET-based `/cmd/*` endpoints, 404 handling, and `/cmd/set_component_state` (paused-required, simulation-required, success path, missing-field error path).

`python -m pytest concordia/utils/simulation_server_test.py -q` → 18 passed.
`python -m pyink --check` on both changed/new files → clean.

SimulationServer binds with socketserver.ThreadingTCPServer(('', port),
...), where an empty host string means "all interfaces" -- so by
default this server is reachable from any other machine on the
network, not just the local one running the simulation.

That matters because every endpoint is unauthenticated. In particular,
POST /cmd/set_component_state lets a caller overwrite arbitrary
component state on any entity in the running simulation, and
/play, /pause, /step, /stop give full remote control over execution --
all with Access-Control-Allow-Origin: * and no credential check.
Binding to all interfaces by default means anyone else on the same
network segment can reach these.

Adds a host constructor parameter, defaulting to '127.0.0.1'
(loopback-only), matching the secure-by-default convention used by
comparable local dev servers (Jupyter, TensorBoard, Flask's dev
server). Passing host='0.0.0.0' explicitly still allows opting into
LAN/remote access for anyone who actually wants that.

Also adds simulation_server_test.py, which had no coverage: default
host, host/port/bound_port properties, broadcast_step/
broadcast_entity_info behavior (including dropping full SSE queues),
and the actual HTTP endpoints exercised end-to-end against a live
server on an OS-assigned port.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant