Validate config JSON body shape

This commit is contained in:
Dymas
2026-05-16 11:48:01 +02:00
parent 839154aa5c
commit 68fdc99bbd
6 changed files with 17 additions and 4 deletions
+5
View File
@@ -1,5 +1,10 @@
# Changelog
## 0.29.5 - 2026-05-16
- Fixed `/api/config` malformed-body handling so non-object JSON payloads now return `400 Bad Request` instead of silently resetting saved settings to defaults.
- Expanded regression coverage for invalid config payload shapes.
## 0.29.4 - 2026-05-16
- Fixed `/api/watchlist/add` malformed-body handling so non-object JSON payloads now return `400 Bad Request` instead of surfacing as an internal server error.
+2 -1
View File
@@ -2,7 +2,7 @@
A local web UI for a system-wide `ani-cli` install.
Current version: `0.29.4`
Current version: `0.29.5`
## Project Layout
@@ -60,6 +60,7 @@ The app currently includes:
- Episode loading for the selected result.
- Queueing downloads with editable library name, season, episode range, quality, and target folder.
- A dedicated Config page for saved default folder, mode, and quality settings.
- Malformed config save requests now return `400 Bad Request` when the JSON body is not an object, instead of resetting values back to defaults.
- A paged SQLite-backed download queue with retry, cancel, remove, retry-failed, and clear-finished actions.
- A paged SQLite-backed watchlist with summary cards, per-show refresh, bulk refresh, source links, and thumbnail tools.
- Duplicate watchlist adds now return `200 OK` with `created: false`, keeping the API status aligned with the no-op result.
+1 -1
View File
@@ -1 +1 @@
0.29.4
0.29.5
+1 -1
View File
@@ -16,7 +16,7 @@ from uuid import uuid4
PROJECT_ROOT = Path(__file__).resolve().parent
ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli"
APP_NAME = "ani-cli-web"
VERSION = "0.29.4"
VERSION = "0.29.5"
ALLANIME_BASE = "allanime.day"
ALLANIME_API = f"https://api.{ALLANIME_BASE}"
ALLANIME_REFERER = "https://allmanga.to"
+1 -1
View File
@@ -214,7 +214,7 @@ class Handler(BaseHTTPRequestHandler):
try:
self.ensure_client_access()
if parsed.path == "/api/config":
self.update_config(self.body_json())
Handler.update_config(self, Handler.require_json_object(self, self.body_json()))
elif parsed.path == "/api/queue":
runtime = Handler._runtime(self)
self.json(runtime["download_queue"].add(self.body_json()), HTTPStatus.CREATED)
+7
View File
@@ -1103,6 +1103,13 @@ class HandlerRouteTests(unittest.TestCase):
APP.Handler.body_json(handler)
self.assertIn("utf-8", str(ctx.exception).lower())
def test_config_post_rejects_non_object_json_body(self):
body = b"[]"
handler = DummyHandler("/api/config", body=body, content_length=len(body))
APP.Handler.do_POST(handler)
self.assertEqual(handler.error_status, HTTPStatus.BAD_REQUEST)
self.assertIn("json object", handler.error_message.lower())
def test_remove_post_missing_show_id_returns_bad_request(self):
handler = DummyHandler("/api/watchlist/remove", body=b"{}", content_length=2)
APP.Handler.do_POST(handler)