Skip to content
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

Fix prototype pollution in utilities.js #301

Merged
merged 2 commits into from
Feb 2, 2022

Conversation

zwade
Copy link
Contributor

@zwade zwade commented Feb 1, 2022

Hi! Last weekend I found a low-impact prototype pollution in this module. It's only problematic in conjunction with certain antipatterns, but it is probably good to fix if possible. This PR makes a change that will fix it under normal usage, along with a few tests to verify that it remains fixed.

A couple of caveats, the first being that it will not address the problem if the caller manually sets req.files (or req.body) to an object. In this case, however, it is less trivial to determine what the correct behaviour should be, so I did not attempt to address it.

Secondly, this diff is a bit messy because VSCode decided to trim extra whitespace in a couple of places. If that's an issue, please let me know and I can manually revert those.

Thanks!
Zach

@cgvwzq
Copy link

cgvwzq commented Feb 1, 2022

It is funny that I was, just right now, writing a fix for this. I'll just comment here instead. As you mention, the Object.create(null) is good practice, but will only protect against some cases. I would suggest, on top of that, to move the inlined prototype pollution check from processNested.js into a function isSafeFromPollution(obj, key) that is exported by utilities.js and use that check in all the places doing sensitive merge-like things, like processNested(), buildFields().

It is probably also a good idea to use Object.create(null) in processNested too (on variables d and current[k]).

I disagree on the low-impact issue, and this can become real bad with certain flags. So it is probably sensible to get the patch and security advisory quickly.

Cheers,
Pepe

@@ -79,13 +79,13 @@ const buildOptions = function() {
const buildFields = (instance, field, value) => {
// Do nothing if value is not set.
if (value === null || value === undefined) return instance;
instance = instance || {};
instance = instance || Object.create(null);
// Non-array fields
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add an isSafeFromPollution(instance, field) check right here. And move https://github.com/richardgirges/express-fileupload/blob/master/lib/processNested.js#L24-L27 into a function exported by utilities.

@zwade
Copy link
Contributor Author

zwade commented Feb 2, 2022

That sounds pretty reasonable, let me take a look at that now

@richardgirges
Copy link
Owner

Thank you very much for taking the time on this! This is a high priority, so I'm interested in getting this merged soon. Agree with @cgvwzq's feedback.

Additionally, if you wouldn't mind, can you double-check that linting and tests are passing? CircleCI is temporarily disconnected so I'm merging blind.

@zwade
Copy link
Contributor Author

zwade commented Feb 2, 2022

Here's an update that hopefully makes the checks more robust. There are, naturally, some more edge cases due to this being fundamentally challenging to determine correctly, but I think the weird behavior should be effectively documented in the test I added.

Also regarding tests, I'm having trouble running some of the unrelated tests on my machine, so I'll need a minute or two more to verify that they're passing correctly.

@zwade
Copy link
Contributor Author

zwade commented Feb 2, 2022

Ahh ok, this might actually be a regression in saveBufferToFile. Specifically on my system (macOS 11.1, node v16.13.1) fs.createWriteStream is firing both an error and close event causing the callback to be invoked twice (once with an error once without).

This is probably largely undetected because most modern code will be using the promise interface which naturally silences duplicate calls to resolve/reject.

Since I would imagine you would like to have the tests passing, I'm happy to submit a separate PR that addresses this.

@zwade
Copy link
Contributor Author

zwade commented Feb 2, 2022

For cleanliness' sake I moved this into a separate PR: #302

Once merged (or rebased) on top of that one, this PR should have passing tests/lints.

@cgvwzq
Copy link

cgvwzq commented Feb 2, 2022

That looks good to me. Thanks a lot @zwade :)

@richardgirges richardgirges merged commit edd91ce into richardgirges:master Feb 2, 2022
@zwade
Copy link
Contributor Author

zwade commented Feb 2, 2022

Glad I could help!

@richardgirges
Copy link
Owner

Thank you @zwade! v1.3.1 published on NPM

https://github.com/richardgirges/express-fileupload/releases/tag/v1.3.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants