fix(admin): security-audit findings — paginate chapters, lock down unchecked helper (0.41.2)
Addresses the security-audit findings on top of the admin feature stack: M1: /admin/mangas/:id/chapters now paginates (default limit 200, max 500). A long-runner with thousands of chapters would otherwise produce a multi-MB response with that many scalar subqueries per row — admin-only but a real stall risk on one expand-click. Adds explicit pagination tests for the cap and offset; frontend renders a "Showing first N of M" hint when the cap clips the result. L1: repo::user::set_is_admin renamed to set_is_admin_unchecked with a doc-comment pointing at admin_safe_set_is_admin for production use. The short name was a footgun — a future contributor reaching for it would silently bypass self-protection, the last-admin invariant, and the audit log. Used only by integration-test setup; production code goes through the admin_safe_* paths. CSRF posture: build_session_cookie carries a comment that the SameSite=Lax default is the project's CSRF defense for state-changing mutations and breaks the instant anyone adds a side-effecting GET under /admin/*. Spells out what to do then (Strict + explicit token check). Test counts: 43 backend admin tests + 12 vitest admin tests all green; svelte-check 0/0 across 446 files.
This commit is contained in:
@@ -309,6 +309,18 @@ async fn start_session(
|
||||
Ok(jar.add(build_session_cookie(raw, &state.auth)))
|
||||
}
|
||||
|
||||
// CSRF posture: `SameSite=Lax` is the project's primary CSRF defense.
|
||||
// Browsers refuse to attach this cookie to cross-site POST / PATCH /
|
||||
// DELETE requests, which covers every state-changing endpoint (auth
|
||||
// mutations, uploads, bookmarks, collections, admin user management,
|
||||
// etc. — all JSON over POST/PATCH/DELETE). Lax DOES still attach the
|
||||
// cookie on top-level cross-site GETs, so this defense breaks the
|
||||
// instant anyone adds a state-changing GET. If you reach for one,
|
||||
// switch to `SameSite=Strict` here AND add an explicit CSRF-token
|
||||
// check on the new endpoint. The Bearer-token branch in the
|
||||
// extractor is unaffected (bots authenticate with the token header,
|
||||
// not the cookie) and admin routes reject Bearer entirely — see
|
||||
// `auth::extractor::RequireAdmin`.
|
||||
fn build_session_cookie(raw: String, cfg: &AuthConfig) -> Cookie<'static> {
|
||||
let mut builder = Cookie::build((SESSION_COOKIE_NAME, raw))
|
||||
.http_only(true)
|
||||
|
||||
Reference in New Issue
Block a user