bugfix: gate manga PATCH and cover endpoints on uploader (0.34.1)
PATCH /mangas/:id, PUT /mangas/:id/cover and DELETE /mangas/:id/cover took the current user but never compared it against the row's uploaded_by. Any signed-in user could overwrite or clear any manga's metadata and cover. Add require_can_edit gate: non-NULL uploaded_by must match the caller; legacy NULL rows stay open until an admin role lands (per migration 0011 historical-data note). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -410,3 +410,53 @@ async fn delete_cover_404_on_unknown_id(pool: PgPool) {
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
/// Authz: PUT /mangas/:id/cover must be uploader-only.
|
||||
#[sqlx::test(migrations = "./migrations")]
|
||||
async fn put_cover_forbidden_for_non_uploader(pool: PgPool) {
|
||||
let h = harness(pool);
|
||||
let (_, owner_cookie) = register_user(&h.app).await;
|
||||
let (_, intruder_cookie) = register_user(&h.app).await;
|
||||
|
||||
let manga =
|
||||
create_manga_with_cover(&h.app, &owner_cookie, "Mine", None).await;
|
||||
let id = id_of(&manga);
|
||||
|
||||
let resp = h
|
||||
.app
|
||||
.oneshot(put_multipart_with_cookie(
|
||||
&format!("/api/v1/mangas/{id}/cover"),
|
||||
cover_form(&fake_png_bytes()),
|
||||
&intruder_cookie,
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
/// Authz: DELETE /mangas/:id/cover must be uploader-only.
|
||||
#[sqlx::test(migrations = "./migrations")]
|
||||
async fn delete_cover_forbidden_for_non_uploader(pool: PgPool) {
|
||||
let h = harness(pool);
|
||||
let (_, owner_cookie) = register_user(&h.app).await;
|
||||
let (_, intruder_cookie) = register_user(&h.app).await;
|
||||
|
||||
let manga = create_manga_with_cover(
|
||||
&h.app,
|
||||
&owner_cookie,
|
||||
"Mine",
|
||||
Some(("image/jpeg", &fake_jpeg_bytes())),
|
||||
)
|
||||
.await;
|
||||
let id = id_of(&manga);
|
||||
|
||||
let resp = h
|
||||
.app
|
||||
.oneshot(delete_with_cookie(
|
||||
&format!("/api/v1/mangas/{id}/cover"),
|
||||
&intruder_cookie,
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user