c7b9e1f98eb24b4043f59bf7d20d8b8e2ac60c55
C1: lesson HTML goes through a sanitize-html allowlist on the fully rendered output (no on* attributes, no style, no svg/math/iframe/object/embed/form; URLs limited to http(s), mailto, relative and anchors). Raw tags outside the allowlist are escaped as text. The SPA document is served with a CSP (script-src 'self', no inline script) through spaWithCsp in main.ts; contract request §5 asks core to own it. C2: exercises run in <iframe sandbox="allow-scripts"> (opaque origin, no cookies) loaded from /api/learner/runner-frame, whose CSP allows only its own script (by hash) and blob: workers, and connect-src 'none'. postMessage only, both sides check event.source; the timeout removes the frame. The worker stubs now also cover Worker, SharedWorker and caches. I1: learner visibility calls core canView with per-course authorship only, like /api/media (contract request §6). I2: Tab and Shift+Tab move focus; Enter keeps the previous line's indentation. Tests: XSS unit cases, headless-Chrome checks for the exploit lesson and for fetch, import, Cache.add, nested Worker, img, EventSource, WebSocket, XHR and sendBeacon from learner and test code (plus a negative control), visibility on both dialects, SPA and runner-frame CSP headers. Co-Authored-By: Claude Opus 5.5 <[email protected]>
STCourses
STCourses is a self-hosted course platform. Homelabbers, universities and companies host their own courses, plus material they already own such as Packt video packages or Pluralsight slide PDFs, in one place that tracks progress. Authors write in Markdown in a git repo or in a browser studio; both edit the same files. Each install has one owner and is not multi-tenant.
Development
bun install # install workspace dependencies
bun test # run all tests
bun run typecheck # tsc --noEmit
bun run dev # start the server with hot reload
License: AGPL-3.0-only.
Languages
TypeScript
97.8%
CSS
1.7%
Dockerfile
0.2%
Python
0.1%