A high-severity prototype pollution vulnerability affects @stablelib/cbor, a JavaScript library for decoding Concise Binary Object Representation (CBOR) data. Attackers can craft malicious CBOR payloads that hijack the prototype of decoded JavaScript objects. This happens because the decoder assigns keys directly to plain objects using bracket notation, which triggers JavaScript’s special handling of __proto__.
Any app parsing untrusted CBOR risks corrupted objects. Prototypes control inherited properties, so poisoned ones can override methods or flags across your codebase. In crypto or finance tools—where CBOR appears in protocols like IPFS, Helium, or blockchain serialization—this could bypass authorization, flip feature flags, or leak data. Check your dependencies now; the issue persists in versions up to the latest as of this advisory.
How It Works
The decoder creates a plain object with {} and sets map entries via obj[key] = value. JavaScript treats __proto__ specially: it calls the internal setter, which swaps the object’s prototype if the value is an object or null. A CBOR map with key "__proto__" and value { isAdmin: true } doesn’t add an own property. Instead, it installs an attacker-controlled prototype.
Later property lookups inherit from this prototype. Code checking obj.isAdmin sees true, even if the object lacks its own isAdmin property. This breaks assumptions in config loaders, auth middleware, or data serializers. If you merge the object elsewhere, pollution spreads like a virus.
Prototype pollution isn’t novel—it’s plagued libraries like Lodash, Handlebars, and minimist since 2018. But CBOR decoders should harden against it, especially since CBOR keys are arbitrary bytes. @stablelib/cbor, part of the stablelib suite by Matteo Collina, targets secure crypto apps. Overlooking JS prototype quirks here feels like a miss, given the library’s security focus.
Proof of Concept
Here’s the attack payload and demo. It decodes to an object inheriting isAdmin: true from a poisoned prototype:
import { decode } from "@stablelib/cbor";
// CBOR hex: a1695f5f70726f746f5fa167697341646d696ef5
// Encodes map with "__proto__": { "isAdmin": true }
const payload = new Uint8Array([
0xa1, 0x69, 0x5f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x5f,
0xa1, 0x67, 0x69, 0x73, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0xf5
]);
const obj = decode(payload);
console.log(Object.hasOwn(obj, "isAdmin")); // false
console.log(obj.isAdmin); // true
console.log(Object.getPrototypeOf(obj).isAdmin); // true
Run this in Node.js with npm i @stablelib/cbor. It confirms the pollution: own-property checks fail, but normal access succeeds.
Why This Matters and Fixes
In practice, untrusted CBOR comes from networks, APIs, or user uploads. Finance apps decoding transaction data or crypto wallets parsing signatures face real risks. Imagine an admin panel loading CBOR-serialized configs: one tainted payload grants isAdmin to any object. Or a feature flag system where pollution enables premium features for free.
Snyk rates this HIGH for good reason—CVSS likely scores 7.5+ due to chainability. Similar flaws have led to RCE in Node apps. @stablelib/cbor sees moderate use (10k+ weekly downloads), often in ethers.js ecosystems or custom serializers. If you’re in crypto/security, audit now.
Mitigate by:
- Defensive decoding: Use
Object.create(null)for maps orMapinstances. - Property checks: Always use
hasOwnPropertyorObject.hasOwn. - Freeze prototypes:
Object.setPrototypeOf(obj, null)post-decode. - Switch libs: Try
cbor-jsorcbor-x, but verify they handle this.
Library maintainers fixed similar issues elsewhere; expect a patch soon. Until then, strip dangerous keys or validate CBOR schemas strictly. Prototype pollution thrives on sloppy object handling—treat decoders like untrusted input gateways.
This vuln underscores JS’s prototype pitfalls in binary formats. CBOR’s rise in Web3 (e.g., Solana programs, Filecoin) amplifies exposure. Stay vigilant; one poisoned object unravels your security model.