Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/server/management/native-integration-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ export async function handleNativeIntegrationRoutes(ctx: ManagementContext): Pro
rethrowManagementBodyTooLarge(error);
return jsonResponse({ error: "invalid JSON body" }, 400);
}
if (typeof body.enabled !== "boolean") {
if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.enabled !== "boolean") {
return jsonResponse({ error: "enabled must be a boolean" }, 400);
}

Expand Down
11 changes: 11 additions & 0 deletions tests/native-claude-code-toggle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ test("a non-boolean enabled is rejected", async () => {
expect(res!.status).toBe(400);
});

test("a null body is rejected instead of crashing the route", async () => {
const { response } = dispatch(baseConfig(), "/api/native-integrations/claude", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: "null",
});
const res = await response;
expect(res!.status).toBe(400);
expect(await res!.json()).toEqual({ error: "enabled must be a boolean" });
});

test("genuine lock contention refuses 409 config_busy, a broken lock is a 500", async () => {
/*
* `ConfigMutationLockError` wraps EVERY acquisition failure behind one
Expand Down
Loading