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
CVE 2022 25765 fix #518
CVE 2022 25765 fix #518
Conversation
Please check this @kiafaldorius @postmodern |
Not to derail here, but the root cause seems to be that the command is executed via # with a String
IO.popen("echo $HOME").read
# => "/home/postmodern\n"
# with an array
IO.popen(['echo', '$HOME']).read
# => "$HOME\n" Does the |
@MuhammetDilmac Thanks for opening the PR! I'm not entirely sure this works, since @postmodern This looks to be the cleaner approach. I think it needs to use IO.popen to pipe the output into rubyland as the result of If it absolutely requires it for some use cases, might I suggest a separate |
I agree with IO open pipe. But I don't know why maintainers use shell. |
FYI this is the call to Lines 74 to 80 in 6213317
Without digging through the Git history I suspect the original authors just through passing a String to I guess it depends on whether users of pdfkit really expect environment variables or shell commands to be embedded in PDF input or not. If not, then I say |
def shellescape_parameter_name(parameter_name) | ||
allow_curly_bracket_regex = %r{\\([\[\]])} | ||
|
||
Shellwords.escape(parameter_name).gsub(allow_curly_bracket_regex, '\1') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see why this is here, but needing this is a sign that there may be other edge cases with special characters when escaped characters are inside strings vs as a regular argument. The #to_input_for_command
will be better off not having the quotes when the content is already escaped.
Spaces are escaped, so the core part of why the quotes need to be there--to have the command treat its content as a singular argument rather than multiple arguments--should be handled already by the escaping.
(I don't know how/whether Shellwords works in Windows, it may not.)
end | ||
|
||
def shellescape_query(url) | ||
url, query = url.split('?') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're assuming a properly formatted URL. That's a dangerous thing...What's the result when someone passes in url = http://localhost:3000/haxor$HOME/hello
. Hint: It doesn't escape the important part....
def needs_shell_escaping?(data) | ||
Shellwords.escape(shellwords_unescape(data)) != data | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't bother checking this, shouldn't hurt to always escape. People should not be passing in shell formatted content to the library anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some specs that test whether a previously escaped source URL does not get double-escaped. Example, what if someone passes in a URL that was formatted by URI::HTTP.build
and already contains '%XX' escaped characters? Should we assume the user must always pass in an unescaped URL? Or should we assume it's the user's responsibility to properly format the URL? Should those tests be removed?
@postmodern I'm pretty sure IO.popen has allowed the array format since 1.9. I remember updating some stuff from back in 1.8.7 transition to use the new format upgrading to 1.9...of course it is a breaking change, but I would hope no one is running 1.8.7 nowadays. |
@MuhammetDilmac Thanks for the PR, I added some quick review notes. Unfortunately, it doesn't handle all the necessary cases. Security is hard! |
@kiafaldorius I could try sending a PR that changes Although, I noticed a lot of code is for adding special shell escaping specifically for Windows. I'm not sure how |
This PR is so dirty. @postmodern PR #519 is more clear and more secure. |
* By calling `IO.popen` with an Array of command arguments (ex: `['ls', '-l', ...]`) it runs the command as a separate process instead of running it in a sub-shell as a shell command. This prevents any arbitrary command injection or env variable interpolation, without needing complex shell-escaping logic. https://ruby-doc.org/core-3.1.2/IO.html#method-c-popen * Changed `Configuration#executable` to return a String or an Array for when xvfb mode is enabled. * Changed `PDFKit#command` to return an Array of command arguments for `IO.popen`. * Removed argument quoting logic as it's not necessary when calling `IO.popen` with an Array of arguments. * Rewrote some specs to test if the command's Array of arguments contains specific argument values. * Added a custom RSpec `contain` matcher for testing if an expected Array exists within another Array.
I wrote a small code for the CVE 2022-25765. Please review and test it.
#517 rubysec/ruby-advisory-db#517 It's too dirty please refactor this :)