ApostropheCMS: Authenticated projection-exclusion prototype-member deletion in @apostrophecms/db-connect causes process-wide denial of service
CVE-2026-113666 · GHSA-j5rq-xfvr-p969 · published 20 Oct 2026
How serious is it
- Severity: high, CVSS score 7.1 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-113666?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
@apostrophecms/db-connect(npm) < 1.1.0apostrophe(npm) < 4.33.0
How to check
- Run
npm ls @apostrophecms/db-connectin your project to see the installed version, including copies pulled in by other packages. - Run
npm ls apostrophein your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- On < 1.1.0: Upgrade
@apostrophecms/db-connectto 1.1.0 or later, commit the lockfile and redeploy. - On < 4.33.0: Upgrade
apostropheto 4.33.0 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Summary
`@apostrophecms/db-connect` ships an in-memory MongoDB emulation used by its SQLite, PostgreSQL, and multipostgres adapters. Its `unsetNestedField` helper walks an attacker-controlled dotted field path with no segment validation and calls `delete` on the resolved property. A low-privileged authenticated user (any role holding `view-draft`, which every editor and contributor has; reduced to any logged-in user when `guestApiAccess` is enabled) can send a piece-type REST GET request whose `project` query builder contains an exclusion key such as `constructor.prototype.hasOwnProperty`. On a SQLite or PostgreSQL adapter this key reaches `unsetNestedField`, which resolves `Object.prototype` and deletes a shared runtime method. Because that method is shared across the whole Node process, the deletion breaks request handling for every tenant and user until the process is restarted, giving a single request a persistent denial of service.
## Root Cause
The projection query builder laundens only projection values, never projection keys, so an arbitrary dotted key survives to the database layer. On the SQLite and PostgreSQL adapters an exclusion projection is applied in JavaScript by `applyProjection`, which for each excluded field calls `unsetNestedField(result, field)`. `unsetNestedField` splits the field on `.` and walks the resulting segments with no guard: unlike its sibling `setNestedField`, it does not even block a literal `__proto__`, and neither helper blocks `constructor` or `prototype`. Because the working object is a plain object produced by `JSON.parse(JSON.stringify(doc))`, its `constructor` resolves to the global `Object`, so a path of `constructor.prototype.<member>` reaches the shared `Object.prototype` and deletes a core, configurable method. The violated invariant is that untrusted input must never be able to traverse into and mutate shared prototype objects.
```text
Affected functions (repository-relative paths):
- unsetNestedField packages/db-connect/lib/shared.js:119-129 (no segment guard; delete on resolved property)
- applyProjection packages/db-connect/lib/shared.js:164-216 (exclusion branch calls unsetNestedField per excluded key)
Reachability driver (consumer on a SQL adapter):
- project.launder / project.finalize packages/apostrophe/modules/@apostrophecms/doc-type/index.js:2003-2103
- getRestQuery packages/apostrophe/modules/@apostrophecms/piece-type/index.js:1139-1157
- SQLite adapter applyProjection call packages/db-connect/adapters/sqlite.js:906,980
```
## Impact
Prerequisites: an authenticated account holding `view-draft` (every editing role; any logged-in user when `guestApiAccess: true`), and a consumer configured to use a SQLite, PostgreSQL, or multipostgres adapter. The default `mongodb` adapter hands the projection to the real MongoDB driver and is not affected. With those conditions, a single crafted GET request deletes an attacker-chosen shared prototype method (for example `Object.prototype.hasOwnProperty`, `toString`, or an `Array.prototype` member). The demonstrated capability is a persistent, process-wide denial of service: after the deletion, ordinary calls such as `obj.hasOwnProperty(...)` throw across Apostrophe, its dependencies, and the database driver, so the affected Node process cannot service requests for any user until it is restarted. The attacker chooses which member to remove, and the corruption of shared runtime state persists for the life of the process.
## Proof of Concept
`The finding was reproduced by executing the shipped helper directly against the published db-connect source; a live consumer requires a SQLite or PostgreSQL adapter, an authenticated view-draft session, and at least one matching document.`
1. Authenticate as any user with `view-draft` on a site whose database adapter is `sqlite`, `postgres`, or `multipostgres`.
2. Issue a piece-type REST read with an exclusion projection whose key targets a shared prototype method.
3. Observe that subsequent requests fail process-wide because the shared method was deleted.
```text
GET /api/v1/@apostrophecms/article?project[constructor.prototype.hasOwnProperty]=0
Cookie: <authenticated view-draft session>
```
```text
safe benign exclusion {title:0} leaves Object.prototype.hasOwnProperty intact
VULNERABLE exclusion projection deleted Object.prototype.hasOwnProperty (callableBefore=true, brokeEveryObject=true)
VULNERABLE exclusion projection deleted an arbitrary Object.prototype method (__pocMarker)
```
## Attack Chain
1. **Exposure:** An authenticated `view-draft` user calls the piece-type REST GET endpoint `GET /api/v1/@apostrophecms/<piece-type>`, which is reachable by any editing role.
2. **Control:** The query string `project[constructor.prototype.hasOwnProperty]=0` is parsed by `qs` (with `allowDots` off) into a single own key with literal dots, `{ 'constructor.prototype.hasOwnProperty': '0' }`.
3. **Path:** `getRestQuery` applies the query builders first; `project.launder` copies the key verbatim and coerces only the value to `false`; `project.finalize` skips the falsy value with `if (!val) continue` and preserves the key; the cursor stores it as the projection.
4. **Guard:** The `publicApiProjection` override that would discard the attacker projection runs only when `canAccessApi(req)` is false, so it is skipped for the authenticated caller; no key allowlist, segment check, or prototype guard exists anywhere on the path.
5. **Primitive:** On the SQLite or PostgreSQL adapter, `applyProjection` takes the exclusion branch and calls `unsetNestedField(result, 'constructor.prototype.hasOwnProperty')`, which resolves `Object.prototype` and executes `delete Object.prototype.hasOwnProperty`.
6. **Result:** A shared prototype method is removed for the entire Node process, so request handling throws for all users and tenants until restart.
## Bypass Evidence
The relevant protections were examined individually and none blocks this p…
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/apostrophecms/apostrophe/security/advisories/GHSA-j5rq-xfvr-p969
- github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
- github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
- github.com/apostrophecms/apostrophe/releases/tag/@apostrophecms/db-connect@1.1.0
- github.com/apostrophecms/apostrophe/releases/tag/apostrophe@4.33.0
- github.com/advisories/GHSA-j5rq-xfvr-p969
Merged from GitHub Advisory Database. Last updated 23 Oct 2026. All vulnerabilities · RSS · JSON