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,aggregateand friends, or PyMongo equivalents) binds a request value (req.body.X,req.query.X,req.params.X, Pythonrequest.json[...],request.form[...],request.args.get(...)) directly to a non-operator field key with no coercion.NOSQLI-002: a$whereclause built from a template literal or concatenation with user input.$whereruns server-side JavaScript. A static$whereliteral 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
$wherewith 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.