VNX-179: Incorrect Behavior Order: Early Validation
Reports a check for a dangerous sequence (.., /, <script, a quote) that runs before the same value is URL-, HTML- or Unicode-decoded, and a check on the raw query string before a decoded parameter is read.
| Field | Value |
|---|---|
| Rule ID | VNX-179 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java, Python, JavaScript / TypeScript, Go, PHP |
| CWE | CWE-179 |
| 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 | validation ordering canonicalization cwe-179 |
| Source | internal/sast/rules/vnx-179.rego |
Overview
VNX-179 maps to CWE-179: Incorrect Behavior Order: Early Validation. It reports a condition that checks a value for a dangerous sequence (.., ../, /, \, <, <script, a quote) when the same value is decoded or canonicalised within the next ten lines:
- Java
URLDecoder.decode,Normalizer.normalize - Python
unquote/unquote_plus,html.unescape,unicodedata.normalize - JavaScript
decodeURI/decodeURIComponent - Go
url.PathUnescape/url.QueryUnescape - PHP
urldecode/rawurldecode/html_entity_decode
It also reports a check on the raw query string, request URI or URL (Java getQueryString()/getRequestURI(), Python request.query_string/request.full_path, Express req.originalUrl/req.url, Go r.URL.RawQuery/RawPath, PHP $_SERVER['QUERY_STRING']/['REQUEST_URI']), directly or through a variable assigned from it, followed within ten lines by a decoded parameter read (getParameter, request.args, req.query/req.params, URL.Query().Get, FormValue, $_GET/$_REQUEST), because the framework decodes the parameter after the check has passed.
The supported checks are contains/includes/indexOf/startsWith/matches, Python in, strings.Contains/HasPrefix/Index, strpos/stripos/str_contains/strstr, and a regular expression for .. or <script (/\.\.\//.test(x), re.search(r"\.\.", x)). Not reported: decoding before the check, a check on a different variable, a decoded value that is checked again within six lines, a check that also rejects % (encoded input), comments and string literals.
Why This Matters
A check made before decoding inspects a form of the data that is not the one used. %2e%2e%2f contains no ../, passes the check, and becomes ../ one line later.
What Gets Flagged
// FLAGGED: checked, then decoded
if (file == null || file.contains("..")) { ... }
String name = URLDecoder.decode(file, StandardCharsets.UTF_8);
// FLAGGED: raw query string checked, decoded parameter used
var query = request.getQueryString();
if (query != null && (query.contains("..") || query.contains("/"))) { ... }
var id = request.getParameter("id");
// FLAGGED
if (p.includes('../')) return res.status(400).end();
const target = decodeURIComponent(p);
// FLAGGED: raw query checked, decoded parameter used
if strings.Contains(r.URL.RawQuery, "..") { ... }
name := r.URL.Query().Get("name")
# NOT FLAGGED: decoded first, then checked
name = unquote(request.args.get("name", ""))
if ".." in name:
abort(400)
Remediation
- Decode and canonicalise input completely before validating it.
- Validate the exact value that is used, not an earlier form of it.
- For paths, resolve the final path and check that it stays under its base directory.
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-179
// nosec vnx-179
Or record an ignore rule: vulnetix ignore add --rule vnx-179 --reason "...". See Suppressing findings.