Skip to content

VNX-863: Incorrect Authorization

Authorization decided by a client-sent header, cookie or privilege-named query value, a role checked as a substring, a Python assert used as the check, or a JavaScript if that assigns the role instead of comparing it.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-863
Severity High
SARIF level error
Kind sast
Languages JavaScript / TypeScript, Python
CWE CWE-863
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H
Tags incorrect-authorization cwe-863 access-control
Source internal/sast/rules/vnx-863.rego

Overview

VNX-863 maps to CWE-863: Incorrect Authorization. It reports four ways an authorization check is made wrongly, in Python and JavaScript/TypeScript:

  1. The client decides. A condition (if/elif, ternary, &&/||) on a request header, cookie or Django META entry, or on a query parameter whose name says admin/role/staff/permission, directly or through a variable assigned from it within the 15 lines above. A header or cookie compared with an administrative literal ('admin', 'superuser', 'owner' …) counts whatever its name. Two cookie forms are left to the rule that names them more precisely: a cookie named for a privilege or login state, compared or tested on its line or through a variable, is reported by VNX-784 (a security decision on a cookie), and an if/elif comparing any other request cookie with a privileged literal is reported by VNX-807 (untrusted input in a security decision).
  2. Substring role check. 'admin' in user.role, role.startswith('admin'), user.role.includes('admin'), role.indexOf('superuser') !== -1, /moderator/.test(user.role): 'not-admin' passes.
  3. Assert as the check. assert request.user.is_staff, assert user.has_role(...) outside tests; python -O removes it.
  4. Assignment in the condition. if (user.role = 'admin'), if (req.user.isAdmin = true): always true.

Not reported: roles read from the server-side user or session (req.user.role, req.session.role, user.roles lists), the Authorization header and other non-privilege headers, a query value compared with 'admin' for navigation, a condition that rejects a request claiming the privilege (if (req.query.role === 'admin') return 403), which refuses an escalation attempt. A negated guard that rejects when the claim is absent (if request.cookies.get("role") != "admin": abort(403)) still trusts the client and is reported, asserts in tests, comments and strings.

Why This Matters

Each of these checks exists and runs, which is why it survives review: it simply answers the wrong question. A cookie named admin is set by the browser, so any caller can send admin=1. A substring test accepts roles nobody intended, an assert vanishes in optimised builds, and an assignment grants the role it was meant to test.

What Gets Flagged

if request.META.get('HTTP_X_IS_STAFF'):         # FLAGGED
if "admin" not in current_user.role:            # FLAGGED (substring)
assert request.user.is_staff, "staff only"      # FLAGGED
if request.headers.get("X-Role") != "admin":     # FLAGGED (guard trusts the header)
    abort(403)
if request.COOKIES.get('admin') == "1":          # reported by VNX-784, not VNX-863
if (req.headers['x-role'] === 'admin') {        // FLAGGED
const role = req.header('X-Role');
if (role === 'admin' || role === 'owner') {     // FLAGGED
if (user.role.includes('admin')) {              // FLAGGED
if (user.role = 'admin') {                      // FLAGGED

Not flagged: if (req.user.role === 'admin'), if "admin" in user.roles:, if request.args.get("tab") == "admin":, if (req.query.role === 'admin') return res.status(403)....

Remediation

  1. Decide authorization from the authenticated identity (session or verified token) and its stored roles, never from a value the client sends.
  2. Compare roles for equality, or test membership in a list of roles.
  3. Raise PermissionDenied or abort(403) instead of asserting.
  4. Use === in conditions; a linter rule such as no-cond-assign catches the assignment form.

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-863
// nosec vnx-863

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

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