← Bug of the week

Bug of the week · 2026-10-19

The redirect check that trusts a leading slash

The post-login redirect in an Express API · Security · difficulty 1/3

After a successful login, the API redirects the user back to the page they originally asked for. The target comes from a `?next=` query parameter, so it is user-controlled. A teammate asked the AI assistant: "Write `resolveRedirect(target, fallback)` for our login endpoint. Only allow redirects to pages on our own site; anything else should go to the fallback." The code came back with tests that pass. The contract promised by the docstring: - `resolveRedirect(target, fallback)` returns `target` when it is a local path on this site, and `fallback` otherwise. - A missing, empty or whitespace-only `target` falls back. - Absolute URLs to other hosts (`https://evil.example`) must never be returned. - `isLocalPath(url)` is the predicate behind it: `true` only for paths that the browser will resolve on the current origin. Keep in mind how a browser interprets a `Location` header: it does not only look for `http://`. Review the code before it ships.
No account needed. No hints. The clock is running.0 s

Click the line (or lines) where the defect is, pick its category, then submit. The code is read-only in this mode.

1/**
2 * Resolves the post-login redirect target.
3 *
4 * Returns `target` when it points to a page on this site, otherwise
5 * `fallback`. Used by the login endpoint to honour `?next=`.
6 */
7export function resolveRedirect(
8 target: string | null | undefined,
9 fallback = "/dashboard",
10): string {
11 if (!target) return fallback;
12 const trimmed = target.trim();
13 if (trimmed.length === 0) return fallback;
14 if (!isLocalPath(trimmed)) return fallback;
15 return trimmed;
16}
17
18/**
19 * True when `url` is a path on the current origin, not a link to another host.
20 */
21export function isLocalPath(url: string): boolean {
22 const lower = url.toLowerCase();
23 if (lower.startsWith("http://") || lower.startsWith("https://")) return false;
24 if (lower.includes("://")) return false;
25 return url.startsWith("/");
26}
0 lines marked