The redirect check that trusts a leading slash
TypeScript · The post-login redirect in an Express API · Security · difficulty 1/3 · ~5 min
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)returnstargetwhen it is a local path on this site, andfallbackotherwise.- A missing, empty or whitespace-only
targetfalls back. - Absolute URLs to other hosts (
https://evil.example) must never be returned. isLocalPath(url)is the predicate behind it:trueonly 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.
Repair the code until the hidden tests pass.
code.ts
Visible tests (they pass on the code as given)
import { describe, it, expect } from "@debunk/harness";
import { resolveRedirect, isLocalPath } from "./code";
describe("resolveRedirect", () => {
it("keeps a local path", () => {
expect(resolveRedirect("/settings/profile?tab=security")).toBe("/settings/profile?tab=security");
});
it("falls back for an absolute URL to another host", () => {
expect(resolveRedirect("https://evil.example/login")).toBe("/dashboard");
expect(isLocalPath("http://evil.example")).toBe(false);
});
it("falls back when the target is missing", () => {
expect(resolveRedirect(null)).toBe("/dashboard");
expect(resolveRedirect(" ", "/home")).toBe("/home");
});
});