Severity
High
Summary
None of the 7 Python service images set a USER, so every backend/parser/worker container runs as root (UID 0). These containers receive a runtime bind mount of the user's home directory, making a root-in-container process with the user's files mounted a meaningful local privilege/escape surface for a local-first product.
Location
backend/Dockerfile (no USER)
mcap_parser/Dockerfile (no USER)
mcp_workers/anomaly_detector/Dockerfile, mcp_workers/planner_failure_inspector/Dockerfile, mcp_workers/report_composer/Dockerfile, mcp_workers/rosbag_reader/Dockerfile, mcp_workers/trajectory_analyzer/Dockerfile (no USER)
docker-compose.yml:59,82,106,127,169 (DATAPILOT_HOST_MOUNT: /host — host home bind-mounted at runtime by the orchestrator)
Details
All seven images default to UID 0 because no unprivileged user is created or selected. At runtime the Electron orchestrator binds the user's home directory into these containers at /host (comments at docker-compose.yml:63-65,83,108 describe this). A root process with the user's home directory mounted broadens the blast radius of any container compromise or parser bug, and is unnecessary: these services only need to read bag files and serve HTTP/stdio.
Suggested fix
Create an unprivileged user in each image, chown /app to it, and add a USER directive so the services run non-root. Ensure the /host bind mount is read-only for every consumer (the compose comments claim read-only for several services, but the mount is injected at runtime — verify the orchestrator actually sets :ro).
Project-rule reference
AGENT.md ## Security & Privacy Guardrails (line 306) and local-first privacy requirements (line 308).
Severity
High
Summary
None of the 7 Python service images set a
USER, so every backend/parser/worker container runs as root (UID 0). These containers receive a runtime bind mount of the user's home directory, making a root-in-container process with the user's files mounted a meaningful local privilege/escape surface for a local-first product.Location
backend/Dockerfile(noUSER)mcap_parser/Dockerfile(noUSER)mcp_workers/anomaly_detector/Dockerfile,mcp_workers/planner_failure_inspector/Dockerfile,mcp_workers/report_composer/Dockerfile,mcp_workers/rosbag_reader/Dockerfile,mcp_workers/trajectory_analyzer/Dockerfile(noUSER)docker-compose.yml:59,82,106,127,169(DATAPILOT_HOST_MOUNT: /host— host home bind-mounted at runtime by the orchestrator)Details
All seven images default to UID 0 because no unprivileged user is created or selected. At runtime the Electron orchestrator binds the user's home directory into these containers at
/host(comments atdocker-compose.yml:63-65,83,108describe this). A root process with the user's home directory mounted broadens the blast radius of any container compromise or parser bug, and is unnecessary: these services only need to read bag files and serve HTTP/stdio.Suggested fix
Create an unprivileged user in each image,
chown/appto it, and add aUSERdirective so the services run non-root. Ensure the/hostbind mount is read-only for every consumer (the compose comments claim read-only for several services, but the mount is injected at runtime — verify the orchestrator actually sets:ro).Project-rule reference
AGENT.md
## Security & Privacy Guardrails(line 306) and local-first privacy requirements (line 308).