-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
superjson throws on constructor property #279
Comments
This error comes from here: In your case, there's no transform happening so I don't think there's a true risk for prototype pollution. Let me see if we can relax this ... |
Does this mean that the parsing side uses these properties to "guess" the original constructor without any other marker in the metadata? That feels very brittle to me. |
No, the parsing side reads the metadata to find the original prototype / constructor. The exception you're seeing was added in response to #267 - we're trying to catch invalid inputs on parsing, not on serialisation. Otherwise you'll store something in your database, only to realise that you can't retrieve it anymore when parsing. |
It's a bit counterintuitive, because I can perfectly parse superjson which it's unable to serialize again. const parsed = superjson.parse(
'{"json":{"schema":{"constructor":{"type":"string"}},"createdAt":"2023-12-06T10:49:28.911Z"},"meta":{"values":{"createdAt":["Date"]}}}'
)
console.log(typeof parsed);
// 'object'
console.log(superjson.stringify(parsed));
// Error: Detected property constructor. This is a prototype pollution risk, please remove it from your object. |
This specific value should be stringifiable, I agree. I believe the open PR already addressed that, will add a test case 👍 |
Test case is over here, and it's green: 5fa55b7 The important thing here is that |
I may still be misunderstanding the attack vector fully but I'm reading this section. Do I understand that when assigning to nested properties of In pseudo code function getSafeNested(obj, [next, ...rest]) {
if (!Object.prototype.hasOwnProperty.call(obj, next)) {
throw new Error('checkmate hackers')
}
return rest.length === 0 ? obj[next] : getSafeNested(obj[next], rest)
}
function setSafeNested(obj, [next, ...rest], value) {
if (!Object.prototype.hasOwnProperty.call(obj, next)) {
throw new Error('checkmate hackers')
}
if (rest.length > 0) {
setSafeNested(obj[next], rest, value)
} else {
obj[next] = value
}
}
for (const [src, tgts] of Object.entries(serialized.meta.referentialEqualities)) {
const srcValue = getSafeNested(serialized.json, src.split('.'))
for (const tgt of tgts) {
setSafeNested(serialized.json, tgt.split('.'), srcValue)
}
} Assuming you already considered this, what am I missing? |
I have not considered this yet, actually. If we can prevent prototype pollution with this, i'd be happy to! @Janpot would you be willing to work on a PR with this? |
I won't in the near future, but I may pick this up at some point if it doesn't get implemented. |
@Skn0tt To be honest, I'm having a bit of trouble replicating the original prototype pollution. Did they ever provide an actual payload that triggers it? |
Yes, there were some test cases added for it: 0d68cd5#diff-258035e5968f6bf645400d417f310218d7d9a9a10606a3c34e7f55db193f58f3 |
Yep, I noticed. You'd think they'd fail if I remove the check for forbidden properties. But the prototype doesn't seem to get polluted in the test, this assert passes: https://github.com/blitz-js/superjson/pull/284/files#diff-258035e5968f6bf645400d417f310218d7d9a9a10606a3c34e7f55db193f58f3L1125 |
I see you only left the test for |
I understood that the specific CVE makes use of prototype pollution in Node.js. I ran the tests in Node.js. The CI runs on Node.js as well so I don't see why it wouldn't be a proper target. In Node.js I can pollute the prototype manually with: {}.__proto__.foo = 'hello';
console.log({}.foo)
// "hello" I've been trying all kinds of ways to trigger a prototype pollution in |
I've tried to replicate one, but also couldnt' come up with something that successfully pollutes, but I think i'm very close. I pushed an example to your PR. When you attach a debugger, you'll see that once it gets to this line: it will have the |
The |
superjson recently started throwing on values that contain a constructor property:
We're transferring values that contain jsonschemas that describe user generated typescript interfaces. Therefore we don't have control over which property names are in our objects, but we do know they are plain javascript objects.
Any way this restriction can be relaxed again?
The text was updated successfully, but these errors were encountered: