no-user-controlled-redirect
Disallow res.redirect() with values directly from req.query / req.body / req.params
Disallow
res.redirect()with values taken directly fromreq.query/req.body/req.params
Severity: 🔴 High CWE: CWE-601
Rule Details
res.redirect(req.query.url) is an open redirect: the attacker chooses the
destination, and the victim sees your trusted domain in the link they clicked.
Open redirects are a standard phishing primitive and an OAuth token-leak vector.
The rule flags a redirect whose argument comes from a user-controlled request
object (req.query, req.body, req.params). Validation only counts when it
guards the same value being redirected — checking req.query.a and then
redirecting req.query.b is still flagged, because the checked value and the
redirected value are different sources.
Examples
❌ Incorrect
app.get('/go', (req, res) => {
res.redirect(req.query.returnTo);
});// Validating one key and redirecting another is NOT validation.
app.get('/go', (req, res) => {
if (new URL(req.query['a']).host !== 'example.com') return res.sendStatus(400);
res.redirect(req.query['b']);
});✅ Correct
res.redirect('/dashboard');
res.redirect(301, '/login');// Allowlist the target, then redirect a value you constructed.
const ALLOWED = new Map([['docs', '/docs'], ['home', '/']]);
app.get('/go', (req, res) => {
res.redirect(ALLOWED.get(req.query.to) ?? '/');
});Configuration Examples
Basic Usage
// eslint.config.js
{
rules: {
'express-security/no-user-controlled-redirect': 'error',
},
}Options
| Option | Type | Default | Description |
|---|---|---|---|
responseObjects | string[] | — | Additional response object names (e.g. ["reply"] for Fastify) |
requestObjects | string[] | — | Additional request object names |
When Not To Use It
An internal admin tool where every user already holds full trust in the redirect targets. Even then, prefer the allowlist — it costs three lines.
Related Rules
no-host-header-in-links— the same trust problem for URLs built fromreq.headers.host
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.
no-unsafe-csp-directives
This rule detects Content-Security-Policy directives that hand back the protection the header exists to provide — unsafe-inline, unsafe-eval, wildcard sources, unrestricted framing, and dropped mixed-content upgrades
no-user-controlled-render-locals
Disallow res.render() with locals or view names sourced wholesale from req.body / req.query / req.params