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

Prevent command injection through the variable option #5085

Closed
wants to merge 1 commit into from

Conversation

stof
Copy link

@stof stof commented Feb 17, 2021

This fixes the issue disclosed at https://app.snyk.io/vuln/SNYK-JS-LODASH-1040724

For now, I used a regexp allowing any ES5 identifier for the variable name. The advantage of that is that it avoids BC breaks if some code uses weird variable names. The drawback is that the regexp is big.
If reducing the range of allowed variable names is OK, a smaller regex could be used.

@stof
Copy link
Author

stof commented Feb 17, 2021

Looks like there is no CI running on the 4.17 branch anymore as the migration to github actions has been done only in master...

@Alan-Liang
Copy link

The regex is of ~10k in size, and is still incomplete. Example:

> var 𝐀 = 1
undefined
> re.test('𝐀')
false

@stof
Copy link
Author

stof commented Feb 18, 2021

@Alan-Liang I indeed took the ES 5 regex, not the ES6 one (which allowed even more chars.

I'll let the maintainers decide what they want to allow for this variable option.

@Alan-Liang
Copy link

Would it be OK to just ban the following characters: ), = (default parameter), , (delimiter)? I think an attacker cannot execute any code without using one of these, and a normal bad identifier will cause a SyntaxError to be thrown once being eval()ed.

Is there any current ES proposal that extends this list of characters?

@bnjmnt4n bnjmnt4n added the bug label Feb 19, 2021
@bnjmnt4n
Copy link
Contributor

What do you think about only accepting ASCII characters for the variable name, and instead of throwing an error, just defaulting to the with(obj){ } branch?

Any change here to fix this CVE would be considered breaking, but I don't want to introduce additional explicit errors.

/cc @jdalton @falsyvalues for any inputs.

@stof
Copy link
Author

stof commented Feb 19, 2021

@bnjmnt4n defaulting to with (obj) would still produce a broken compiled template, as using variable or not using it leads to writing the source of the template differently.

@bnjmnt4n
Copy link
Contributor

Yeah, the generated template function would be broken, but it would be a runtime error instead of an error during _.template. However, now that I think about it, it's probably fine to throw an error at "compile time" since there's a likelihood that our Function call would do that also, so keeping the error throwing behaviour should be fine.

@stof
Copy link
Author

stof commented Feb 19, 2021

And throwing a compile-time error at least allows to throw a meaningful error, explaining what went wrong.
Also, projects not allowing a user-defined variable name are probably using a working one.

@stof
Copy link
Author

stof commented Feb 19, 2021

I updated the PR to use a much smaller regex forbidding some offending chars instead of the huge ES identifier regex

@falsyvalues
Copy link
Contributor

Main problem is that we don't know what users put there, its configuration layer. Honestly I'm not really sure why this is even reported as Command Injection since its configuration layer.

@bnjmnt4n I agree we should avoid throwing new errors and go with default path. Maybe it would be easier to use negative check and detect characters that cannot be part of variable name in ES6.

@stof
Copy link
Author

stof commented Feb 19, 2021

@falsyvalues lodash cannot enforce that no user-controlled values is ever used by projects when passing the variable.

I agree we should avoid throwing new errors and go with default path.

having a variable name or not having one produce a different template compilation, for a different kind of usage. You cannot consider the with (obj) to be the default case of a variable name. that's a different kind of template compilation. A dedicated error is better than weird errors due to accessing undefined variables inside the generated code during the template execution.

Maybe it would be easier to use negative check and detect characters that cannot be part of variable name in ES6.

This has now been done (with a set of specific forbidden chars) to avoid the huge regexp.
Also, this should reduce the BC impact, as the only projects that would get the new error would be project that currently inject broken variables (and so are either subject to the reported security issue with carefully crafted config or are generating broken code)

@falsyvalues
Copy link
Contributor

@falsyvalues lodash cannot enforce that no user-controlled values is ever used by projects when passing the variable.

Sorry, but I don't know what are you trying to say. To which part of comment are you referring?

@stof
Copy link
Author

stof commented Feb 19, 2021

@falsyvalues that was the answer to the first paragraph of your previous comment.
The fact that lodash does not know what gets passed there is precisely the reason why it should not trust the value to be valid.

@bnjmnt4n
Copy link
Contributor

I think the list of forbidden characters used is a good compromise between having to embed a large ES identifier regex, and maximizing back-compat. This does mean that we might have to update the regex in the future if there are any changes to allowed ECMAScript syntax.

@bnjmnt4n
Copy link
Contributor

Thanks for the PR, merged in 3469357.

@@ -14865,6 +14875,8 @@
var variable = hasOwnProperty.call(options, 'variable') && options.variable;
if (!variable) {
source = 'with (obj) {\n' + source + '\n}\n';
} else if (reForbiddenIdentifierChars.test(variable)) {
Copy link

Choose a reason for hiding this comment

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

I noticed there is no check that variable is typeof 'string'

Choose a reason for hiding this comment

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

RegExp.prototype.test coerces its argument to a string before making matches.

@lodash lodash deleted a comment from adityamatt Nov 16, 2021
@lodash lodash locked and limited conversation to collaborators Nov 16, 2021
tencyle-dev added a commit to tencyle-dev/lodash that referenced this pull request Feb 15, 2023
Prevent command injection through `_.template`'s `variable` option

Closes lodash#5085.
@jdalton jdalton added the issue bankruptcy Closing the issue/PR to start fresh label Sep 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug issue bankruptcy Closing the issue/PR to start fresh
Development

Successfully merging this pull request may close these issues.

None yet

6 participants