RSTR-NOSQLI-003 — PyMongo filter built from request.*
Summary
A PyMongo query is built by passing a request-derived dict (or request.json,
request.form, request.values) directly as the filter argument. An
attacker who submits {"$gt": ""} instead of a string causes the filter
to match every document — bypassing intended access controls.
This is the Python counterpart of RSTR-NOSQLI-001.
Severity
High.
Languages
Python (PyMongo / Motor).
What rastray flags
db.users.find_one(request.json) # ← flagged
db.posts.update_one(request.form, {'$set': ...}) # ← flagged
What rastray deliberately does not flag
- Filters constructed from individual fields:
db.users.find_one({'_id': ObjectId(request.json['id'])}). - Filters built after
marshmallow/pydanticschema validation.
How to fix it
Validate and coerce inputs before building the filter. With pydantic:
from pydantic import BaseModel
from bson import ObjectId
class UserQuery(BaseModel):
id: str
email: str | None = None
@app.post('/users')
def find_user(q: UserQuery = Depends(...)):
return db.users.find_one({
'_id': ObjectId(q.id),
'email': q.email,
})
Plain dict construction also works as long as each field comes through a per-field coercion (string, int, etc.) — the rule fires precisely because passing the entire request object lets operator keys through.