One of many problems of using bash for scripting is how difficult it is to keep data properly quoted - even writing SQL by string concatenation is far easier to do safely, and we all know how that usually ends.
Unfortunately while everybody knows that building SQL by string concatenation is crazy, doing that in bash is somehow still practiced widely, and what's worse - even people who know better than that and use a sane language often revert to many awful and insecure bashisms.
How not to code
Let's start with a simple task. User has some EDITOR variable set. We have some files. We want to edit them with user's favourite editor.
Plenty of people who suffered from being exposed to bash in their youth will write code like this:
system "#{ENV['EDITOR']} #{files.join(" ")}"
And such code is going to fail miserably if files have any spaces in them, let alone any unusual but perfectly legitimate characters like "lol.txt\nrm -rf /".
One step towards sanity
People who know better are aware that system command takes multiple argument, so you can pass files separately:
system ENV['EDITOR'], *files
This is not only far safer it's also much more concise. There's only one little problem - unlike in the unsafe version EDITOR is now restricted to a single command - user can no longer select mate -w to be their editor, since that's the name of the executable.
If you intuition is to do something like *ENV['EDITOR'].split(" "), please don't since that's just opens another set of problems.
Shellwords to the rescue
Fortunately a solution to all these problems is easy:
require shellwords system *Shellwords.split(ENV['EDITOR']), *files
(In case you didn't know it yet, Ruby 1.9 and 2.0 lets you use multiple * in the same method invocation.)
And that's all in today's lesson.
No comments:
Post a Comment