GitHub's advisory database lists GHSA-pcrw-h46h-c93h (CVE-2026-125120) as a critical Mongoose flaw with CVSS 9.8 network impact. Mongoose's sanitizeFilter option is meant to neutralize query selector injection: operators like $where or nested $gt objects that should not appear in user-influenced filters. On affected versions, sanitizeFilter was not applied to countDocuments(), so a filter shaped like a normal equality query could still carry operator semantics on the count path while sibling find() calls looked protected. If you run agent backends, internal admin tools, or MCP servers on MongoDB through Mongoose, upgrade to the patched release for your major version and grep for countDocuments in code agents generated.
explainx.ai has the same advisory timing in its npm security feed; this post sticks to the Automattic/mongoose advisory text and release tags the maintainers link.
TL;DR
| Question | Answer |
|---|---|
| Am I affected? | mongoose below 6.13.11 / 7.8.12 / 8.24.3 / 9.9.2 with sanitizeFilter enabled and countDocuments() on user-influenced filters |
| Is there a fix? | Patched versions on all supported major lines (same table as advisory) |
| Severity? | Critical 9.8 — network, low complexity, no privileges per GitHub |
| Exploited in the wild? | Not stated in the advisory |
| Quick check? | npm ls mongoose in each Node project |
| Agent angle? | Pagination counts are easy for agents to scaffold and rare for humans to audit |
Mechanism: why countDocuments was different
Mongoose maps JavaScript objects to MongoDB query documents. Query selector injection happens when attacker-controlled properties become top-level operators ($or, $where, comparison objects) instead of literal field values. Mongoose's sanitizeFilter strips or rewrites patterns that look like operator injection when the feature is on.
The advisory's impact section is short and specific: sanitizeFilter was not applied for countDocuments() queries on older releases. Applications that enabled the global guard for defense in depth reasonably assumed every query helper inherited it. Pagination flows often call Model.countDocuments(filter) with the same filter object as find(filter); if only one path is sanitized, an attacker can probe whether counts leak existence of rows matching an injected selector even when list endpoints appear locked down.
Weakness: CWE-943 improper neutralization in query logic. The finder koyokr is credited on GitHub. Maintainer commits linked from the advisory (7e912af, 8b10d57, b1df045, b5003d3) land fixes across major branches; release tags 6.13.11, 7.8.12, 8.24.3, and 9.9.2 are the upgrade targets.
Attack chain (logical steps)
This is the logical chain security reviewers use for NoSQL injection; the advisory does not ship a public proof of concept.
- Attacker influences a filter object (query string parsed into JSON,
POSTbody, GraphQL argument, or nested key the server merges). - Application enables
sanitizeFilterand uses Mongoose < patched version. - Handler calls
countDocuments(filter)for totals, badges, or "results: N" headers. - Unsanitized operators reach MongoDB on the count path, potentially bypassing intended tenancy or ownership constraints encoded only in safe
find()usage. - Impact depends on schema and authz: row existence leaks, broader counts than policy allows, or paired attacks with write APIs — the advisory rates confidentiality, integrity, and availability High at 9.8.
Preview Pane-style mitigations do not apply here; this is server-side query construction.
Why agents amplify NoSQL selector bugs
Coding agents frequently emit pagination helpers in one shot: GET /items?page=1 backed by find(filter).skip().limit() plus countDocuments(filter) for a total. Humans reviewing the diff often read the find branch and skim the count line because it "only returns a number." That habit is dangerous when count accepts the same attacker-shaped object.
Agents also love spreading req.query into filters with object spread or Object.assign, which copies unexpected nested keys into MongoDB queries. A field like ?username[$ne]= becomes operator syntax unless every query entry point sanitizes consistently.
Finally, MCP tool layers sometimes expose "how many rows match X?" tools for operators without going through the same middleware stack as user-facing HTTP routes. If one code path registers the advisory plugin and another does not, you have a split brain inside one repository.
Detection ideas (application-side, not Beam)
None of these replace patching, but they help find bad patterns before production:
- Static grep:
countDocuments(beside variables namedfilter,query,where, orreq.query. - Contract tests: feed a filter object containing a benign
$whereor$gtsentinel in CI and assert the server rejects or strips it on both list and count endpoints. - Dependency policy: fail CI when
npm ls mongooseresolves below patched versions; store the allowed range inpackage.jsonoverrideswhen transitive deps lag.
Affected and patched versions
| Major line | Affected | Fixed |
|---|---|---|
| 6.x | < 6.13.11 | 6.13.11 |
| 7.x | < 7.8.12 | 7.8.12 |
| 8.x | < 8.24.3 | 8.24.3 |
| 9.x | < 9.9.2 | 9.9.2 |
Transitive dependency note: a single locked mongoose in a monorepo may serve multiple microservices. Agents that npm install inside generated folders can pin an older transitive copy even when the root lockfile is fresh — see the Node.js October 2026 security release notes for MCP servers for the same "one stale nested node_modules" pattern.
What to run today
Version inventory
npm ls mongoose
Repeat in Docker build contexts, pnpm/yarn workspaces, and template repos your coding agent clones. Compare against the table above.
Upgrade
Bump mongoose in package.json to at least the patched version for your major line, reinstall, run your test suite, and redeploy. If agents maintain lockfiles, commit the lockfile change and re-run generation jobs so scaffolded services do not revert.
Advisory middleware (bridge only)
GitHub documents this plugin until every instance is patched:
mongoose.plugin(function () {
this.schema.pre('countDocuments', function () {
mongoose.sanitizeFilter(this.getFilter());
});
});
Place it once at application bootstrap. It does not replace upgrading; future Mongoose APIs could add other gaps the plugin does not cover.
Code review targets
Search your tree for countDocuments, estimatedDocumentCount, and wrapper helpers agents added for "total hits". Pair each call with the source of the filter object. Cross-check with fastapi-code-generator import-time RCE lessons: agents love pagination, humans rarely read decorators or count helpers.
Rolling upgrades through agent-heavy repos
- Pin
mongooseto the minimum patched release in the rootpackage.json. - Run agents only after lockfile refresh so they do not revert pins while "fixing" install errors.
- Re-scan generated services in
services/*folders agents maintain; monorepos often hide an older pin two directories down. - Log resolved versions in deployment manifests (container image SBOM or
npm lsoutput artifact) so incident response can answer "were we vulnerable?" without guessing.
After you patch: what compromise might look like
The advisory does not describe in-the-wild exploitation, but NoSQL injection incidents often leave:
- Unexpected count queries in MongoDB profiler logs with operator keys in filter documents.
- Authentication bypass symptoms when counts leak existence of admin rows across tenants.
- Automated scanners hitting
/countor GraphQLtotalfields more aggressively than list routes.
If you suspect abuse, rotate database credentials and JWT signing keys for services that used vulnerable counts on internet-exposed admin APIs—not because the bug exfiltrates secrets directly, but because bypassed counts often pair with follow-on read APIs.
What this means if you run AI coding agents
- MCP and tool backends: Many internal MCP servers are thin Express or Fastify layers over Mongoose models. An agent that exposes "count users matching …" hands attackers a direct line to the vulnerable API if versions are stale.
- Generated CRUD: When an agent builds admin dashboards from a schema, it often copies the same
req.queryobject into bothfindandcountDocuments. Treat those PRs as security-sensitive even when the agent marks them "read-only." - Secrets elsewhere: This bug is not credential theft by itself. It is data-plane logic abuse. Combined with environment leaks after provider breaches, weak counts can help attackers prioritize which stolen API keys still map to live tenants.
- LLM apps with Mongo: If you embed Protect LLM Feature guidance atop a Mongoose store, the prompt-injection layer does not sanitize Mongo operators — patch the driver layer first.
What Beam can and cannot do here
Beam watches local agent tool use (files, shells, MCP configs) on developer machines. It does not instrument Mongoose queries inside your deployed Node services.
Where Beam still helps:
- PreToolUse hooks can block reckless
npm install mongoose@olddowngrades when agents editpackage.json. - Heuristic skill/MCP scans flag skills that pipe untrusted specs into code generators — adjacent supply-chain risk to agent-written pagination code.
- Redaction before local event storage limits accidental connection strings in logs agents echo.
Limits: no runtime NoSQL firewall; no guarantee every generated service runs a Beam-connected harness.
How Mongoose sanitizeFilter is supposed to work
Mongoose documents sanitizeFilter as a global option that rewrites query objects before they reach the driver. The intent is to block patterns where user input becomes top-level operators instead of literal equality on named fields. When it works on every entry point, developers can accept structured filters from JSON with fewer hand-rolled allow lists.
The CVE is narrow but painful: one high-traffic API (countDocuments) missed the hook. Security reviews that checklist only find, findOne, updateMany, and aggregation pipelines would still miss the gap. That is why the maintainer workaround uses pre('countDocuments') middleware rather than telling everyone to stop counting.
For readers comparing ORMs: SQL tools face similar split-brain problems when ORMs sanitize SELECT builders but not COUNT(*) helpers generated from the same user map. The remediation pattern is the same—enumerate every query method that accepts a filter object.
Supply-chain timeline for this advisory
| Date | Event |
|---|---|
| Nov 2, 2026 | GitHub publishes GHSA-pcrw-h46h-c93h |
| Nov 10, 2026 | Advisory metadata updated; npm patched lines stable |
| Ongoing | NVD/CVE-2026-125120 indexing; no KEV listing at publication time |
Because severity is 9.8 with network, no privileges in GitHub's CVSS v3.1 vector, treat urgency like an unauthenticated read/write class bug even before CISA adds catalog entries.
Frequently asked questions
What is CVE-2026-125120?
Critical Mongoose query-logic flaw: sanitizeFilter skipped on countDocuments(). See GHSA-pcrw-h46h-c93h.
Which versions are fixed?
6.13.11, 7.8.12, 8.24.3, and 9.9.2 on their respective major lines.
Do I need sanitizeFilter enabled for impact?
The gap bites when you turned the guard on and trusted it globally; without sanitizeFilter, other injection paths may already exist — patch anyway.
Any in-the-wild exploitation?
The GitHub advisory does not claim active exploitation. Treat as patch-now critical regardless.
How does this compare to SQL injection in agent SQL tools?
Same class — attacker-controlled structure in a query API — but MongoDB operator syntax and Mongoose helpers differ. Audit both if agents touch SQL and Mongo.
Related reading
- Node.js October 2026 security release: MCP servers and agent tools
- fastapi-code-generator import-time OpenAPI RCE
- Protect an LLM feature from prompt injection and key theft
- Your first 24 hours after a vendor breach
- GPUStack SAML callback auth bypass (another agent-adjacent auth bug pattern)
- Beam landing: what Beam watches
Version numbers and advisory text reflect GitHub and Mongoose releases as of 2026-11-10.

