VNX-140: Improper Neutralization of Delimiters
Reports Python and JavaScript/TypeScript code that writes a CSV, TSV, pipe- or colon-delimited record to a file or stream by joining request data with the delimiter, without quoting or escaping it.
| Field | Value |
|---|---|
| Rule ID | VNX-140 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript, Python |
| CWE | CWE-140 |
| 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 | delimiter injection csv cwe-140 |
| Source | internal/sast/rules/vnx-140.rego |
Overview
VNX-140 maps to CWE-140: Improper Neutralization of Delimiters. It reports a delimited record built by gluing request data together with a delimiter (,, |, ;, \t, :) and written to a file or stream: f-strings and template literals, + concatenation, "|".join(...) / .join(','), "%s,%s" % and "{},{}".format(...), either on the write itself or in a record variable built in the 6 lines above it. A value that contains the delimiter or a newline then adds fields or whole records.
The data must come from the request on that line (Flask/Django request.form/args/values/json/data/cookies/headers/GET/POST, Express/Koa req/ctx body/query/params/cookies/headers) or from a variable assigned from it in the 10 lines above. Sinks are .write(), .writelines(), print(..., file=...) and fs.appendFile/writeFile (and their Sync forms).
What Gets Flagged
name = request.form["name"]
email = request.form["email"]
f.write(f"{name},{email}\n") # FLAGGED
out.write(user + ":" + role + "\n") # FLAGGED
fs.appendFileSync('audit.csv', `${req.body.user},${req.body.action}\n`) // FLAGGED
const line = [req.body.item, req.body.qty, req.ip].join(',')
sink.write(line + '\n') // FLAGGED
What Is Not Flagged
csv.writer/DictWriter,csv-stringify,json.dumps/JSON.stringify.- Values passed through
.replace(),re.sub(),quote()or an escape function first. - Constant header rows, and data that does not come from the request.
- HTTP response writes (
res.write,response.write,self.wfile), which are a different weakness. - Splitting input on a delimiter, which is VNX-166.
Remediation
Write delimited files with a CSV library that quotes fields (csv.writer, csv-stringify), or reject or escape the delimiter and newlines in every value before joining.
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-140
// nosec vnx-140
Or record an ignore rule: vulnetix ignore add --rule vnx-140 --reason "...". See Suppressing findings.