Jones
Jones

Reputation: 1214

Run command and capture output in Ruby, without risking command injection

Does Ruby ship with a function/method to run a command and capture it's output without risking command injection.

For example:

out = `ls #{directory}`

would capture the output I want, but is insecure.

system("ls", directory)

is safer (as far as I know), but I can't capture it's output.

Is there some equivalent to Python's out = call(['ls', directory])?

Edit for @Eric Duminil

irb(main):001:0> RUBY_DESCRIPTION
=> "ruby 2.1.8p440 (2015-12-16 revision 53160) [x86_64-linux]"
irb(main):002:0> Shellwords
NameError: uninitialized constant Shellwords
        from (irb):2
        from /apollo/env/SDETools/ruby2.1.x/bin/irb:15:in `<main>'
irb(main):003:0> require "shellwords"
=> true
irb(main):004:0> Shellwords
=> Shellword

Upvotes: 1

Views: 193

Answers (1)

Eric Duminil
Eric Duminil

Reputation: 54223

You can escape the argument with Shellwords#shellescape:

Escapes a string so that it can be safely used in a Bourne shell command line. str can be a non-string object that responds to to_s.

Note that a resulted string should be used unquoted and is not intended for use in double quotes nor in single quotes.

require 'shellwords' # <- Only needed for Ruby 1.9
"ls %s" % Shellwords.escape("test.txt && whoami")
# => "ls test.txt\\ \\&\\&\\ whoami"
system("ls %s" % Shellwords.escape("test.txt && whoami"))
# ls: cannot access test.txt && whoami: No such file or directory

As mentioned by @engineersmnky in the comments, you could simply use system with multiple arguments:

system("ls", "test.txt && whoami")
# ls: cannot access test.txt && whoami: No such file or directory

Upvotes: 1

Related Questions