Skip to content

fix: prevent open redirect in OIDC callback flow#61

Merged
overtrue merged 3 commits into
mainfrom
copilot/fix-security-advisory-issue
Feb 23, 2026
Merged

fix: prevent open redirect in OIDC callback flow#61
overtrue merged 3 commits into
mainfrom
copilot/fix-security-advisory-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 23, 2026

Pull Request

Description

The OIDC callback flow accepts a redirect parameter from the URL hash fragment without validation. An attacker can craft a callback URL with #...&redirect=https://evil.com to redirect users to a phishing site after SSO login.

Added isSafeRedirectPath() to validate redirect targets, applied at two layers:

  • lib/routes.ts — New isSafeRedirectPath(path, fallback) utility that blocks protocol URLs, protocol-relative URLs (//), backslash variants (\\, \/), javascript:/data: URIs, non-relative paths, and directory traversal (/../)
  • lib/oidc.ts — Sanitizes redirect at parse time in parseOidcCallback()
  • app/(auth)/auth/oidc-callback/page.tsx — Defense-in-depth validation before router.replace()
// Before (vulnerable)
redirect: params.get("redirect") ?? "/"
redirectPath.current = credentials.redirect || "/browser"

// After
redirect: isSafeRedirectPath(params.get("redirect") ?? "", "/")
redirectPath.current = isSafeRedirectPath(credentials.redirect, "/browser")

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test improvements
  • Security fix

Testing

Validated isSafeRedirectPath against 25 test vectors covering safe paths, absolute URLs, protocol-relative URLs, backslash variants, javascript:/data: URIs, directory traversal, empty/null input, and whitespace edge cases. All pass.

  • Unit tests added/updated
  • Manual testing completed
pnpm test:run

Checklist

  • Code follows the project's style guidelines
  • Self-review completed
  • TypeScript types are properly defined
  • All commit messages are in English (Conventional Commits)
  • All existing tests pass
  • No new dependencies added, or they are justified

Related Issues

GHSA-v9fg-3cr2-277j

Screenshots (if applicable)

N/A — no UI changes.

Additional Notes

CodeQL scan returned 0 alerts after changes.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • www.google.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits February 23, 2026 07:08
Add isSafeRedirectPath() validation to ensure redirect URLs in the OIDC
callback are relative paths only. This prevents attackers from crafting
malicious callback URLs that redirect users to external phishing sites.

Defense-in-depth: validation is applied both in parseOidcCallback()
(lib/oidc.ts) and in the callback page component.

Co-authored-by: overtrue <1472352+overtrue@users.noreply.github.com>
…checks

Address code review feedback:
- Improved backslash URL detection with /^[\\/]{2,}/ and explicit \\ check
- Added directory traversal prevention for /../ patterns

Co-authored-by: overtrue <1472352+overtrue@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix vulnerability in RustFS security advisory GHSA-v9fg-3cr2-277j fix: prevent open redirect in OIDC callback flow Feb 23, 2026
Copilot AI requested a review from overtrue February 23, 2026 07:13
@overtrue overtrue marked this pull request as ready for review February 23, 2026 07:30
Copilot AI review requested due to automatic review settings February 23, 2026 07:30
@overtrue overtrue merged commit 3b632be into main Feb 23, 2026
1 check passed
@overtrue overtrue deleted the copilot/fix-security-advisory-issue branch February 23, 2026 07:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses an open-redirect risk in the OIDC callback flow by validating the redirect value extracted from the URL hash before client-side navigation.

Changes:

  • Added isSafeRedirectPath() helper to validate same-origin redirect targets and apply a fallback.
  • Sanitized the parsed redirect value in parseOidcCallback().
  • Re-validated the redirect path in the OIDC callback page before calling router.replace().

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
lib/routes.ts Introduces isSafeRedirectPath() for redirect target validation.
lib/oidc.ts Sanitizes redirect during OIDC hash parsing via isSafeRedirectPath().
app/(auth)/auth/oidc-callback/page.tsx Applies defense-in-depth validation before navigation in the callback page.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

redirectPath.current = credentials.redirect || "/browser"
redirectPath.current = isSafeRedirectPath(credentials.redirect, "/browser")
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

credentials.redirect has already been sanitized in parseOidcCallback() (and will be a non-empty string due to its fallback), so this second isSafeRedirectPath(..., "/browser") call will never hit the "/browser" fallback. Either remove the duplicate sanitization here, or make the two layers use the same fallback so the effective default isn’t surprising.

Copilot uses AI. Check for mistakes.
Comment thread lib/routes.ts
Comment on lines +30 to +33
/**
* Validate that a redirect path is safe (relative, no protocol, no external domain).
* Returns the path if safe, or the fallback otherwise.
*/
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSDoc says the redirect path is validated as "relative", but the implementation requires it to start with "/" (i.e., an absolute-path reference). Consider updating the comment to match the actual constraint (same-origin path starting with "/", and explicitly disallowing "foo/bar").

Copilot uses AI. Check for mistakes.
Comment thread lib/oidc.ts
Comment on lines 55 to 57
expiration: params.get("expiration") ?? "",
redirect: params.get("redirect") ?? "/",
redirect: isSafeRedirectPath(params.get("redirect") ?? "", "/"),
}
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With params.get("redirect") ?? "" and isSafeRedirectPath treating empty strings as invalid, a callback containing redirect= (present but empty) will now produce "/" (fallback) rather than propagating an empty value to the caller. If the caller-level fallback (e.g. "/browser") is intended for empty redirects, consider aligning the behavior by either (a) sanitizing only at the call site with the desired fallback, or (b) using a fallback here that preserves the empty-string case (so the page-level fallback can still apply).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants