Skip to content

VNX-943: NoSQL injection in a MongoDB query (Python, PHP, Java)

Reports MongoDB queries built from untrusted data in Python, PHP and Java: $where clauses built from formatted strings, filters taken from a parsed request body or a PHP request array, and JSON query strings built by concatenation.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-943
Severity High
SARIF level error
Kind sast
Languages Python, PHP, Java
CWE CWE-943
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 nosql-injection mongodb injection cwe-943
Source internal/sast/rules/vnx-943.rego

Overview

VNX-943 maps to CWE-943: Improper Neutralization of Special Elements in Data Query Logic. It reports a MongoDB query whose structure an attacker can control:

  • Python (pymongo, motor): a $where clause built with an f-string, +, % or .format(); or the filter passed to find, find_one, find_one_and_*, update_*, delete_*, replace_one, count_documents or aggregate that is, or contains, the parsed JSON request body (request.json, request.get_json(), json.loads(request.data)), directly or through a variable assigned from it a few lines above.
  • PHP (mongodb extension or library): a query array given a $_GET/$_POST/$_REQUEST/$_COOKIE value (PHP parses name[$ne]=x into an array, which becomes an operator), or a '$where' built by concatenation or interpolation.
  • Java: a $where or Filters.where() built by concatenation, or Document.parse, BasicDBObject.parse, BsonDocument.parse or new BasicQuery given a JSON string built by concatenation or String.format.

Not reported: values cast to a string or number (str(), int(), ObjectId(), (string), strval()), {"$eq": ...} wrappers, Flask request.args values (always strings), constant $where clauses, typed filter builders (Filters.eq, Criteria), ORM find() calls. Node.js MongoDB filters are reported by VNX-NODE-014.

Why This Matters

A request body such as {"username": "admin", "password": {"$ne": null}} turns an equality match into an operator and logs in without a password. $where runs JavaScript on the database server, so any interpolated value can rewrite the predicate.

What Gets Flagged

user = users.find_one({"username": request.json["username"], "password": request.json["password"]})
cursor = db.orders.find({"$where": f"this.owner == '{owner}'"})
$user = $users->findOne(['username' => $_POST['username'], 'password' => $_POST['password']]);
Document filter = Document.parse("{ username: '" + user + "' }");
return orders.find(Filters.where("this.level > " + level));

Remediation

  1. Cast each request value to the type the field holds (str(), ObjectId(), (string)) or wrap it in {"$eq": value}.
  2. Replace $where with ordinary query operators that take the value as data.
  3. In Java, build filters with Filters.eq / Criteria.where(...).is(...) instead of parsing string-built JSON.

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

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

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