Feb 12, 2026·6 min read·87 visits
The bouncer is drunk. @casl/ability versions < 6.7.5 contain a flaw in the `setByPath` utility that allows writing to `__proto__`. This enables attackers to pollute the global Object prototype, turning a library meant for restricting access into a tool for granting total control.
A critical Prototype Pollution vulnerability in the popular authorization library @casl/ability allows attackers to corrupt the Object prototype. By supplying malicious rule conditions, an attacker can bypass security checks, cause Denial of Service, or potentially achieve Remote Code Execution (RCE) in Node.js environments.
You know what's tragically ironic? Authorization libraries are supposed to be the digital bouncers of your application. They stand at the function calls, checking IDs, ensuring 'User A' doesn't touch 'Resource B'. They are the trusted gatekeepers. But what happens when the bouncer leaves the back door propped open with a brick? That is CVE-2026-1774.
CASL (@casl/ability) is the go-to isomorphic library for handling permissions in the JavaScript ecosystem. It's elegant, powerful, and apparently, until recently, fatally trusting of input. The vulnerability lies deep within the extra module, specifically in how it parses field paths.
If you are using this library to parse user-defined rules—common in multi-tenant SaaS apps where admins define permissions—you aren't just vulnerable. You are handing attackers a loaded gun and pointing it at your own foot. This isn't just a bypass; it's a full-system compromise waiting to happen via Prototype Pollution.
The vulnerability hides in a utility function called setByPath. Developers love these little helpers. They take a string like 'user.address.zip' and magicaly hydrate a nested object structure. It saves time. It feels clean. It's also a minefield if you don't watch where you step.
The logic inside setByPath uses a .reduce() loop to traverse the object based on the dot-notation string. It looks innocent enough: split the string, walk the object, create properties if they don't exist. The fatal flaw? It didn't care what property it was walking.
When the function encounters the forbidden token __proto__, it doesn't stop. It treats it like any other key. It traverses up the prototype chain, setting the accumulator reference to Object.prototype. Once the loop lands there, the next key in the path is written directly to the global prototype. In JavaScript, modifying Object.prototype is like poisoning the town well—everyone who drinks from it gets sick. Every object created thereafter will inherit your malicious property.
Let's look at the diff. This is where the developer realized they messed up. The vulnerability existed because the code blindly assigned values to whatever key was in the path array.
The Vulnerable Logic:
// The code that broke the world
export function setByPath(object: AnyObject, path: string, value: unknown): void {
let ref = object;
const keys = path.split('.');
// No validation. Just vibes.
ref = keys.reduce((res, prop) => {
res[prop] = res[prop] || {};
return res[prop] as AnyObject;
}, object);
ref[lastKey] = value;
}The Fix (v6.7.5):
In commit 39da920ec1dfadf3655e28bd0389e960ac6871f4, the maintainers added a blocklist. It's not the most elegant solution (blocklists rarely are), but it stops the immediate bleeding.
// The sanity check
const FORBIDDEN_PROPERTIES = new Set(['__proto__', 'constructor', 'prototype']);
export function setByPath(object: AnyObject, path: string, value: unknown): void {
// ...
ref = keys.reduce((res, prop) => {
// If the attacker tries to climb the prototype chain, we just ignore them.
if (FORBIDDEN_PROPERTIES.has(prop)) return res;
res[prop] = res[prop] || {};
return res[prop] as AnyObject;
}, object);
// ...
}> [!NOTE]
> While this fixes the specific gadget in setByPath, it serves as a reminder: anytime you accept a key/path from a user and use it to access an object, you are playing with fire.
So, how do we weaponize this? The vulnerable function is often triggered via rulesToFields(). This is used to convert CASL rules into database queries (like MongoDB criteria). If your application allows users to store or define their own permissions (e.g., "I want to allow access to Posts where condition X meets Y"), you are cooked.
Here is a realistic attack chain:
Proof of Concept:
import { rulesToFields } from '@casl/ability/extra';
import { defineAbility } from '@casl/ability';
// The Trojan Horse
const maliciousAbility = defineAbility((can) => {
// "I can read Posts if... oh, by the way..."
can('read', 'Post', {
'__proto__.isAdmin': true,
'__proto__.toString': 'Checking permissions... JK broken.'
});
});
// The trigger
rulesToFields(maliciousAbility, 'read', 'Post');
// The Verification
if (({}).isAdmin) {
console.log("pwnd: access granted to everyone.");
}Once Object.prototype.isAdmin is true, any subsequent check in your code like if (user.isAdmin) might accidentally return true if the user object doesn't have its own isAdmin property. You just promoted every user in the database to Administrator.
You might wonder why this gets a near-perfect 9.8 score. "It's just adding properties to objects," you say. Wrong. In Node.js environments, Prototype Pollution is often a stepping stone to Remote Code Execution (RCE).
There are known "gadgets" in popular libraries (like generic template engines, child_process spawning logic, or argument parsers) that look for specific properties on objects. If they don't find them, they check the prototype. By polluting Object.prototype, an attacker can inject malicious configurations into unrelated system calls.
For example, polluting shell, env, or execPath can trick child_process.spawn into executing arbitrary binaries. Even without RCE, overwriting toString or valueOf will almost certainly crash the application (DoS), causing a complete outage.
If you are running @casl/ability versions 2.4.0 through 6.7.4, you need to update immediately. Version 6.7.5 is the patched release. Go check your package-lock.json right now. I'll wait.
Remediation Steps:
npm install @casl/ability@latestMap instead of Object for dictionaries where keys are user-controlled.If you cannot upgrade for some reason (legacy constraints?), you can try to mitigate this at the runtime level by freezing the prototype early in your application boot process:
// The nuclear option
Object.freeze(Object.prototype);Be warned: this might break other libraries that legitimately modify the prototype (though they shouldn't be doing that anyway).
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
@casl/ability Stalniy | >= 2.4.0 < 6.7.5 | 6.7.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1321 |
| Attack Vector | Network |
| CVSS Score | 9.8 (Critical) |
| EPSS Score | 0.00022 (Low Probability) |
| Exploit Status | PoC Available |
| Impact | RCE / DoS / Auth Bypass |
The application does not properly control which attributes can be modified in the prototype of an object, allowing an attacker to manipulate the prototype chain.
CVE-2026-48861 is a client-side HTTP request-line CRLF (Carriage Return Line Feed) injection vulnerability in the popular Elixir HTTP client library, Mint. The vulnerability permits HTTP Request Splitting and HTTP Request Smuggling when an application forwards untrusted, attacker-controlled inputs to Mint's HTTP client requests as either the HTTP request method or target. By embedding CRLF characters within these parameters, an attacker can terminate the request line prematurely, inject malicious headers, or pipeline entirely independent requests. These smuggled requests are then processed by upstream or downstream proxy servers as separate HTTP queries on the same TCP connection. While Mint version 1.7.0 introduced target validation to secure the request target, the HTTP request method parameter remained completely unvalidated. This flaw allows attackers to bypass routing filters, access restricted internal APIs, or poison HTTP caches under default configurations.
An Inconsistent Interpretation of HTTP Requests (HTTP Request/Response Smuggling) vulnerability in the Elixir Mint HTTP client allows attacker-controlled HTTP/1 servers to desynchronize response framing on shared connections due to over-lenient parsing of sign-prefixed Content-Length headers.
An allocation of resources without limits or throttling vulnerability in Elixir Mint allows an attacker-controlled HTTP/2 server to exhaust memory in a Mint client. The vulnerability is exploited by sending a HEADERS frame without the END_HEADERS flag followed by an infinite stream of CONTINUATION frames. Because the client lacks limits on the incoming header-block accumulator, the client continuously consumes memory until an out-of-memory crash occurs.
CVE-2026-48596 is an Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting, CWE-113) in the Elixir Tesla HTTP client. The flaw resides in how multipart content-type parameters are joined and serialized, enabling attackers to inject arbitrary headers or split HTTP requests when applications pass untrusted inputs to the parameters of multipart uploads.
An improper handling of highly compressed data (decompression bomb) vulnerability exists in the Elixir Tesla HTTP client when utilizing response decompression middlewares. By serving highly compressed responses or stacked content-encoding headers, a malicious server can cause arbitrary heap exhaustion, leading to a denial of service (DoS) crash in the BEAM virtual machine.
A high-severity security vulnerability in Elixir's Tesla HTTP client library (CVE-2026-48595) allows unauthenticated remote attackers to harvest sensitive credentials, including Authorization headers and cookies. The flaw resides in the 'Tesla.Middleware.FollowRedirects' component, which performs case-sensitive lookups when stripping credentials during cross-origin redirects. Because HTTP headers are case-insensitive by RFC specifications, standard canonical casing (e.g., 'Authorization') bypasses the lowercase-only blocklist, leaking tokens to untrusted external redirect destinations.