diff --git a/SC2089.md b/SC2089.md new file mode 100644 index 0000000..f1fb271 --- /dev/null +++ b/SC2089.md @@ -0,0 +1,44 @@ +# Quotes/backslashes will be treated literally. Use an array. + +### Problematic code: + + args="-lh "My File.txt"' + ls $args + +### Correct code: + + args=(-lh "My File.txt") + ls "${args[@]}" + +### Rationale: + +Bash does not interpret data as code. Consider almost any other languages, such as Python: + + print 1+1 # prints 2 + a="1+1" + print a # prints 1+1, not 2 + +Here, `1+1` is Python syntax for adding numbers. However, passing a literal string containing this expression does not cause Python to interpret it, see the `+` and produce the calculated result. + +Similarly, `"My File.txt"` is Bash syntax for a single word with a space in it. However, passing a literal string containing this expression does not cause Bash to interpret it, see the quotes and produce the tokenized result. + +The solution is to use an array instead, whenever possible. + +If you due to `sh` compatibility can't use arrays, you can use `eval` instead. However, this is very insecure and easy to get wrong, leading to various forms of security vulnerabilities and breakage: + + quote() { local q=${1//\'/\'\\\'\'}; echo "'$q'"; } + args="-lh $(quote "My File.txt")" + eval ls "$args" # Do not use unless you understand implications + +If you ever accidentally forget to use proper quotes, such as with: + + for f in *.txt; do + args="-lh '$1'" # Example security exploit + eval ls "$args" # Do not copy and use + done + +Then you can use `touch "'; rm -rf \$'\x2F'; '.txt"` (or someone can trick you into downloading a file with this name, or create a zip file or git repo containing it, or changing their nick and have your chat client create the file for a chat log, or...), and running the script to list your files will run the command `rm -rf /`. + +### Contraindications + +Few and far between. \ No newline at end of file