Skip to main content

NoSQL Injection (MongoDB operator injection)

A route puts a request field, unparsed, into a MongoDB query filter. A plain string behaves; an operator object such as {"$ne": null} changes the filter, which means an authentication bypass or data exfiltration.

What Vulkro detects​

Three call-site rules for JavaScript / TypeScript and Python, all High severity (no cross-function taint):

  • NOSQLI-001: a MongoDB read or write sink (find, findOne, updateOne, deleteOne, aggregate and friends, or PyMongo equivalents) binds a request value (req.body.X, req.query.X, req.params.X, Python request.json[...], request.form[...], request.args.get(...)) directly to a non-operator field key with no coercion.
  • NOSQLI-002: a $where clause built from a template literal or concatenation with user input. $where runs server-side JavaScript. A static $where literal is not flagged.
  • NOSQLI-003: the whole request object passed as the filter (Model.find(req.body), coll.find(request.json)).

Not flagged: values coerced with String(...), Number(...), new ObjectId(...), .toString(), str(...) or int(...); user values under an operator key the code controls ({ age: { $gt: req.query.age } }). Files with no MongoDB context are skipped, so Array .find(callback) or a SQL ORM never fires.

Non-compliant code​

// Express + Mongoose: request value bound straight into the filter
app.post('/login', async (req, res) => {
const user = await User.findOne({
username: req.body.username,
password: req.body.password, // {"$ne": null} matches any row
});
res.json(user);
});

Compliant code​

// Express + Mongoose: coerced to strings, password verified separately
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '');
const user = await User.findOne({ username });
if (!user || !(await bcrypt.compare(String(req.body.password ?? ''), user.hash))) {
return res.sendStatus(401);
}
res.json({ id: user.id });
});

How to fix​

  • Coerce every user filter value to the field's type (String(...), new ObjectId(...), or a schema validator such as zod or pydantic).
  • Never pass a request body in as a filter.
  • Replace $where with a query operator or an aggregation expression.

References​

See also: Confidence model.


This page is generated by vulkro rules export <out-dir> from Vulkro's built-in detector catalogue. Edits made by hand are overwritten on the next regeneration.