mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-07 16:19:09 +00:00
fix: reject protocol-relative path from sanitizeAuthRedirect (open redirect)
sanitizeAuthRedirect returned parsed.pathname+search+hash, which `new URL` can resolve to a protocol-relative `//host` (e.g. input `/..//evil.com` normalizes same-origin, passing the origin check, but yields a path starting with `//`). ClientRedirect / login.tsx feed that path to window.location.replace, so it navigates cross-origin — an open redirect. Reject any resolved path that is not a single-leading-slash path (`^/[^/]`), falling back to the default. Adds coverage for `/..//evil.com`, `/.//evil.com`, and `//evil.com`.
This commit is contained in:
parent
179867d120
commit
3daddcd14e
2 changed files with 14 additions and 1 deletions
|
|
@ -50,7 +50,12 @@ export function sanitizeAuthRedirect(
|
|||
}
|
||||
|
||||
const path = `${parsed.pathname}${parsed.search}${parsed.hash}`
|
||||
if (!path.startsWith("/") || isBlockedRedirectPath(path)) return fallback
|
||||
// Reject anything that is not a single-leading-slash path. `new URL` can resolve inputs
|
||||
// like `/..//evil.com` to a protocol-relative `//evil.com` that still matches the origin
|
||||
// check above but, when handed to `window.location.replace`, navigates cross-origin.
|
||||
if (path.startsWith("//") || !/^\/[^/]/.test(path) || isBlockedRedirectPath(path)) {
|
||||
return fallback
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,14 @@ describe("auth redirect helpers", () => {
|
|||
)
|
||||
})
|
||||
|
||||
it("rejects inputs that resolve to a protocol-relative path", () => {
|
||||
// These resolve same-origin (passing the origin check) but yield a `//host`
|
||||
// path that would navigate cross-origin via window.location.replace.
|
||||
expect(sanitizeAuthRedirect("/..//evil.com")).toBe(DEFAULT_AUTH_REDIRECT)
|
||||
expect(sanitizeAuthRedirect("/.//evil.com")).toBe(DEFAULT_AUTH_REDIRECT)
|
||||
expect(sanitizeAuthRedirect("//evil.com")).toBe(DEFAULT_AUTH_REDIRECT)
|
||||
})
|
||||
|
||||
it("builds a plan sign-in target for the current plan URL", () => {
|
||||
window.history.pushState({}, "", "/agents/thread-1/plan?from=slack")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue