Navigation that goes wherever it is told
Unvalidated Redirect
Why this workflow produces it
Read the two numbers in the header of this section together: it reached most of the sample and fired several times inside each project. That is not one login redirect. That is a navigation pattern used throughout the app.
These projects are single-page dashboards, and the generated navigation helper takes its destination from something dynamic — a query parameter, a stored return-to value, a field on a record — and hands it to the router or straight to the browser's location.
The behaviour it implements is genuinely useful, which is why it never gets removed: send the user back where they came from after signing in, or forward to the thing they clicked before they were asked to authenticate. The only missing piece is that the destination is never checked against the set of places you are willing to send somebody.
The result is a link on your own domain that quietly bounces a visitor onto someone else's, carrying your app's credibility with it. On a page people reach while signed in, that is a working phishing setup that costs the attacker nothing to build.
The change that prevents it
Resolve destinations against an allowlist of your own routes instead of passing a URL through. A redirect parameter should name a route, not supply a location.
Paste this into your project instructions, or into the prompt itself:
Never pass a value from a query string, a stored value or a record field directly to router.push, navigate or window.location. Redirect targets must be looked up in an allowlist of internal route names defined in code. If a value does not match one, fall back to the home route rather than following it.
The scan that verifies it
Scan, then try it: load your own app with a redirect parameter pointing at an external domain and see whether it follows. That single test answers the question faster than reading every hit.
npx xploitscan scan . -f json \ | jq '[.findings[] | select(.rule == "VC016")] | length'
Honest caveat. This check cannot always tell an internal route constant from a value someone else controls, so expect a share of these to be internal navigation that was never a risk. It is also worth being accurate about the impact — in a client-side router this is a phishing and trust problem, not a server-side request forgery. Worth an hour, not a weekend.