fix(security): defense-in-depth against already-polluted Object.prototype in formDataToJSON#7413
Conversation
|
Hi - this has been outstanding for a while now, should still be good. |
thanks i will check this out asap |
17122a4 to
2f78311
Compare
|
@jasonsaayman I dont think there is anything outstanding on this for me? Didnt hear back from your last comment |
|
Thanks for the patch. The code change is the right pattern and worth landing, but a couple of things need fixing before it can go in, and the framing around what it actually fixes needs to change too. I traced and reproduced both the claimed and actual behaviour against the current v1.x HEAD. The description says this prevents pollution via The new test passes against unpatched code too, so it isn't covering the path your change affects. The real benefit of the change is defence-in-depth against an already-polluted runtime. If something else has set That's the scenario these guards face, and that's what the test should cover. A few more things to fix:
it('should not write through to inherited objects on Object.prototype', () => {
Object.prototype.injected = { hijack: true };
try {
const formData = new FormData();
formData.append('injected.hijack', 'STOLEN');
const result = formDataToJSON(formData);
expect(result.injected).toEqual({ hijack: 'STOLEN' });
expect(Object.prototype.injected.hijack).toEqual(true);
} finally {
delete Object.prototype.injected;
}
});This would fail against the current HEAD and pass with your one-line change applied. Could you fold the fix into |
Replace falsy check with hasOwnProp in the intermediate-path branch of formDataToJSON's buildPath to prevent write-through into inherited objects. Without this patch, if Object.prototype is already polluted (e.g. via a third-party library or earlier vulnerability), user-supplied FormData paths like 'injected.hijack' traverse the inherited object and mutate Object.prototype in place. With hasOwnProp, the inherited slot is shadowed by a new own property, keeping writes local to the result. This is defense-in-depth: the existing __proto__ guard blocks direct prototype injection, while this change prevents exploitation of an already-polluted prototype chain. Closes axios#7209
2f78311 to
aa3b45a
Compare
|
Hi - thanks for the detailed feedback. Please see below - Updated: Rebased onto current v1.x (was 164 commits behind). Branch is now up to date with the test/ → tests/ migration and Vitest. |
Summary
Replace the falsy check with hasOwnProp in the intermediate-path branch of formDataToJSON's buildPath to prevent write-through into inherited objects.
Problem
If Object.prototype is already polluted (e.g. via a third-party library or an earlier vulnerability), user-supplied FormData paths like injected.hijack can traverse the inherited object and mutate Object.prototype in place. The original check:
if (!target[name] || !utils.isObject(target[name]))
follows the prototype chain — so if Object.prototype.injected exists and is truthy/an object, the code walks into it and writes there.
Fix
if (!utils.hasOwnProp(target, name) || !utils.isObject(target[name]))
With hasOwnProp, the inherited slot is shadowed by a new own property on the result object, keeping writes local. No prototype mutation occurs.
Context
This is defense-in-depth, not a standalone CVE fix. The existing proto guard blocks direct prototype injection; this change prevents exploitation of an already-polluted prototype chain. It does not claim to fix constructor.prototype pollution — that path was already handled by the existing CVE test.
Test
Added a write-through regression test that:
Manually pollutes Object.prototype.injected = { hijack: true }
Sends injected.hijack=STOLEN through formDataToJSON
Asserts the result gets its own { hijack: 'STOLEN' } (not appended to the inherited object)
Asserts Object.prototype.injected.hijack remains true (unmutated)
The test fails on unpatched HEAD and passes with the one-line change.
Closes #7209