Skip to content

PyStarter v1.0.7 — security hardening (please upgrade)

Choose a tag to compare

@E-Conners-Lab E-Conners-Lab released this 27 Sep 15:55

A security review of 1.0.6 found real problems in how student code was executed. Upgrade if you are running any earlier version, and read SECURITY.md.

What was wrong in 1.0.6 and earlier

  • Student code ran inside the Django process. The Python restrictions can be escaped — submitted code reaches real builtins through ordinary object attributes — and in-process that meant an escape reached the application database connection, the Anthropic API key, the Django secret key, and Django internals.
  • The execution deadline did not stop anything. A 1-second limit returned a timeout status only after the code ran to completion (measured: 9.2s). An infinite loop occupied a worker.
  • The container ran as root, so an escape inside Docker was root inside the container.
  • The stack published on every network interface with TLS, secure cookies, and HSTS off.
  • Tokens lived in browser-readable storage and logout never revoked them server-side.
  • Password validators were configured but never invoked — password1 was an acceptable password.
  • AI critique was ungated and puts the exercise solution in the prompt, so it could be steered into revealing answers for exercises the caller had not solved.

Fixed in 1.0.7

Area Change
Execution Separate process, scrubbed env (no keys, no DB password), memory limits, deadline that kills the job
Container Non-root user, uid 10001
Exposure Binds 127.0.0.1 by default; set NGINX_BIND to widen deliberately
Auth httpOnly cookies, refresh token blacklisted on logout, default-deny permissions
Passwords Django validators enforced on registration and reset
AI Critique requires a passing submission by the requesting user

Verified in a clean install: an escaped payload now sees uid 10001 and four environment variables (HOME, LANG, LC_CTYPE, PATH) — no secret key, no database password.

The honest caveat

This is not a hard security boundary. Submitted Python can still escape the language-level restrictions; what changed is what an escape can reach. There is no seccomp profile, user namespace, or network isolation. PyStarter is for local use with people you trust. For untrusted users, put real isolation under the executor (gVisor, Firecracker, nsjail) — see SECURITY.md.

Upgrading

sed -E -i.bak "s/pystarter-(backend|frontend):[0-9.]+/pystarter-\1:1.0.7/" docker-compose.yml
docker compose pull
docker compose up -d

Your progress in the pgdata volume is untouched. If you previously set NGINX_PORT to reach PyStarter from another machine, note the default is now loopback-only — set NGINX_BIND consciously, and read SECURITY.md first. Existing sessions are invalidated by the move to cookie auth; log in again.

Images

  • ghcr.io/e-conners-lab/pystarter-backend:1.0.7 (linux/amd64, linux/arm64)
  • ghcr.io/e-conners-lab/pystarter-frontend:1.0.7 (linux/amd64, linux/arm64)
  • :latest now points at 1.0.7