[]previous and next page

Redirects

A redirect target that comes from a request (?next=... after sign-in) is the classic open redirect.

For agentsUpdated

A redirect target that comes from a request (?next=... after sign-in) is the classic open redirect. Every redirect that leaves a page's beforeLoad or loader, an API route or a server function (a JSON call and a form post alike) passes checkRedirectTarget first, applied to the target as the code wrote it. Allowed: a path on this site (/dashboard), a relative path, and an absolute http(s) URL whose origin is the request's own or is listed in trustedOrigins. Refused: //host and /\host, backslashes and control characters, a scheme such as javascript:, user info (https://app.example@evil.test) and any other origin. A refused target is a server error, not a redirect: the handler reports RedirectBlockedError to onError, answers 500, and the target never reaches a Location header.

src/routes/api/go.tsts
import { redirect, type RequestEvent } from "@shadow-js/dusk/server";

/** Sends the browser to ?next=..., which is checked by the framework, not by this code. */
export const GET = (event: RequestEvent): never => {
  throw redirect(event.url.searchParams.get("next") ?? "/", { status: 303 });
};

A page redirects the same way, from beforeLoad or a loader. The router would turn //evil.example into the local path /evil.example and hide the attempt, so the page path checks the target as written, like the API route above.

src/routes/leave.tsxtsx
import { redirect } from "@shadow-js/dusk";

/** Sends the visitor to ?next=...; the framework checks the target. */
export const beforeLoad = ({ location }: { location: { search: string } }): never => {
  throw redirect(new URLSearchParams(location.search).get("next") ?? "/", { status: 303 });
};