Consider the following code, which invokes a program (echo) with some arguments:
String malicious_input = '""; rm -rf / #yikes'
sh "echo Hello ${malicious_input}!"
The resulting shell script is then
echo Hello ""; rm -rf / #yikes!
Simple, classic code injection. Nothing unheard. What I have been struggling to find is a way to properly handle this case. First approaches to fix this are:
- Just add single quotes around the string in the shell call, like
sh "echo Hello '${malicious_input}'!". Yes, but no, I only need to switch tomalicious_input = "'; rm -rf / #yikes"to circumvent that. - Just add double quotes then! Still no, not only are these just as simple to circumvent but those are even prone to path globbing/expansion.
- Then add the quotes around the input string before invoking Groovy string interpolation. Same thing, the shell commandline is unchanged.
- Then, add single quotes but prefix every single quote inside the string with a backslash to prevent its interpretation as meta character by the shell. Yes, that kind-of works, if I also escape every existing backslash with a second one. Still, the details of how to prevent this expansion depend a bit on the shell (POSIX-ish, Windows
bat, not sure about powershell). Also, this takes three lines of code for every argument. Plus, without an explicit shebang line, I can't even be sure which shell is taken.
So, my question is this: Where is the built-in function in Groovy that does this for me in a portable, shell-agnostic way? I find it hard to believe that this doesn't exist, yet I can't find it. Also, quite puzzling for me that I'm the first one to come across this issue...