Phase 8
This commit is contained in:
82 files changed
+4903
-287
No files matched your search
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { safeRedirectPath } from "../safe-redirect";
|
||||
|
||||
describe("safeRedirectPath", () => {
|
||||
it("accepts a plain internal path", () => {
|
||||
expect(safeRedirectPath("/book/details", "/home")).toBe("/book/details");
|
||||
});
|
||||
|
||||
it("falls back for null/undefined/empty input", () => {
|
||||
expect(safeRedirectPath(null, "/home")).toBe("/home");
|
||||
expect(safeRedirectPath(undefined, "/home")).toBe("/home");
|
||||
expect(safeRedirectPath("", "/home")).toBe("/home");
|
||||
});
|
||||
|
||||
it("rejects protocol-relative URLs", () => {
|
||||
expect(safeRedirectPath("//evil.com", "/home")).toBe("/home");
|
||||
});
|
||||
|
||||
it("rejects absolute URLs to another host", () => {
|
||||
expect(safeRedirectPath("https://evil.com/phish", "/home")).toBe("/home");
|
||||
expect(safeRedirectPath("http://evil.com", "/home")).toBe("/home");
|
||||
});
|
||||
|
||||
it("rejects a path not starting with a single slash", () => {
|
||||
expect(safeRedirectPath("book/details", "/home")).toBe("/home");
|
||||
expect(safeRedirectPath("javascript:alert(1)", "/home")).toBe("/home");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Validates a caller-supplied return path (a `callbackUrl` query param,
|
||||
* a booking-draft continuation target) is a safe, internal, relative
|
||||
* path before it's ever used in a redirect — see the brief's "Accept
|
||||
* only safe internal return paths. Prevent open redirects." Auth.js's
|
||||
* own `signIn()`/`redirectTo` already restricts to same-origin
|
||||
* internally, but this is checked explicitly rather than relied on
|
||||
* implicitly, since it guards a value that reached this code from a URL
|
||||
* query string, not from a trusted internal source.
|
||||
*/
|
||||
export function safeRedirectPath(path: string | null | undefined, fallback: string): string {
|
||||
if (!path) return fallback;
|
||||
// Must start with exactly one "/" — rejects "//evil.com" (protocol-relative),
|
||||
// "https://evil.com", and "javascript:..." in one check.
|
||||
if (!path.startsWith("/") || path.startsWith("//")) return fallback;
|
||||
if (path.includes("://")) return fallback;
|
||||
return path;
|
||||
}
|
||||
Reference in new issue
Block a user