VNX-603: Authentication decided in browser code
Reports browser JavaScript that decides authentication itself: a literal login or role flag written to localStorage, sessionStorage or document.cookie, such a flag read back to gate the page, or a password or PIN compared with a string literal.
| Field | Value |
|---|---|
| Rule ID | VNX-603 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript |
| CWE | CWE-603 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | javascript browser authentication client-side-auth cwe-603 |
| Source | internal/sast/rules/vnx-603.rego |
Overview
VNX-603 maps to CWE-603: Use of Client-Side Authentication. Anything the browser decides, the user can change from the developer console.
The rule reports:
- a login or role flag written as a literal to
localStorage/sessionStorage(setItem('isLoggedIn', 'true'),sessionStorage.isAdmin = true) or todocument.cookie(loggedIn=true,role=admin); - such a flag read back from storage and compared or tested to gate the page, directly or through a variable it was read into and a condition tests within the next 10 lines;
- a password, passcode or PIN (a variable so named, or the
.valueof an element whose id names one) compared with a non-empty string literal, in a file that uses the DOM and is not server code.
Flag names are isLoggedIn/loggedIn/logged_in, isAuthenticated/authenticated/auth, isAdmin/admin, role/userRole, and isAuthorized/authorized.
It does not report tokens stored or sent to the server (the server verifies them), flags set from a server response rather than a literal, clearing a flag (removeItem, 'false'), unrelated preferences (theme, consent), empty-string checks, server-side password comparison (bcrypt.compare, req.body), or comments and prose inside string literals.
What Gets Flagged
localStorage.setItem('isLoggedIn', 'true'); // FLAGGED
if (localStorage.getItem('isLoggedIn') !== 'true') { } // FLAGGED
if (user === 'admin' && pass === 'S3cretPass!') { } // FLAGGED
document.cookie = "loggedIn=true; path=/"; // FLAGGED
// NOT FLAGGED
localStorage.setItem('token', res.accessToken);
fetch('/api/me', { headers: { Authorization: 'Bearer ' + token } });
localStorage.setItem('theme', 'dark');
Remediation
- Authenticate on the server and keep the result in a server-side session or a signed token.
- Gate every protected page and API on the server; browser-side checks are for display only.
- Never ship a password or PIN to the browser to compare against.
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-603
// nosec vnx-603
Or record an ignore rule: vulnetix ignore add --rule vnx-603 --reason "...". See Suppressing findings.