api-mongo-agg-facet-bypasslisted
Install: claude install-skill NoorQureshi/ronin
# MongoDB aggregation injection — $facet allowlist bypass → cross-collection read
## When it applies
- An API runs a user-controlled **aggregation pipeline** on a fixed collection
(`db.collection('x').aggregate(userPipeline)`), usually exposed as a `pipeline`
query/body parameter for "advanced" search.
- The server defends with a **stage allowlist** — only screens the *top-level* stage
names (`$match/$project/$sort/$limit/$facet` allowed; `$lookup/$unionWith/$group/…`
rejected with something like `"invalid or disallowed pipeline stage"`).
- Tell-tale that a `pipeline` param even exists: sending the normal search term as a
Mongo **operator object** (`?q[$ne]=x`) returns a hint such as
`"Operator-form queries not accepted on 'q'. Use the 'pipeline' parameter…"`.
## Why it works
The allowlist inspects only the outermost stage keys. **`$facet` runs sub-pipelines
whose stages are never re-screened** by the app, so a disallowed read stage placed
inside a `$facet` sub-pipeline reaches MongoDB unchecked. MongoDB itself still forbids
a few stages inside `$facet` (`$out/$merge/$collStats/$indexStats/$listCatalog/$documents`),
but it **permits `$lookup` and `$unionWith`** there — and those read *other collections
in the same database*. That turns a "search our metadata" endpoint into "read any
collection in this DB".
## Method
1. **Confirm the pipeline sink & allowlist.** Baseline `?pipeline=[{"$limit":1}]` returns
docs; `?pipeline=[{"$count":"n"}]` / `$group` / top-le