diff --git a/lib/busser/runner_plugin/bash.rb b/lib/busser/runner_plugin/bash.rb index 0a5a3f8..bf0bc5d 100644 --- a/lib/busser/runner_plugin/bash.rb +++ b/lib/busser/runner_plugin/bash.rb @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require "shellwords" unless defined?(Shellwords) + require "busser/runner_plugin" # A Busser runner plugin for Bash. @@ -46,7 +48,19 @@ def self.test_files_in(path) def test self.class.test_files_in(suite_path("bash")).each do |file| banner "[bash] #{File.basename(file)}" - run!("bash #{file}") + run!(self.class.command_for(file)) end end + + # Builds the command that runs one script. + # + # The path is quoted. It is rooted at BUSSER_ROOT, which the caller chooses, + # so an unquoted path containing a space would be split by the shell and bash + # would be handed a fragment instead of the script. + # + # @param file [String, Pathname] path to the script + # @return [String] the command to run + def self.command_for(file) + "bash #{Shellwords.escape(file.to_s)}" + end end diff --git a/spec/busser/runner_plugin/bash_spec.rb b/spec/busser/runner_plugin/bash_spec.rb index 831dcc2..1ddec0b 100644 --- a/spec/busser/runner_plugin/bash_spec.rb +++ b/spec/busser/runner_plugin/bash_spec.rb @@ -1,5 +1,6 @@ require_relative "../../spec_helper" +require "shellwords" require "tmpdir" require "busser/runner_plugin/bash" @@ -58,4 +59,27 @@ def selecting(*names) end end end + + describe ".command_for" do + it "runs the script with bash" do + cmd = Busser::RunnerPlugin::Bash.command_for("/opt/busser/suites/bash/a_test.sh") + + _(Shellwords.split(cmd)).must_equal ["bash", "/opt/busser/suites/bash/a_test.sh"] + end + + # BUSSER_ROOT is chosen by the caller, and Test Kitchen roots it under a + # user-controlled directory. Unquoted, a space split the path and bash was + # handed a fragment. + it "quotes a path containing spaces" do + cmd = Busser::RunnerPlugin::Bash.command_for("/tmp/my tests/bash/a_test.sh") + + _(Shellwords.split(cmd)).must_equal ["bash", "/tmp/my tests/bash/a_test.sh"] + end + + it "neutralises shell metacharacters in the path" do + cmd = Busser::RunnerPlugin::Bash.command_for("/tmp/a;touch pwned/a_test.sh") + + _(Shellwords.split(cmd)).must_equal ["bash", "/tmp/a;touch pwned/a_test.sh"] + end + end end