fix: final-review wave (CSRF guard, fail-closed owner bootstrap, streamed zips, e2e visibility suite)
- Critical 1 (CSRF): core wrap refuses every POST/PUT/PATCH/DELETE whose Origin (Referer as fallback) is not publicUrl's origin or STC_ALLOWED_ORIGINS, and any body that is not application/json (multipart only on declared upload routes). Better Auth routes go through the same guard; only declared identity-provider callbacks (SAML ACS/SLO, OAuth callbacks) are exempt. Better Auth trustedOrigins use the same list. Session cookie stays Lax (Strict breaks OAuth/OIDC/SAML returns); documented. - Important 4 / learner minor 4: the first-owner claim and the role row are one transaction guarded by "no owner yet"; only a unique violation means learner; any other error fails the sign-up with 500 and removes the half-created user. Serialised per DB handle (Bun's SQLite connection). - Important 2 / learner minor 12: code zips stream entry by entry (large files stored in 256 KB chunks with a data descriptor and incremental CRC-32), so memory stays at one chunk; the cap is now 1 GiB (ZIP32). - Important 3 / learner minor 10: the e2e runs the production wiring (productFeatures order, AI drafts through the real sink) against a restricted course and asserts 404/401 for learner and anonymous on course, lesson, media, cover, code, zip, notes, progress, attempts, AI chat, library, search and AI status; a real Range request (206, 416); forged writes (foreign Origin, text/plain, no Origin) are 403 and the MCP test command never runs. - Minors 7-9: compose passes every documented variable; the health check uses STC_PORT; productFeatures checks the studio-before-AI order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
69aaedcb43
commit
8045c80d93
@@ -9,6 +9,13 @@ export interface Feature {
|
||||
jobs?: Record<string, JobHandler>; // job kind -> handler
|
||||
auth?: AuthService; // replaces the anonymous AuthService (the learner package supplies it)
|
||||
stop?(): void | Promise<void>; // called when the server stops (timers, watchers)
|
||||
/** Route keys (as in `routes`) whose writes take multipart/form-data instead of JSON (uploads). */
|
||||
multipartRoutes?: string[];
|
||||
/**
|
||||
* Path prefixes an outside identity provider posts to (SAML ACS/SLO, OAuth callbacks). Core's cross-site request
|
||||
* guard skips them; the handler must verify the request itself (signed assertion, OAuth state).
|
||||
*/
|
||||
externalCallbacks?: string[];
|
||||
}
|
||||
/** Features are built after the ServerContext exists. The job queue is built from the union of their `jobs`, so a
|
||||
* factory must not use `ctx.jobs` synchronously (only while a request or job runs). */
|
||||
@@ -48,4 +55,9 @@ export interface AppConfig {
|
||||
transcribeUrl: string | null;
|
||||
/** STC_PYTHON, default python3: Python with PyMuPDF, used for PDF page images and text. */
|
||||
python: string;
|
||||
/**
|
||||
* Origins allowed to send state-changing requests: the origin of `publicUrl` plus STC_ALLOWED_ORIGINS
|
||||
* (comma-separated; STC_TRUSTED_ORIGINS is read as an alias). Core refuses writes from any other origin.
|
||||
*/
|
||||
allowedOrigins: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user