Skip to content

VNX-626: Request input with an appended file extension used as a file path (null byte truncation)

Reports PHP and Java file paths built from request input with a fixed extension appended, where a NUL byte can truncate the path and drop the suffix.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-626
Severity Medium
SARIF level warning
Kind sast
Languages PHP, Java
CWE CWE-626
CAPEC CAPEC-97
ATT&CK T1557
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 null-byte path-traversal cwe-626
Source internal/sast/rules/vnx-626.rego

Overview

In languages that interface with C-based libraries (PHP, early Python versions), a null byte (%00 or \0) in a string terminates it at the C level while the higher-level language sees the full string. Attackers append %00 to filenames to strip validation suffixes: ../../../../etc/passwd%00.jpg passes an .jpg extension check but opens /etc/passwd.

Why This Matters

PHP versions before 5.3.4 were notoriously vulnerable: include($_GET['page'] . '.php') could be bypassed with page=../../../../etc/passwd%00 to read arbitrary files. Even modern applications using C-extension file paths may be vulnerable if null bytes are not stripped from user input.

What Gets Flagged

<?php
// FLAGGED: User input used directly in include without null byte sanitization
include($_GET['page'] . '.php');

// FLAGGED: the suffix is the only type restriction
$doc = $_GET['doc'];
echo file_get_contents("docs/{$doc}.html");
// FLAGGED
File f = new File(reportDir, request.getParameter("name") + ".txt");

Not flagged: input passed through basename(), an int cast, str_replace(chr(0), ...) or an allow-list check (in_array, preg_match, String.matches) before use; paths with no appended extension (path traversal and file inclusion rules such as VNX-98 cover those); constants; comments and string literals. Python, Ruby, Go and Node.js reject NUL bytes in paths and are not covered.

Remediation

Strip null bytes from user input and validate after sanitization:

<?php
// SAFE: Strip null bytes and validate extension
$page = str_replace(chr(0), '', $_GET['page']);
if (!preg_match('/^[a-zA-Z0-9_\-]+$/', $page)) {
    die('Invalid page name');
}
include($BASE_DIR . $page . '.php');
# SAFE: Strip null bytes, use basename, validate
path = request.GET.get('file', '')
path = path.replace('\x00', '')
path = os.path.basename(path)  # strip directory traversal
full_path = os.path.realpath(os.path.join(base_dir, path))
if not full_path.startswith(os.path.realpath(base_dir)):
    return HttpResponseForbidden()

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

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

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