Skip to content

VNX-289: Authentication Bypass by Alternate Name

Detects access checks that compare a name exactly when another spelling reaches the same thing: an if on the raw request path (getRequestURI, req.url/req.path, request.path) against a protected '/admin'-style literal or followed by a denial, and a reserved-username check without case folding.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-289
Severity High
SARIF level error
Kind sast
Languages Java, JavaScript / TypeScript, Python
CWE CWE-289
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H
Tags auth-bypass-alternate-name path-normalisation cwe-289
Source internal/sast/rules/vnx-289.rego

Overview

VNX-289 maps to CWE-289: Authentication Bypass by Alternate Name. A check that matches a name exactly is skipped by any other name for the same resource.

  1. A guard on the raw request path. An if compares Java request.getRequestURI()/getRequestURL(), Node req.url/req.path/req.originalUrl/req.baseUrl or Python request.path/path_info/full_path/url (inline, or through a variable assigned from one in the 10 lines above) with startsWith, equals, endsWith, includes, indexOf, == or === against a /... literal, and the literal names a protected area (/admin, /internal, /private, /manage, /actuator, …) or the next three lines deny the request. /ADMIN, //admin, /admin/, /./admin, %61dmin and /admin;x reach the same handler in common frameworks and skip the check.
  2. A reserved account name compared case-sensitively. An if compares a username/login/account name with admin, administrator, root, superuser, system, … and rejects it within three lines, without lower-casing; Admin passes.

What Gets Flagged

if (request.getRequestURI().startsWith("/admin") && session.getAttribute("admin") == null) {  // FLAGGED
    response.sendError(403);
}
if (req.originalUrl === "/api/private/export") {   // FLAGGED
  return res.sendStatus(401);
}
if username == "admin":                            # FLAGGED
    raise ValueError("this name is reserved")

What Is Not Flagged

  • A path that is lower-cased, normalised or decoded first, and Java getServletPath().
  • Routing comparisons against unprotected paths that do not deny (req.url === '/health').
  • Role checks (user.role == "admin"), case-folded name checks, comments and string literals.

Whether the framework normalises the path before this code runs is configuration and is not judged.

Remediation

Enforce access on the matched route in the router or security framework (Spring Security requestMatchers, Express router-level middleware, Django decorators), not by comparing strings. Where a comparison is unavoidable, compare a canonical form: decode, normalise, and lower-case the path, and casefold() names after Unicode normalisation.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-289
// nosec vnx-289

Or record an ignore rule: vulnetix ignore add --rule vnx-289 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-289.rego, cli/internal/sast/docs/vnx-289.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.