Home/ Blog/ Security news/ Article
Blog · Security news

One rigged account-sync update can poison your whole app and forge an admin

CVE-2026-48170 lets one SCIM PATCH request poison Object.prototype across a Node.js app using scim-patch, risking admin forgery. Update to 0.9.1 now.

A cracked dark sphere on a mirrored floor with spreading hairline fractures

An identity provider syncing user accounts into your app is a trusted, automated channel. CVE-2026-48170, published June 22, turns that channel into a way to corrupt every object in a running Node.js process. The flaw sits in scim-patch, a small npm library that applies the account updates your identity provider sends, and it is fixed in version 0.9.1. If a service of yours uses it to handle user provisioning, this is today's work.

The library pulls roughly 108,000 downloads a week, so it is not obscure. It rates 9.1 on the CVSS scale, and the damage it allows is not data theft. It is state corruption: a single crafted update can set a property on JavaScript's shared object prototype, and from there the consequences depend on what the rest of your code trusts.

What one bad update actually does

SCIM is the standard protocol an identity provider such as Okta or Microsoft Entra uses to create and update accounts inside the apps your company runs. When someone's title changes or they join a group, the provider sends a SCIM PATCH request, and a library on your side applies that change to the user record. scim-patch is one of the common libraries that does the applying.

The bug is a prototype pollution flaw, tracked as CWE-1321. In JavaScript, almost every plain object inherits from one shared parent, Object.prototype. The advisory describes a PATCH whose value object carries a key shaped like __proto__.someProp. Instead of writing that property onto the one user being updated, the library walks the dotted path and writes it onto the shared prototype. After that single request, every plain object in the process appears to carry the property, until the process restarts.

What that buys an attacker depends on your own code, and the advisory is careful to frame these as the realistic outcomes seen in similar bugs rather than a guaranteed exploit. If any authentication or middleware check reads a flag such as actor.isAdmin off a plain object and assumes the key is absent unless set, the poisoned prototype can make that flag look true for everyone. Code that branches on obj.name or obj.type can be steered down the wrong path, and the advisory points to a real case where polluting the property a database driver uses to name prepared statements caused a denial of service. It is the same shape as a small library quietly disabling a check and making an assumption meaningless. The one certain fact is the one that matters: a single request reaches every object in the process.

The endpoint you trusted is the attack surface

The reason this deserves your attention is where the input comes from. CVSS scores the privilege required as low, because the request only needs whatever your SCIM endpoint already accepts, which for most setups is a provisioned identity-provider client. That sounds reassuring and is not. Provisioning clients use long-lived service credentials, the malicious body looks exactly like an ordinary attribute update, and the whole purpose of a SCIM endpoint is to accept JSON from a system outside your direct control. A feature you enabled for single-sign-on convenience is reachable by anyone who can drive that provisioning channel, and the request will not stand out in your logs. Trusting identity data that arrives from outside is exactly how a forged identity once walked past a signature check as admin.

There is also a timing trap for whoever investigates. The poison persists across requests, so the symptom, a sudden admin or an odd crash, can surface minutes or hours after the request that planted it. Anyone reading the logs will be staring at the wrong request. This is the kind of misplaced-trust failure we keep seeing, where one overlooked default was enough to take over a whole cluster.

Checking the key name is not enough

It is tempting to fix this by blocking the bare word __proto__. That is the brittle path, and it is the same mistake that keeps reopening bugs elsewhere. The dangerous key here was nested inside a dotted path, not sent as a top-level field, which is exactly how a naive string check gets bypassed. We watched the same shape play out when an encoded variant reopened an auth bypass that a string filter had supposedly closed. The durable fix is structural: reject the dangerous keys, __proto__, constructor, and prototype, before walking the path, or apply patches onto objects that have no prototype at all. That is the approach the maintainer took in 0.9.1.

Patch to 0.9.1, then hunt the poisoned keys

Update scim-patch to 0.9.1 now. That closes the hole. If you cannot upgrade immediately, screen incoming SCIM PATCH bodies and reject any whose value object contains __proto__, constructor, or prototype in a key, including dotted forms, at your gateway or in a small middleware before the library runs.

For detection, the request to look for is a SCIM PATCH to your user endpoint, such as /Users/:id, carrying one of those keys in its patch operations. Log and alert on it at the web application firewall or the app layer. Then, because the effect is delayed, correlate any unexplained privilege change or branching-logic crash against recent provisioning traffic rather than the request that tripped the symptom. Restarting the Node.js process clears a poisoned prototype, which is worth knowing for incident response, but it is not a fix on its own.

One last thing worth saying plainly: a SCIM endpoint is an untrusted-input boundary that most teams file under internal plumbing. It is authenticated, it is automated, and it is fed by a system you do not own. Treat the data crossing it like any other request from the outside, validate it as such, and this whole class of bug gets a lot smaller.

Topics

Frequently asked questions

What is CVE-2026-48170 in scim-patch?

CVE-2026-48170 is a critical prototype pollution flaw, scored CVSS 9.1, in the npm library scim-patch. When the library applies a SCIM PATCH whose value object contains a key like __proto__.someProp, it writes to JavaScript's shared Object.prototype, affecting every plain object in the Node.js process. It is fixed in version 0.9.1.

Which scim-patch versions are affected and what is the fix?

All versions of scim-patch up to and including 0.9.0 are vulnerable. The fix is release 0.9.1, which rejects the dangerous keys before applying a patch. Upgrade as soon as you can, because the flaw lets a single request corrupt state for every object in the process.

What can an attacker actually do with this bug?

It depends on your own code, and the advisory frames the outcomes as realistic rather than guaranteed. If an auth check reads a flag such as actor.isAdmin off a plain object, the poisoned prototype can make it true for everyone, enabling privilege escalation. It can also cause logic bypasses or denial of service.

Who can trigger the vulnerability?

Anyone who can send a SCIM PATCH request to your user endpoint. In most deployments that means a provisioned identity-provider client, which CVSS rates as low privilege. The malicious request looks like an ordinary attribute update, so it does not stand out from legitimate provisioning traffic.

How do I mitigate it if I cannot upgrade yet?

Screen incoming SCIM PATCH bodies and reject any whose keys contain __proto__, constructor, or prototype, including dotted forms, before the library runs. Restarting the Node.js process clears a poisoned prototype, but it does not fix the underlying flaw. Upgrading to 0.9.1 is the real fix.

Is CVE-2026-48170 being exploited in the wild?

There are no public reports of exploitation in the wild as of June 22, 2026, and the vulnerability is not on CISA's Known Exploited Vulnerabilities list. No public proof-of-concept exploit has been published. A patched release exists, so the priority is to upgrade before that changes.

Ready to meet the Guardians?

Deploys fast - agentless for monitoring and cloud, a lightweight agent for deep endpoint security. Just Suriq, standing watch.