Description
Summary
Neither server/Dockerfile nor web/Dockerfile sets a USER directive, so both final production images run their process as root by default.
Evidence
server/Dockerfile:10-17— final stage isFROM node:22-slim, noUSERinstruction anywhere in the file;CMD ["node", "dist/index.js"]therefore runs as root.web/Dockerfile:8-14— same pattern: final stageFROM node:22-slim, noUSERinstruction;CMD ["node", "server.js"]runs as root.- Confirmed via
grep -n "USER" server/Dockerfile web/Dockerfilereturning no matches.
Why this matters
Running application containers as root is unnecessary privilege that widens the blast radius of any future code-execution vulnerability (in this app, a dependency, or the Node runtime itself) — a compromised process running as root inside the container has more capability to interact with the container's filesystem/environment than one running as an unprivileged user, and any container-escape-class vulnerability becomes more severe.
Attack or failure scenario
If any future vulnerability (in this codebase or a dependency) allows arbitrary file write or command execution inside either container, the attacker's resulting process runs as root, rather than being constrained to a non-privileged account — a defense-in-depth gap rather than a standalone exploitable bug today.
Root cause
Neither Dockerfile creates or switches to a non-root user; the official node:22-slim base image's default user (root) is used unmodified in the final stage of both.
Recommended fix
Add a non-root user in each final stage, e.g.:
RUN groupadd -r app && useradd -r -g app app
USER app(or use the node user already present in the official Node.js images: USER node), placed after all COPY/RUN steps that need root (installing dependencies, copying build output) and before CMD. Verify the app can still read/write its required paths (e.g., DATA_DIR for the API) as the non-root user.
Acceptance criteria
docker inspect(ordocker exec ... whoami) on both running containers reports a non-root user.- The app continues to function normally (data directory read/write, port binding) under the new user.
Suggested labels
docker, security (neither exists on this repo; recommend bug)
Priority
P2
Severity
Medium — standard container-hardening gap; not directly exploitable on its own, but removes a meaningful layer of defense-in-depth.
Confidence
Confirmed — read both Dockerfiles in full; no USER directive present in either.
Activity