bugfix: security & correctness bundle (0.34.1)
Five fixes bundled into one release: - preserve user-attached tags across crawler upserts (repo::crawler::sync_tags now scopes to added_by IS NULL; orphaned attachments from deleted users are reaped as crawler-owned) - gate manga PATCH and cover endpoints on uploaded_by (require_can_edit in api::mangas; non-NULL uploaded_by must match the caller) - equalise login response time across user-existence branches (run argon2 against a OnceLock-cached dummy hash on the no-user branch so timing doesn't leak username existence) - crawler download defences (SSRF allowlist of host literals including IPv4-mapped IPv6 ranges, 32 MiB streamed size cap, reject non-whitelisted image types, three-way chapter-probe classifier replaces the binary #avatar_menu check) - tighten validation and clean up dead unload path (attach_tag + create_token enforce 64-char caps; LocalStorage rejects NUL bytes explicitly; reader flushFinalProgress drops the always-405 sendBeacon path) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -274,7 +274,20 @@ async fn sync_tags(
|
||||
manga_id: Uuid,
|
||||
tags: &[String],
|
||||
) -> sqlx::Result<()> {
|
||||
sqlx::query("DELETE FROM manga_tags WHERE manga_id = $1")
|
||||
// Only clear crawler-owned attachments (added_by IS NULL). User-
|
||||
// attached tags are owned by the attaching user and must survive
|
||||
// the recurring metadata pass — see manga_tags.added_by in
|
||||
// migration 0009.
|
||||
//
|
||||
// Note on orphans: `manga_tags.added_by` is `ON DELETE SET NULL`,
|
||||
// so an attachment whose user was deleted becomes
|
||||
// indistinguishable from a crawler-owned row and is cleaned up
|
||||
// here. That mirrors how `api::mangas::detach_tag` already treats
|
||||
// orphans ("nobody owns it, refuse to let anyone but admin clear
|
||||
// them") — the crawler now becomes the eventual reaper. Tracked
|
||||
// by `sync_tags_garbage_collects_orphan_user_attachments` in
|
||||
// backend/tests/crawler_sync.rs.
|
||||
sqlx::query("DELETE FROM manga_tags WHERE manga_id = $1 AND added_by IS NULL")
|
||||
.bind(manga_id)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user