How a Missing Validation Chain Led to Global Authorization Bypass

Maor Caplan
Feb 15, 2026
Overview
CASL is the go-to authorization library in the JavaScript ecosystem, used across React, Angular, Vue, and backend frameworks like NestJS to define and enforce what users can and cannot do. We are disclosing CVE-2026-1774, a Prototype Pollution vulnerability in @casl/ability versions 2.4.0 through 6.7.4 that can subvert the library's own authorization logic.
The Sink: setByPath()
CASL's extra submodule exports a utility called rulesToFields() that collects condition values from permission rules into a plain object. For example, if a rule says a user can read posts where { authorId: 12 }, the function returns { authorId: 12 } so a developer can use those as defaults when creating a new object.
Because condition keys can use dot-notation (e.g., 'author.name'), rulesToFields() relies on a helper called setByPath() to walk the dotted path and build the nested object structure. So a key like 'author.name' with value 'John' becomes { author: { name: 'John' } }.
setByPath() splits the path string on dots, walks through the object creating any missing intermediate properties along the way, and sets the final value. In vulnerable versions, it performed no validation on the property names it walked through:
This is where things break. A path like __proto__.polluted causes the function to access object['__proto__'], which in JavaScript returns Object.prototype, the base prototype that virtually all objects inherit from. Since it already exists, the || {} fallback is skipped, and the function is now holding a reference to Object.prototype. The final line then writes directly to it, poisoning every object in the entire runtime. The path constructor.prototype.x reaches the same target through the constructor chain.
Proof of Concept
If an application sources permission rules from any untrusted input, an attacker can inject a malicious condition key. The condition key '__proto__.polluted' flows through rulesToFields() into setByPath() completely unsanitized:
The modelName Variant: Authorization Bypass
The pollution primitive alone is dangerous, but the real payoff comes from targeting a specific property: modelName.
CASL identifies what type of object it is checking by reading constructor.modelName. If we pollute Object.prototype.modelName with 'all', CASL's built-in wildcard subject type, every object in the application suddenly identifies as 'all'. Applications commonly define broad rules like can('manage', 'all') for admin roles, so now every authorization check matches those wildcard rules, and every action is permitted regardless of the caller's actual role.
The attack is a single condition key:
The attacker injects a rule with condition key
__proto__.modelNameset to'all'.rulesToFields()triggerssetByPath({}, '__proto__.modelName', 'all'), writing'all'ontoObject.prototype.modelName.From this point,
constructor.modelNamereturns'all'for every object, because no normal constructor defines its ownmodelName, so JavaScript walks the prototype chain and finds the polluted value.CASL now resolves every subject as
'all', matching wildcard rules and authorizing actions that should be forbidden.

The Fix
Fixed in version 6.7.5 (December 20, 2025). The patch adds a blocklist of dangerous property names (__proto__, constructor, prototype) using a Set for fast lookup. Before walking into any property or assigning a value, setByPath() now checks the name against this blocklist and silently skips it if it matches. This closes both the __proto__.x and constructor.prototype.x pollution vectors.
Remediation
Update to v6.7.5+. This neutralizes all known prototype pollution vectors through this sink.
Sanitize external rules. If your application loads rules from untrusted sources, validate that no condition keys contain
__proto__,constructor, orprototype.Implement Runtime Monitoring: Deploy Application Detection and Response (ADR) tools that can identify unauthorized modifications to the global prototype chain in real time, as these are often clear indicators of an active exploit attempt.
Fix Commit: 39da920 | Fixed Version: @casl/ability@6.7.5 | Affected Range: @casl/ability@2.4.0 through @casl/ability@6.7.4

