Skip to main content

scim-patch CVE-2026-48170

| EUVDEUVD-2026-54686 CRITICAL
Improperly Controlled Modification of Object Prototype Attributes (Prototype Pollution) (CWE-1321)
2026-06-22 https://github.com/thomaspoignant/scim-patch GHSA-9m6g-wc8r-q59c
9.1
CVSS 3.1 · Vendor: https://github.com/thomaspoignant/scim-patch
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Obtain SCIM provisioning credentials
  2. Delivery
    Send PATCH /Users/:id with __proto__ key in value
  3. Exploit
    scim-patch walks key onto Object.prototype
  4. Execution
    Object.prototype.isAdmin set process-wide
  5. Persist
    Subsequent request authorizes as admin
  6. Impact
    Privilege 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 scimPatch() on a value (or path) supplied by the request body, which is the normal usage pattern for any SCIM 2.0 PATCH endpoint such as /Users/:id or /Groups/:id. The attacker must hold whatever credentials that SCIM endpoint requires - typically a provisioned IdP/service-account token (CVSS PR:L), not anonymous access. Exploitation is limited where downstream code uses Object.create(null), Maps, or class instances with own-property checks instead of plain objects, where the Node process is started with --frozen-intrinsics, or where a reverse proxy/WAF strips __proto__/constructor/prototype substrings from request bodies; no user interaction (UI:N) and no specific OS/platform beyond a stock Node runtime are required.

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 Object.create(null) for security-sensitive state may see only DoS-style anomalies. EPSS, KEV status and SSVC decisions were not provided in the input, and the package is a low-prevalence niche SCIM library, so this is most likely a high-impact-if-deployed issue rather than mass-exploitation material. Publicly available exploit code exists (the GHSA advisory and merged regression tests demonstrate the payload), so any internet-exposed SCIM endpoint backed by scim-patch <= 0.9.0 should be patched without delay.

Exploit ScenarioDerived

A compromised or malicious identity provider (or any tenant with provisioning credentials) sends PATCH /Users/some-id with body [{"op":"add","path":"name","value":{"__proto__.isAdmin":true}}]; scim-patch walks the dotted key, lands on Object.prototype, and sets isAdmin=true for every plain object in the Node process. Subsequent unrelated requests that authorize on req.user.isAdmin (or branch on similarly absent properties) then succeed as administrators until the container restarts. The proof-of-concept in the GHSA advisory and the regression tests added in commit 260f9cd2 directly demonstrate this shape.

Act Now Prioritize within 24-48h. Why: CRITICAL severity.
Patch available 0.2% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – 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

scim-patch < 0.9.1

Affected Packages

npm scim-patch fix: 0.9.1

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

Vendor (https://github.com/thomaspoignant/scim-patch) PRIMARY 9.1 CRITICAL
AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L
vuln.today AI 9.1 CRITICAL

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.

3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L

Primary rating from Vendor (https://github.com/thomaspoignant/scim-patch).

CVSS VectorVendor: https://github.com/thomaspoignant/scim-patch

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
Low
Integrity
High
Availability
Low

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 HEAD 871b1e2)
  • Attack vector: Network - sent as part of a normal SCIM PATCH /Users/:id request 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.prototype mutation) 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.id etc. 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 .:

ts
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):

ts
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 obj is always true, so the fresh-object branch is skipped.
  • obj = obj["__proto__"] now points to Object.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:

ts
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:

ts
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.

Executive ViewAI

Why It Matters

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.

Business Impact: Severe
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.2%
Top 84% exploitable
Prevalence
Low
niche SCIM provisioning library with modest npm adoption
Threat Intel
1
1 source
Social Media
2
Sightings
Data Quality
6 sources
Confidence: High

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.

SignalValue
Severity CRITICAL
CISA KEV No
Public POC No
EPSS 0.2%
Patch available Yes
Active threat No
Deciding rule
CRITICAL severity
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
No
Human Impact
Very High

Priority Score

46
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +46
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Privilege escalation occurs when an attacker leverages flaws in access control mechanisms to gain permissions beyond what they were originally granted.

CVE-2026-34621 HIGH POC
8.6 Apr 11

Prototype pollution in Adobe Acrobat Reader versions 24.001.30356, 26.001.21367 and earlier enables arbitrary code execu

CVE-2021-23450 CRITICAL POC
9.8 Dec 17

All versions of package dojo are vulnerable to Prototype Pollution via the setObject function. Rated critical severity (

CVE-2024-56059 CRITICAL
9.8 Dec 18

Prototype pollution in the farinspace Partners WordPress plugin (versions up to and including 0.2.0) enables remote unau

CVE-2021-23383 CRITICAL POC
9.8 May 04

The package handlebars before 4.7.7 are vulnerable to Prototype Pollution when selecting certain compiling options to co

CVE-2020-7788 CRITICAL POC
9.8 Dec 11

This affects the package ini before 1.3.6. Rated critical severity (CVSS 9.8), this vulnerability is remotely exploitabl

CVE-2020-28271 CRITICAL POC
9.8 Nov 12

Prototype pollution vulnerability in 'deephas' versions 1.0.0 through 1.0.5 allows attacker to cause a denial of service

CVE-2023-38894 CRITICAL POC
9.8 Aug 16

A Prototype Pollution issue in Cronvel Tree-kit v.0.7.4 and before allows a remote attacker to execute arbitrary code vi

CVE-2024-24292 CRITICAL POC
9.8 Mar 28

A Prototype Pollution issue in Aliconnect /sdk v.0.0.6 allows an attacker to execute arbitrary code via the aim function

CVE-2023-26121 CRITICAL POC
10.0 Apr 11

All versions of the package safe-eval are vulnerable to Prototype Pollution via the safeEval function, due to improper s

CVE-2021-23449 CRITICAL POC
10.0 Oct 18

This affects the package vm2 before 3.9.4 via a Prototype Pollution attack vector, which can lead to execution of arbitr

CVE-2024-39011 CRITICAL POC
9.8 Jul 30

Prototype Pollution in chargeover redoc v2.0.9-rc.69 allows attackers to execute arbitrary code or cause a Denial of Ser

CVE-2024-38988 CRITICAL POC
9.8 Mar 28

alizeait unflatto <= 1.0.2 was discovered to contain a prototype pollution via the method exports.unflatto at /dist/inde

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy