fix(webui): reject malformed JSON body in system config PUT
An unparseable request body was silently converted to null, merged over defaults, and saved — wiping the admin's config while returning 200. Throw SystemConfigError on JSON parse failure so the route returns 400 INVALID_REQUEST without touching the saved config file. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { NO_STORE, routeErrorResponse } from "@/lib/server/routeErrors";
|
||||
import {
|
||||
getSystemConfig,
|
||||
saveSystemConfig,
|
||||
SystemConfigError,
|
||||
validateSystemConfig,
|
||||
} from "@/lib/server/systemConfig";
|
||||
|
||||
@@ -39,7 +40,12 @@ export async function PUT(request: NextRequest) {
|
||||
}
|
||||
actor = requireActor(request);
|
||||
requireAdmin(actor);
|
||||
const body = (await request.json().catch(() => null)) as unknown;
|
||||
let body: unknown;
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
throw new SystemConfigError("Invalid JSON body.");
|
||||
}
|
||||
const config = validateSystemConfig(body);
|
||||
saveSystemConfig(config);
|
||||
return NextResponse.json(config, { headers: NO_STORE });
|
||||
|
||||
@@ -73,6 +73,31 @@ describe("system config routes", () => {
|
||||
expect(await response.json()).toMatchObject({ code: "INVALID_REQUEST" });
|
||||
});
|
||||
|
||||
it("PUT rejects an unparseable JSON body with 400 and does not overwrite the saved config", async () => {
|
||||
const valid = await adminRoute.PUT(
|
||||
request("http://localhost/api/system/config", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ branding: { wordmark: "KeepMe" } }),
|
||||
}) as never
|
||||
);
|
||||
expect(valid.status).toBe(200);
|
||||
const before = fs.readFileSync(systemConfigPath(), "utf8");
|
||||
|
||||
const response = await adminRoute.PUT(
|
||||
request("http://localhost/api/system/config", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: "not-json{",
|
||||
}) as never
|
||||
);
|
||||
expect(response.status).toBe(400);
|
||||
expect(await response.json()).toMatchObject({ code: "INVALID_REQUEST" });
|
||||
|
||||
expect(fs.readFileSync(systemConfigPath(), "utf8")).toBe(before);
|
||||
expect(JSON.parse(before)).toMatchObject({ branding: { wordmark: "KeepMe" } });
|
||||
});
|
||||
|
||||
it("public route hides terms markdown when disabled", async () => {
|
||||
await adminRoute.PUT(
|
||||
request("http://localhost/api/system/config", {
|
||||
|
||||
Reference in New Issue
Block a user