Reflected XSS/Open redirect not detected in React application

Product used:
SonarQube EE

Rules affected: jssecurity:S5696, jssecurity:S5146, jssecurity:S6105

There’s an open redirect vulnerability in the UrlParam parameter, with javascript: URLs it can be escalated to an XSS as well. e.g. http://localhost/vulnerable?UrlParam=javascript:prompt(123)

The source is a GET parameter, the sink is the navigate function in @reach/router final sink in the lib source

The relevant code sections from the application:

import React, { useCallback } from 'react';
import { navigate } from '@reach/router';

const NavigateLink = ({ Content }) => {

	const onLinkClick = useCallback((e, url) => {
		const searchParams = new URLSearchParams(;
		const referrer = searchParams.get('UrlParam');
		navigate(referrer || url || '/');
	}, []);

   return (
    <TextLink onClick={(e) => onLinkClick(e, url)}>{linkLabel}</TextLink>
export default RouteContent('/vulnerable')(NavigateLink);

Let me know if further details are needed to reproduce the finding.

Hello @Adam_B,

Thanks a lot for this definitely complete work. We’ve been actually supporting React 9.6 for quite a short amount of time now, so it’s going to be one of our first feedbacks on this matter. Thanks a lot!

I see two actions on our side:

  • Supporting the navigate sink
  • Supporting Open Redirect escalations to XSS (this might need a new rule, for clarity’s sake)

Have a good day!


A PoC app (without our internal dependencies) for better reproducibility:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { useCallback, useState } from 'react';
import { navigate } from '@reach/router';

const useToggle = (initialState = false) => {
  // Initialize the state
  const [state, setState] = useState(initialState);
  // This function change the boolean value to it's opposite value
  const toggle = useCallback(() => {
      setState(state => !state);
      const searchParams = new URLSearchParams(;
  const referrer = searchParams.get('UrlParam');
  console.log("URL: "+referrer);
      navigate(referrer || '/');
  }, []);
  return [state, toggle]

function App() {
  const [isDisabled, setIsDisabled] = useToggle();
  return (
      <button onClick={setIsDisabled} disabled={isDisabled ? 'disabled' : ''}>Click to navigate to URL</button>

const root = ReactDOM.createRoot(document.getElementById('root'));
  <h1>Open redirect/XSS PoC</h1>
  <p>There's no validation of target URLs in @reach/router's navigate function. This function should be identified by SAST tools as a sensitive sink when supplying user input to it.
  <br/><br/>Open the links below then click on the button to navigate. The injection point is in the <i>UrlParam</i> GET parameter</p><br/>
  <a href="?UrlParam=">Open redirect (</a><br/>
  <a href="?UrlParam=javascript:confirm('XSS')">Cross-site scripting (confirm dialog)</a><br/><br/>
    <App />
1 Like

Thanks for this. I will use this in our internal tickets.


For the record, I created two tickets to track the support of navigate and the escalation from Open Redirectts to XSS (unfortunately closed-source):

Have a good day!



This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.