-
Notifications
You must be signed in to change notification settings - Fork 35
Security finding: MongoDB operator injection in find and deleteOne tools #18
Description
Summary
We scanned mongo-mcp using agent-audit and found that the find and deleteOne tools pass unvalidated MongoDB query operators directly to the driver, enabling operator injection attacks.
OWASP Agentic AI Top 10: A03 - Insufficient Input/Output Validation
Finding: MongoDB Operator Injection via Unfiltered Query Filters
Files:
src/tools/documents/find.ts— line 47src/tools/documents/delete-one.ts— line 32
Both tools accept a filter object from the MCP tool call and pass it directly to the MongoDB driver with no operator filtering:
// src/tools/documents/find.ts:42-50
async execute(params: FindParams) {
const collection = this.validateCollection(params.collection);
const results = await db
.collection(collection)
.find(params.filter || {}) // ← filter passed directly, no operator validation
.project(params.projection || {})
.limit(Math.min(params.limit || 10, 1000))
.toArray();// src/tools/documents/delete-one.ts:28-32
async execute(params: DeleteOneParams) {
const collection = this.validateCollection(params.collection);
const filter = this.validateObject(params.filter, "Filter");
const result = await db.collection(collection).deleteOne(filter); // ← direct passvalidateObject (in base/tool.ts) only checks that the value is an object — it does not filter MongoDB operators.
Attack Vectors
1. ReDoS via $regex operator (any MongoDB version)
An AI agent receiving a malicious prompt could call find with:
{ "filter": { "name": { "$regex": "^(a+)+$", "$options": "i" } } }The catastrophic backtracking regex would block the MongoDB thread pool, causing server-side denial of service.
2. JavaScript execution via $where (MongoDB instances with javascriptEnabled: true)
On MongoDB deployments that have not explicitly disabled JavaScript execution:
{ "filter": { "$where": "function() { sleep(5000); return true; }" } }$where is disabled by default in MongoDB 4.4+, but many deployments still use older versions or have security.javascriptEnabled: true set explicitly.
3. Authentication bypass pattern via $ne/$gt
{ "filter": { "role": { "$ne": "user" } } }Can be used to enumerate privileged documents by querying "everything that is NOT user".
4. Recursive operator nesting for query amplification
Deeply nested operators like $or, $and, $not can be nested to force O(n^k) query plans, causing resource exhaustion.
Why This Matters for MCP
MCP tools receive inputs from AI agents. A document the AI reads, a webpage it visits, or a user message can contain a prompt injection payload that causes the AI to call find or deleteOne with a crafted MongoDB operator. Since the filter is passed directly to the driver, any valid MongoDB operator the AI sends will execute.
Suggested Fix
Implement a recursive operator allowlist or blocklist before executing the query:
const BLOCKED_OPERATORS = new Set(['$where', '$function', '$accumulator']);
function sanitizeFilter(filter: Record<string, unknown>): Record<string, unknown> {
for (const key of Object.keys(filter)) {
if (BLOCKED_OPERATORS.has(key)) {
throw new McpError(ErrorCode.InvalidRequest, `Operator ${key} is not permitted`);
}
const value = filter[key];
if (value && typeof value === 'object' && !Array.isArray(value)) {
sanitizeFilter(value as Record<string, unknown>);
}
}
return filter;
}For $regex specifically, validate that the pattern is not catastrophically backtracking using a safe-regex library before executing the query.
Found using agent-audit — static analysis for MCP servers. OWASP Agentic AI Top 10 reference: A03:2025 Insufficient Input/Output Validation.