Skip to content

Commit 1235a6f

Browse files
committed
fix(webapp): tighten /admin POST validation to stop impersonate fall-through
1 parent 97ad9a5 commit 1235a6f

1 file changed

Lines changed: 5 additions & 7 deletions

File tree

apps/webapp/app/routes/admin._index.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
4949
return typedjson({ ...result, justDeleted });
5050
};
5151

52-
const ImpersonateSchema = z.object({ id: z.string() });
52+
const ImpersonateSchema = z.object({ action: z.literal("impersonate"), id: z.string() });
5353
const DeleteSchema = z.object({ intent: z.literal("delete"), id: z.string() });
5454

5555
export async function action({ request }: ActionFunctionArgs) {
@@ -92,14 +92,12 @@ export async function action({ request }: ActionFunctionArgs) {
9292
return redirect("/admin?deleted=1");
9393
}
9494

95-
// Reject any POST that set `intent` to something we don't recognise so
96-
// unknown intents don't fall through to the impersonate flow.
97-
if (typeof payload.intent === "string") {
98-
return typedjson({ error: "Unknown action." }, { status: 400 });
95+
const impersonateAttempt = ImpersonateSchema.safeParse(payload);
96+
if (impersonateAttempt.success) {
97+
return redirectWithImpersonation(request, impersonateAttempt.data.id, "/");
9998
}
10099

101-
const { id } = ImpersonateSchema.parse(payload);
102-
return redirectWithImpersonation(request, id, "/");
100+
return typedjson({ error: "Unknown action." }, { status: 400 });
103101
}
104102

105103
export default function AdminDashboardRoute() {

0 commit comments

Comments
 (0)