-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separate command line building and sanitizing into its own class.
- Loading branch information
Showing
4 changed files
with
150 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
require "shellwords" | ||
|
||
module AwesomeSpawn | ||
class CommandLineBuilder | ||
# Build the full command line. | ||
# | ||
# @param [String] command The command to run | ||
# @param [Hash,Array] params Optional command line parameters. They can | ||
# be passed as a Hash or associative Array. The values are sanitized to | ||
# prevent command line injection. Keys as symbols are prefixed with `--`, | ||
# and `_` is replaced with `-`. | ||
# | ||
# - `{:key => "value"}` generates `--key value` | ||
# - `{"--key" => "value"}` generates `--key value` | ||
# - `{:key= => "value"}` generates `--key=value` | ||
# - `{"--key=" => "value"}` generates `--key=value` | ||
# - `{:key_name => "value"}` generates `--key-name value` | ||
# - `{:key => nil}` generates `--key` | ||
# - `{"-f" => ["file1", "file2"]}` generates `-f file1 file2` | ||
# - `{nil => ["file1", "file2"]}` generates `file1 file2` | ||
# | ||
# @return [String] The full command line | ||
def build(command, params = nil) | ||
return command.to_s if params.nil? || params.empty? | ||
"#{command} #{assemble_params(sanitize(params))}" | ||
end | ||
|
||
private | ||
|
||
def sanitize(params) | ||
return [] if params.nil? || params.empty? | ||
params.collect do |k, v| | ||
[sanitize_key(k), sanitize_value(v)] | ||
end | ||
end | ||
|
||
def sanitize_key(key) | ||
case key | ||
when Symbol then "--#{key.to_s.tr("_", "-")}" | ||
else key | ||
end | ||
end | ||
|
||
def sanitize_value(value) | ||
case value | ||
when Array then value.collect { |i| i.to_s.shellescape } | ||
when NilClass then value | ||
else value.to_s.shellescape | ||
end | ||
end | ||
|
||
def assemble_params(sanitized_params) | ||
sanitized_params.collect do |pair| | ||
pair_joiner = pair.first.to_s.end_with?("=") ? "" : " " | ||
pair.flatten.compact.join(pair_joiner) | ||
end.join(" ") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
require 'spec_helper' | ||
|
||
describe AwesomeSpawn::CommandLineBuilder do | ||
subject { described_class.new } | ||
|
||
let(:params) do | ||
{ | ||
"--user" => "bob", | ||
"--pass" => "P@$sw0^& |<>/-+*d%", | ||
"--db" => nil, | ||
"--desc=" => "Some Description", | ||
:symkey => nil, | ||
:symkey_dash => nil, | ||
nil => ["pkg1", "some pkg"] | ||
} | ||
end | ||
|
||
let (:modified_params) do | ||
params.to_a + [123, 456].collect {|pool| ["--pool", pool]} | ||
end | ||
|
||
context "#build" do | ||
it "sanitizes crazy params" do | ||
cl = subject.build("true", modified_params) | ||
expect(cl).to eq "true --user bob --pass P@\\$sw0\\^\\&\\ \\|\\<\\>/-\\+\\*d\\% --db --desc=Some\\ Description --symkey --symkey-dash pkg1 some\\ pkg --pool 123 --pool 456" | ||
end | ||
|
||
it "handles Symbol keys" do | ||
cl = subject.build("true", :abc => "def") | ||
expect(cl).to eq "true --abc def" | ||
end | ||
|
||
it "handles Symbol keys with tailing '='" do | ||
cl = subject.build("true", :abc= => "def") | ||
expect(cl).to eq "true --abc=def" | ||
end | ||
|
||
it "handles Symbol keys with underscore" do | ||
cl = subject.build("true", :abc_def => "ghi") | ||
expect(cl).to eq "true --abc-def ghi" | ||
end | ||
|
||
it "handles Symbol keys with underscore and tailing '='" do | ||
cl = subject.build("true", :abc_def= => "ghi") | ||
expect(cl).to eq "true --abc-def=ghi" | ||
end | ||
|
||
it "sanitizes Fixnum array param value" do | ||
cl = subject.build("true", nil => [1]) | ||
expect(cl).to eq "true 1" | ||
end | ||
|
||
it "sanitizes Pathname param value" do | ||
cl = subject.build("true", nil => [Pathname.new("/usr/bin/ruby")]) | ||
expect(cl).to eq "true /usr/bin/ruby" | ||
end | ||
|
||
it "sanitizes Pathname param key" do | ||
cl = subject.build("true", Pathname.new("/usr/bin/ruby") => nil) | ||
expect(cl).to eq "true /usr/bin/ruby" | ||
end | ||
|
||
it "with params as empty Hash" do | ||
cl = subject.build("true", {}) | ||
expect(cl).to eq "true" | ||
end | ||
|
||
it "with params as nil" do | ||
cl = subject.build("true", nil) | ||
expect(cl).to eq "true" | ||
end | ||
|
||
it "without params" do | ||
cl = subject.build("true") | ||
expect(cl).to eq "true" | ||
end | ||
|
||
it "with Pathname command" do | ||
cl = subject.build(Pathname.new("/usr/bin/ruby")) | ||
expect(cl).to eq "/usr/bin/ruby" | ||
end | ||
|
||
it "with Pathname command and params" do | ||
cl = subject.build(Pathname.new("/usr/bin/ruby"), "-v" => nil) | ||
expect(cl).to eq "/usr/bin/ruby -v" | ||
end | ||
end | ||
end |