VNX-115: Misinterpretation of Input
A request value is converted or compared in a way that reads it as something other than what the requester sent, so the program acts on a different value than the one it checked or meant (CWE-115). Reported: JavaScript/TypeScript parseInt/Number.parseInt of request data with no radix argument ("0x10" is read as 16); JavaScript Boolean(x) or !!x and Python bool(x) of a string request value (req.query/params/headers/cookies; request.args/form/values/GET/POST/cookies/headers .get() or [...]), which is true for "false" and "0"; PHP loose comparison (== or !=) of request data with a token, password, hash, secret, signature, key, code or PIN ("0e..." magic hashes and type juggling make unequal values equal), strcmp() of request data tested with == 0 or ! (an array argument returns NULL, which equals 0), and in_array() of request data without the strict third argument. Request data is used inline or through a variable assigned from it in the 25 lines above. Not reported: parseInt with a radix, Number(), explicit comparisons such as === 'true' or .lower() == 'true', bool() of a whole collection (bool(request.args)), JSON body values in the Boolean checks (already typed), ===, hash_equals and password_verify, in_array(..., true), loose comparison with an ordinary string, and code in comments. Two shapes are reported by a more precise rule, so the line is reported once: a radix-less parseInt of request data used inline or through a variable assigned within the 10 lines above by VNX-1389 (incorrect parsing of numbers with different radices), and a PHP loose comparison or strcmp() test that VNX-PHP-010 reports (PHP type juggling); this rule keeps the longer 25-line flows, the Boolean/bool checks and non-strict in_array().
| Field | Value |
|---|---|
| Rule ID | VNX-115 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript, PHP, Python |
| CWE | CWE-115 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | misinterpretation type-juggling input cwe-115 |
| Source | internal/sast/rules/vnx-115.rego |
Overview
VNX-115 maps to CWE-115: Misinterpretation of Input. It reports conversions and comparisons that interpret a request value differently from what the requester sent, when the value is used inline or through a variable assigned from the request in the 25 lines above:
- JavaScript/TypeScript
parseInt()/Number.parseInt()of request data with no radix ("0x10"becomes 16). - JavaScript
Boolean(x)/!!xand Pythonbool(x)of a string request value (query, params, headers, cookies, form):"false"and"0"are true. - PHP
==/!=between request data and a token, password, hash, secret, signature, key, code or PIN (type juggling,0e...magic hashes);strcmp()of request data tested with== 0or!(an array argument returns NULL);in_array()of request data without the strict third argument.
Not reported: parseInt(v, 10), Number(), explicit comparisons (=== 'true', .lower() == "true"), bool(request.args), JSON body values in the boolean checks, ===, hash_equals, password_verify, in_array(..., true), and loose comparison with an ordinary string.
Two shapes are reported by a more precise rule, so the same line is not reported twice: a radix-less parseInt of request data used inline or through a variable assigned within the 10 lines above by VNX-1389, and a PHP loose comparison or strcmp() test of request data by VNX-PHP-010. This rule keeps the longer flows (a variable assigned up to 25 lines above), the Boolean/bool checks and non-strict in_array().
Why This Matters
When the program’s reading of a value differs from the requester’s, checks stop meaning what they say. ?include_deleted=false turns the flag on; a reset token of 0e1234 loosely equals another magic-hash token; password[]=x makes strcmp() return NULL and the login succeed.
What Gets Flagged
const isAdmin = Boolean(req.query.admin); // FLAGGED: "false" is true
// REPORTED BY VNX-1389: const limit = parseInt(req.query.limit);
include_deleted = bool(request.args.get("include_deleted")) # FLAGGED
<?php
// REPORTED BY VNX-PHP-010: if ($_GET['token'] == $stored) { }
$role = $_GET['role'];
if (in_array($role, ['viewer', 'editor'])) { } // FLAGGED
Not flagged: parseInt(req.query.limit, 10), req.query.admin === 'true', request.args.get("x", type=str2bool), hash_equals($stored, $_GET['token']), in_array($_GET['role'], [...], true), $_GET['page'] == 'home'.
Remediation
- Always pass a radix to
parseInt, or useNumber()and checkNumber.isInteger. - Convert boolean flags explicitly:
v === 'true',v.lower() in ('1', 'true', 'yes'), or a typed request parser. - In PHP, compare secrets with
hash_equals(), passwords withpassword_verify(), and use===andin_array(..., true)for everything else.
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-115
// nosec vnx-115
Or record an ignore rule: vulnetix ignore add --rule vnx-115 --reason "...". See Suppressing findings.