VNX-117: Improper Output Neutralization for Logs
Decoded HTTP request data written into a Java, Python or Node log message without CR/LF neutralisation, inline or through a variable assigned from the request.
| Field | Value |
|---|---|
| Rule ID | VNX-117 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Java, JavaScript / TypeScript, Python |
| CWE | CWE-117 |
| 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 | log-injection crlf logging cwe-117 |
| Source | internal/sast/rules/vnx-117.rego |
Overview
VNX-117 maps to CWE-117: Improper Output Neutralization for Logs. It reports a logger call whose arguments carry a decoded request value: Java request.getParameter()/getHeader() or a String handler parameter bound with @RequestParam, @PathVariable, @RequestHeader or @CookieValue; Python request.args/form/json/headers/GET/POST/META; Node req.query/body/params/headers/cookies. The value can be used inline, interpolated, or through a variable assigned from it (or from such a variable) in the 25 lines above.
Not reported: values passed through a CR/LF replace/replaceAll, an escaping or encoding call, JSON.stringify, repr/%r, or a numeric conversion; structured log calls whose first argument is an object literal; still-URL-encoded values such as getRequestURI(), req.originalUrl and request.path.
Why This Matters
A request value containing \r\n starts a new line in a plain-text log. An attacker can forge entries (“login succeeded for admin”), hide their own activity among fake lines, or inject content that log viewers and SIEM parsers interpret as markup or fields.
What Gets Flagged
// FLAGGED
String user = request.getParameter("user");
log.warn("Failed login for user " + user);
log.info("User-Agent: {}", request.getHeader("User-Agent"));
# FLAGGED
username = request.form.get("username")
logger.warning("failed login for %s", username)
app.logger.info(f"login attempt from {request.headers.get('X-Forwarded-For')}")
// FLAGGED
console.log('login attempt for ' + req.body.username);
logger.warn(`bad password for ${req.body.username}`);
Not flagged: log.info("User " + user.replace('\n', '_').replace('\r', '_')), logger.info("query %r", request.args.get("q")), console.log('search for ' + JSON.stringify(req.query.q)), pino.info({ query: req.query.q }, 'search').
Remediation
- Strip or replace
\rand\n(and other control characters) from request values before logging them. - Prefer a structured (JSON) log encoder, which escapes newlines inside field values, and pass request data as fields rather than inside the message text.
- In Python,
%rorrepr()renders control characters escaped.
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-117
// nosec vnx-117
Or record an ignore rule: vulnetix ignore add --rule vnx-117 --reason "...". See Suppressing findings.