require-case-insensitive-path-guard
Require case-insensitive comparison when guarding protected paths via req.path / req.url / req.originalUrl
Require case-insensitive comparison when guarding protected paths via req.path / req.url / req.originalUrl
This rule detects path-based authorization guards that compare req.path case-sensitively, which case-insensitive Express routing bypasses
Severity: 🔴 High
CWE: CWE-178
Rule Details
Express routers can match paths case-insensitively — Router({ caseSensitive: false }) is the default, and regex routes frequently carry the i flag. A middleware guard that checks req.path.startsWith('/admin') only sees the exact lower-case spelling: a request to /Admin/users skips the guard entirely but still reaches the handler. The authorization check silently never runs.
This rule flags case-sensitive comparisons (startsWith, endsWith, includes, indexOf, match, and ===/==/!==/!= equality) between a req.path / req.url / req.originalUrl access and a protected-looking value (default patterns: admin, api, dashboard, internal, private).
Route registrations are never flagged — app.get('/admin', ...) is routing, not guarding.
Examples
❌ Incorrect
// Prefix guard — GET /Admin/users bypasses it
app.use((req, res, next) => {
if (req.path.startsWith('/admin')) {
if (!req.user || req.user.role !== 'admin') return res.sendStatus(403);
}
next();
});
// Equality guard — same bypass
if (req.url === '/admin') requireAuth(req);
// indexOf / includes guards
if (req.path.indexOf('/admin') === 0) requireAuth(req);
if (req.url.includes('/internal')) requireAuth(req);
// Regex guard without the i flag
if (req.path.match(/^\/admin/)) requireAuth(req);✅ Correct
// Normalize the path before comparing
const normalizedPath = req.path.toLowerCase();
if (normalizedPath.startsWith('/admin')) {
if (!req.user || req.user.role !== 'admin') return res.sendStatus(403);
}
// Or normalize inline
if (req.path.toLowerCase().startsWith('/admin')) requireAuth(req);
// Regex guard with the i flag matches the router's semantics
if (req.path.match(/^\/admin/i)) requireAuth(req);
// Route registration is routing, not guarding — never flagged
app.get('/admin', requireAuth, adminHandler);
// Non-protected paths are not flagged by default
if (req.path.startsWith('/health')) return next();Suggestions
The rule offers editor suggestions (no auto-fix):
- String / equality guards — insert
.toLowerCase()after the path access, and lower-case the compared literal when it contains upper-case characters. - Regex guards — append the
iflag to the regex literal.
Options
| Option | Type | Default | Description |
|---|---|---|---|
protectedPaths | string[] | ['admin', 'api', 'dashboard', 'internal', 'private'] | Substrings that mark a compared value as a protected path (replaces the default set) |
checkAllPaths | boolean | false | Flag every case-sensitive path guard regardless of the compared value |
{
"rules": {
"express-security/require-case-insensitive-path-guard": [
"error",
{ "protectedPaths": ["admin", "billing"], "checkAllPaths": false }
]
}
}When Not To Use It
If every router in your app is explicitly created with Router({ caseSensitive: true }) and you audit that setting in review, case-sensitive guards match the router's semantics and this rule can be disabled.
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Indirect Path Variable
Why: No data-flow analysis — only direct req.path / req.url / req.originalUrl member accesses are matched.
// ❌ NOT DETECTED — path copied to a variable first
const p = req.path;
if (p.startsWith('/admin')) requireAuth(req);Mitigation: Normalize at the copy: const p = req.path.toLowerCase();.
Non-Literal Comparison Values
Why: The compared value must be a string or regex literal.
// ❌ NOT DETECTED — prefix held in a variable
const ADMIN = '/admin';
if (req.path.startsWith(ADMIN)) requireAuth(req);Mitigation: Enable checkAllPaths in a follow-up audit, or normalize the path unconditionally.
Computed Property Access
Why: req['path'] is not matched — only non-computed member accesses.
// ❌ NOT DETECTED
if (req['path'].startsWith('/admin')) requireAuth(req);Mitigation: Prefer req.path — the rule then applies.
Further Reading
- CWE-178: Improper Handling of Case Sensitivity
- OWASP A01:2021 – Broken Access Control
- Express Router options (
caseSensitive)
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.