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

Paranoia with Prototype Pollution #32

Closed
shadowspawn opened this issue Jan 9, 2022 · 12 comments
Closed

Paranoia with Prototype Pollution #32

shadowspawn opened this issue Jan 9, 2022 · 12 comments

Comments

@shadowspawn
Copy link
Collaborator

Prototype pollution is a concern for situations where end-user input ends up on an object as a property name. (I am not an expert in this, so my questions may include misconceptions!)

Does it affect parseArgs? (I am guessing to some extent since we are taking end-user arguments as option names and storing values.)

Is it enough for our context to protect against using a property key named __proto__? (i.e. from arguments including --__proto__.)

Does using null objects help, or separate issue for our context?

__proto__ related links:

@ljharb
Copy link
Member

ljharb commented Jan 9, 2022

Yes, there’s been tons of CVEs about this on yargs and minimist and others. Not handling this will make node itself a CVE target.

@bcoe
Copy link
Collaborator

bcoe commented Jan 16, 2022

I think we should create all our objects with null prototypes, Object.create(null).

@ljharb
Copy link
Member

ljharb commented Jan 16, 2022

That’s good, but isn’t sufficient to protect against prototype pollution because of the dunder proto setter on Object.prototype.

@bcoe
Copy link
Collaborator

bcoe commented Jan 16, 2022

In yargs-parser, we also have an additional sanitization step:

/ TODO(bcoe): in the next major version of yargs, switch to
// Object.create(null) for dot notation:
function sanitizeKey (key: string): string {
  if (key === '__proto__') return '___proto___'
  return key
}

The idea with the tripple underscore was thatm on the off chance that someone legit wants __proto__ to be passed into their application, perhaps they can atleast detect that someone passed in something resembling it.

A less silly approach now that I think about it, is perhaps we can instead do something like HTML encoding:

%5F%5Fproto%5F%5F

On the off chance a key that's being set on flags or values is __proto__.

@ljharb
Copy link
Member

ljharb commented Jan 16, 2022

Yargs’ approach sounds fine; as does just outright rejecting an arg named that, because why support that use case.

@bcoe
Copy link
Collaborator

bcoe commented Jan 16, 2022

@ljharb @shadowspawn I'm wondering, part of why we went with the approach we went with in yargs-parser was to minimize refactoring, since it was a fairly large library with a lot of edge-cases, what if we just switched parseargs to using a Map:

> const values = new Map()
undefined
> values.set('__proto__', 'hello')
Map(1) { '__proto__' => 'hello' }
> values.set('apple', 99)
Map(2) { '__proto__' => 'hello', 'apple' => 99 }
> Object.fromEntries(values)
{ ['__proto__']: 'hello', apple: 99 }

And then return Object.fromEntries in the public interface?

@ljharb
Copy link
Member

ljharb commented Jan 16, 2022

Seems like a good approach as well.

@shadowspawn
Copy link
Collaborator Author

With the idea proposed that we return something different than what we work with internally, map is a contender, and I like that Map is a container consistent with the intent to store any string key safely.

I am not clear on the risks to the client code though. After we write back the properties onto an object in the result, are we handing the client code a grenade if end-user has specified problematic option names? (like say toString)

@ljharb
Copy link
Member

ljharb commented Jan 17, 2022

In JavaScript you always have to know to check for/iterate own properties anyways; toString isn’t an issue on the read side as long as the data structure is written properly.

@shadowspawn
Copy link
Collaborator Author

My current understanding is that we do not currently have ability to create prototype pollution (3) because we only ever overwrite properties directly by key. We do not do a deep copy, we do not support nested properties (like foo.bar, leading to __proto__.bar).

We do get some protection against future code changes introducing a CVE by taking the special keys out of play by internally using (say) map, and by protecting the very special __proto__.

@shadowspawn
Copy link
Collaborator Author

Closing #33 with the understanding that "one of the goals in the approach we land on to prevent prototype pollution should be that the public interface still feels natural".

#33 (comment)

@shadowspawn
Copy link
Collaborator Author

I am still interested in adding a unit test, but not going to hold this issue open.

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

No branches or pull requests

3 participants