fix(coderd): harden oauth2 redirect validation (#27274)

Closes DEVEX-604

Hardens `redirect` URL handling in the OAuth2/OIDC/external-auth
callback flows so redirects are always reduced to a safe, relative path
local to the application. Previously a redirect value with an opaque
scheme (e.g. `javascript:...`) or a path with multiple leading slashes
(e.g. `///evil.com`) could survive sanitization mostly intact.

Also de-duplicates the previously copy-pasted `uriFromURL` helper (now
exported `httpmw.URIFromURL`) so there's a single implementation shared
by `coderd/userauth.go`, `coderd/externalauth.go`, and
`coderd/httpmw/oauth2.go`.

<details>
<summary>Context</summary>

Addresses a low-severity finding reported via a pentest disclosure: the
redirect sanitizer used `url.Parse(...).RequestURI()`, which doesn't
reject non-hierarchical (opaque) URLs and doesn't collapse extra leading
slashes, so crafted `redirect` values could partially survive
sanitization.

</details>

This PR was authored by a Coder Agent on behalf of @aslilac.
This commit is contained in:
McKayla はな
2026-07-23 11:56:07 -06:00
committed by GitHub
parent 10624122c5
commit 2f879910af
5 changed files with 83 additions and 23 deletions
+2 -2
View File
@@ -1140,7 +1140,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
http.SetCookie(rw, cookie)
}
redirect = uriFromURL(redirect)
redirect = httpapi.SafeRedirectPath(redirect)
if api.GithubOAuth2Config.DeviceFlowEnabled {
// In the device flow, the redirect is handled client-side.
httpapi.Write(ctx, rw, http.StatusOK, codersdk.OAuth2DeviceFlowCallbackResponse{
@@ -1574,7 +1574,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
redirect := state.Redirect
// Strip the host if it exists on the URL to prevent
// any nefarious redirects.
redirect = uriFromURL(redirect)
redirect = httpapi.SafeRedirectPath(redirect)
http.Redirect(rw, r, redirect, http.StatusTemporaryRedirect)
}