Bug
An own __proto__ key does not survive a round-trip — it is silently renamed to __proto_, and its value is lost:
const { encode, decode } = require('cbor-x');
const value = JSON.parse('{"__proto__": 1, "a": 2}'); // own enumerable __proto__
Object.keys(decode(encode(value))); // ["__proto_", "a"] ← key renamed
Object.getOwnPropertyDescriptor(decode(encode(value)), '__proto__'); // undefined
safeKey (in decode.js) rewrites __proto__ to __proto_ to avoid prototype pollution. That avoids pollution, but corrupts the data — the same issue raised for the sibling library in kriszyp/msgpackr#159, where the agreed direction was to use Object.defineProperty so the key becomes a real own property (as JSON.parse/structuredClone do).
Heads-up for the fix
I tried the defineProperty approach locally (a setKeyValue helper at the object[safeKey(key)] = … sites + a computed ["__proto__"] key in the compiled record reader). It fixes every reader path, but a fuzz run surfaced one interaction worth flagging: when a __proto__ value participates in the value-sharing extension (tag 28), the shared-reference path does Object.assign(target, targetProperties) (decode.js ~L1096), and target can end up being the shared value itself (e.g. a Uint8Array), so Object.assign throws Cannot set property length … which has only a getter. So the fix needs to account for that sharing path, not only the key assignment.
I can put together a PR if useful — but since it touches the decode hot path and you've already weighed in on the approach in #159, an issue with the repro and the sharing caveat seemed the better first step.
Bug
An own
__proto__key does not survive a round-trip — it is silently renamed to__proto_, and its value is lost:safeKey(indecode.js) rewrites__proto__to__proto_to avoid prototype pollution. That avoids pollution, but corrupts the data — the same issue raised for the sibling library in kriszyp/msgpackr#159, where the agreed direction was to useObject.definePropertyso the key becomes a real own property (asJSON.parse/structuredClonedo).Heads-up for the fix
I tried the
definePropertyapproach locally (asetKeyValuehelper at theobject[safeKey(key)] = …sites + a computed["__proto__"]key in the compiled record reader). It fixes every reader path, but a fuzz run surfaced one interaction worth flagging: when a__proto__value participates in the value-sharing extension (tag 28), the shared-reference path doesObject.assign(target, targetProperties)(decode.js ~L1096), andtargetcan end up being the shared value itself (e.g. aUint8Array), soObject.assignthrowsCannot set property length … which has only a getter. So the fix needs to account for that sharing path, not only the key assignment.I can put together a PR if useful — but since it touches the decode hot path and you've already weighed in on the approach in #159, an issue with the repro and the sharing caveat seemed the better first step.