command-runner 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7547604c52079e08e7fb9394e3ac7436332ca402
4
- data.tar.gz: beaddb35df551eb3858a10fb3991e69d2963f8db
3
+ metadata.gz: 9b5636d13fb538ac969b67e6eb4fdc7ad5ddaa27
4
+ data.tar.gz: fa207be281ffb8b56279bbe7d50e76c1f52ef225
5
5
  SHA512:
6
- metadata.gz: d4d2ae85fd767805cfa8d1d5c4ada39aae4ccb689d2d2ba150ba284f5336d016d1326fd9fc86011738dbdc95b13792ffb43210fa4f8c2cd918578598961e833b
7
- data.tar.gz: 2d341ceec6a4f57732d88ab64be721842707435952eefce32da0fbf97855c0c0b0c977d247bc4f082b0a1d3a40a73e862f301b600279251895954ab4bb9036f0
6
+ metadata.gz: 037f29024cedc32f6b469288a2c7229f7d74d9035df02894b9b8685834f867eab990fc3c9f69a9ba305ae38eb33a7a141b284dbb46396603fde613083aaddaf3
7
+ data.tar.gz: 5804940205201d61ab9ff50164e75dc9e5b18c415d46a38747345b9a990e52c3d731f3e0754eb1f60253df69373cde1a514f9644f09605c6d34d7d61f6fbf3fc
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Command Runner
2
- [![Build Status](https://travis-ci.org/redjazz96/command-runner.png?branch=master)](https://travis-ci.org/redjazz96/command-runner) [![Code Climate](https://codeclimate.com/github/redjazz96/command-runner.png)](https://codeclimate.com/github/redjazz96/command-runner)
2
+ [![Build Status](https://travis-ci.org/medcat/command-runner.png?branch=master)](https://travis-ci.org/medcat/command-runner) [![Code Climate](https://codeclimate.com/github/medcat/command-runner.png)](https://codeclimate.com/github/medcat/command-runner)
3
3
 
4
4
  Runs commands.
5
5
 
@@ -46,7 +46,7 @@ It can also use different methods to run commands...
46
46
 
47
47
  ```Ruby
48
48
  line = Command::Runner.new("echo", "something")
49
- line.backends = Command::Runner::Backends::Spawn.new
49
+ line.backend = Command::Runner::Backends::Spawn.new
50
50
  line.pass
51
51
  ```
52
52
 
@@ -80,7 +80,6 @@ It works on
80
80
  - 2.1.0
81
81
  - 2.0.0
82
82
  - 1.9.3
83
- - 1.8.7
84
83
  - JRuby (2.0 Mode)
85
84
  - JRuby (1.9 Mode)
86
85
 
@@ -88,5 +87,5 @@ It works on
88
87
  unless the travis build fails.
89
88
 
90
89
 
91
- [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/redjazz96/command-runner/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
90
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/medcat/command-runner/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
92
91
 
@@ -9,7 +9,7 @@ module Command
9
9
 
10
10
  # Returns whether or not this backend is avialable on this
11
11
  # platform.
12
- def self.available?
12
+ def self.available?(_ = false)
13
13
  true
14
14
  end
15
15
 
@@ -11,8 +11,10 @@ module Command
11
11
  # Returns whether or not this backend is avialable on this
12
12
  # platform.
13
13
  #
14
+ # @param force_unsafe [Boolean] if the backend needs to be
15
+ # able to handle unsafe execution, then this will be true.
14
16
  # @abstract
15
- def self.available?
17
+ def self.available?(force_unsafe = false)
16
18
  true
17
19
  end
18
20
 
@@ -27,6 +29,15 @@ module Command
27
29
  false
28
30
  end
29
31
 
32
+ # Whether or not it can handle unsafe execution. This is in
33
+ # case the developer wants to force unsafe execution on a
34
+ # safe backend.
35
+ #
36
+ # @return [Boolean]
37
+ def self.unsafe_execution?
38
+ true
39
+ end
40
+
30
41
  # Initialize the fake backend.
31
42
  def initialize
32
43
  @ran = []
@@ -13,10 +13,10 @@ module Command
13
13
  #
14
14
  # @see Fake.available?
15
15
  # @return [Boolean]
16
- def self.available?
16
+ def self.available?(_ = false)
17
17
  @_available ||= begin
18
18
  require 'posix/spawn'
19
- true
19
+ super
20
20
  rescue LoadError => e
21
21
  false
22
22
  end
@@ -27,7 +27,13 @@ module Command
27
27
  # @see Spawn#spawn
28
28
  # @return [Numeric]
29
29
  def spawn(env, command, arguments, options)
30
- POSIX::Spawn.spawn(env, command, *[arguments, options].flatten)
30
+ if options.delete(:unsafe)
31
+ POSIX::Spawn.spawn(env,
32
+ "#{command} #{arguments.join(' ')}", options)
33
+ else
34
+ POSIX::Spawn.spawn(env, command,
35
+ *[arguments, options].flatten)
36
+ end
31
37
  end
32
38
 
33
39
  end
@@ -11,7 +11,7 @@ module Command
11
11
  # that platform.
12
12
  #
13
13
  # @return [Boolean]
14
- def self.available?
14
+ def self.available?(_ = false)
15
15
  Process.respond_to?(:spawn) && !(RUBY_PLATFORM == "java" && RUBY_VERSION =~ /\A1\.9/)
16
16
  end
17
17
 
@@ -81,7 +81,11 @@ module Command
81
81
  # @see Process.spawn
82
82
  # @return [Numeric] the process id
83
83
  def spawn(env, command, arguments, options)
84
- Process.spawn(env, command, *[arguments, options].flatten)
84
+ if options.delete(:unsafe)
85
+ Process.spawn(env, "#{command} #{arguments.join(' ')}", options)
86
+ else
87
+ Process.spawn(env, command, *arguments, options)
88
+ end
85
89
  end
86
90
 
87
91
  # Waits for the given process, and returns the process id and the
@@ -10,10 +10,14 @@ module Command
10
10
  class SSH < Fake
11
11
 
12
12
  # (see Fake.available?)
13
- def self.available?
13
+ def self.available?(force_unsafe = false)
14
14
  begin
15
15
  require 'net/ssh'
16
- true
16
+ if force_unsafe
17
+ false
18
+ else
19
+ true
20
+ end
17
21
  rescue LoadError
18
22
  false
19
23
  end
@@ -23,6 +27,10 @@ module Command
23
27
  true
24
28
  end
25
29
 
30
+ def self.unsafe_execution?
31
+ false
32
+ end
33
+
26
34
  # Initializes the backend.
27
35
  #
28
36
  # @param host [String] the host to connect to.
@@ -41,7 +49,7 @@ module Command
41
49
 
42
50
 
43
51
  ch.exec "#{command} " \
44
- "#{arguments.join(" ").shellescape}" do |sch, success|
52
+ "#{arguments.join(" ")}" do |sch, success|
45
53
  raise Errno::ENOENT unless success
46
54
 
47
55
  env.each do |k, v|
@@ -0,0 +1,19 @@
1
+ module Command
2
+ class Runner
3
+ module Backends
4
+ class UnsafeFake < Fake
5
+
6
+ # A backend is considered unsafe when the arguments are
7
+ # exposed directly to the shell. This is a vulnerability, so
8
+ # we mark the class as unsafe and when we're about to pass
9
+ # the arguments to the backend, escape the safe
10
+ # interpolations.
11
+ #
12
+ # @return [Boolean]
13
+ def self.unsafe?
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -11,6 +11,7 @@ module Command
11
11
  autoload :Spawn, "command/runner/backends/spawn"
12
12
  autoload :Backticks, "command/runner/backends/backticks"
13
13
  autoload :PosixSpawn, "command/runner/backends/posix_spawn"
14
+ autoload :UnsafeFake, "command/runner/backends/unsafe_fake"
14
15
 
15
16
  end
16
17
 
@@ -3,7 +3,7 @@ module Command
3
3
  class Runner
4
4
 
5
5
  # The current version of Runner.
6
- VERSION = "0.9.1".freeze
6
+ VERSION = "0.9.2".freeze
7
7
 
8
8
  end
9
9
  end
@@ -1,5 +1,6 @@
1
1
  require 'shellwords'
2
2
  require 'future'
3
+ require 'hashie'
3
4
 
4
5
  require 'command/runner/version'
5
6
  require 'command/runner/message'
@@ -24,11 +25,11 @@ module Command
24
25
  #
25
26
  # @return [#call] a backend to use.
26
27
  def best_backend(force_unsafe = false)
27
- if Backends::PosixSpawn.available? && !force_unsafe
28
+ if Backends::PosixSpawn.available?(force_unsafe)
28
29
  Backends::PosixSpawn.new
29
- elsif Backends::Spawn.available? && !force_unsafe
30
+ elsif Backends::Spawn.available?(force_unsafe)
30
31
  Backends::Spawn.new
31
- elsif Backends::Backticks.available?
32
+ elsif Backends::Backticks.available?(force_unsafe)
32
33
  Backends::Backticks.new
33
34
  else
34
35
  Backends::Fake.new
@@ -98,7 +99,9 @@ module Command
98
99
  # @return [Message, Object] message if no block was given, the
99
100
  # return value of the block otherwise.
100
101
  def pass!(interops = {}, options = {}, &block)
101
- backend.call(*[contents(interops), options.delete(:env) || {}, options].flatten(1), &block)
102
+ options[:unsafe] = @unsafe
103
+ env = options.delete(:env) || {}
104
+ backend.call(*contents(interops), env, options, &block)
102
105
 
103
106
  rescue Errno::ENOENT
104
107
  raise NoCommandError, @command
@@ -135,7 +138,7 @@ module Command
135
138
  #
136
139
  # @return [void]
137
140
  def force_unsafe!
138
- @backend = self.class.best_backend(true)
141
+ @unsafe = true
139
142
  end
140
143
 
141
144
  # The command line being run by the runner. Interpolates the
@@ -159,24 +162,35 @@ module Command
159
162
  # @param interops [Hash] the interpolations to make.
160
163
  # @return [Array<String>] the interpolated string.
161
164
  def interpolate(string, interops = {})
162
- interops = interops.to_a.map { |(k, v)| { k.to_s => v } }.inject(&:merge) || {}
163
- results = [*string.shellsplit]
164
-
165
- results.map do |part|
166
- if part =~ /(\{{1,2})([0-9a-zA-Z_\-]+)(\}{1,2})/
167
- if interops.key?($2) && $1.length == $3.length
168
- if $1.length == 1
169
- escape interops[$2].to_s
165
+ interops = Hashie::Mash.new(interops)
166
+ args = string.shellsplit
167
+
168
+ args.map do |arg|
169
+ arg.gsub(/(\{{1,2})([0-9a-zA-Z_\-.]+)(\}{1,2})/) do |m|
170
+ if $1.length != $3.length
171
+ next m
172
+ end
173
+
174
+ ops = interops
175
+ parts = $2.split('.')
176
+ parts.each do |part|
177
+ if ops.key?(part)
178
+ ops = ops[part]
170
179
  else
171
- interops[$2].to_s.shellsplit
180
+ ops = nil
181
+ break
172
182
  end
183
+ end
184
+
185
+ if ops == nil
186
+ m
187
+ elsif $1.length == 1
188
+ escape(ops)
173
189
  else
174
- part
190
+ ops
175
191
  end
176
- else
177
- part
178
192
  end
179
- end.flatten
193
+ end
180
194
  end
181
195
 
182
196
  private
@@ -187,7 +201,7 @@ module Command
187
201
  # @param string [String] the string to escape.
188
202
  # @return [String] the escaped string.
189
203
  def escape(string)
190
- if backend.unsafe?
204
+ if backend.unsafe? || @unsafe
191
205
  Shellwords.escape(string)
192
206
  else
193
207
  string
@@ -1,19 +1,19 @@
1
1
  describe Command::Runner::Backends::Backticks do
2
2
 
3
3
  it "is available" do
4
- Command::Runner::Backends::Backticks.should be_available
4
+ expect(Command::Runner::Backends::Backticks).to be_available
5
5
  end
6
6
 
7
- its(:unsafe?) { should be_true }
7
+ it("is unsafe") { expect(described_class).to be_unsafe }
8
8
 
9
9
  it "returns a message" do
10
10
  value = subject.call("echo", ["hello"])
11
- value.should be_instance_of Command::Runner::Message
12
- value.should be_executed
11
+ expect(value).to be_instance_of Command::Runner::Message
12
+ expect(value).to be_executed
13
13
  end
14
14
 
15
15
  it "gives the correct time" do
16
- subject.call("sleep", ["0.5"]).time.should be_within(0.1).of(0.5)
16
+ expect(subject.call("sleep", ["0.5"]).time).to be_within(0.1).of(0.5)
17
17
  end
18
18
 
19
19
  end
@@ -0,0 +1,19 @@
1
+ describe Command::Runner do
2
+
3
+ before :each do
4
+ Command::Runner.backend = Command::Runner::Backends::Fake.new
5
+ end
6
+
7
+ subject { Command::Runner.new("gcc", "{env.flags} -o {output} {input} -l{lib}") }
8
+ let(:data) do
9
+ { env: { flags: "-g3" },
10
+ output: "test",
11
+ input: "test.c",
12
+ lib: "m" }
13
+ end
14
+
15
+ it "interpolates correctly" do
16
+ value = subject.contents(data).last
17
+ expect(value).to eq ["-g3", "-o", "test", "test.c", "-lm"]
18
+ end
19
+ end
@@ -1,7 +1,7 @@
1
1
  describe Command::Runner do
2
2
 
3
3
  before :each do
4
- Command::Runner.backend = Command::Runner::Backends::Fake.new
4
+ Command::Runner.backend = Command::Runner::Backends::UnsafeFake.new
5
5
  end
6
6
 
7
7
  subject do
@@ -21,13 +21,13 @@ describe Command::Runner do
21
21
  it "escapes bad values" do
22
22
  expect(
23
23
  subject.contents(:interpolation => "`bad value`")
24
- ).to eq ["echo", ["some", "`bad value`"]]
24
+ ).to eq ["echo", ["some", "\\`bad\\ value\\`"]]
25
25
  end
26
26
 
27
27
  it "doesn't interpolate interpolation values" do
28
28
  expect(
29
29
  subject.contents(:interpolation => "{other}", :other => "hi")
30
- ).to eq ["echo", ["some", "{other}"]]
30
+ ).to eq ["echo", ["some", "\\{other\\}"]]
31
31
  end
32
32
  end
33
33
 
@@ -44,7 +44,7 @@ describe Command::Runner do
44
44
  it "doesn't escape bad values" do
45
45
  expect(
46
46
  subject.contents(:interpolation => "`bad value`")
47
- ).to eq ["echo", ["some", "`bad", "value`"]]
47
+ ).to eq ["echo", ["some", "`bad value`"]]
48
48
  end
49
49
  end
50
50
 
@@ -61,16 +61,17 @@ describe Command::Runner do
61
61
 
62
62
  context "when selecting backends" do
63
63
  it "selects the best backend" do
64
- Command::Runner::Backends::PosixSpawn.stub(:available?).and_return(false)
65
- Command::Runner::Backends::Spawn.stub(:available?).and_return(true)
66
- Command::Runner.best_backend.should be_instance_of Command::Runner::Backends::Spawn
67
-
68
- Command::Runner::Backends::PosixSpawn.stub(:available?).and_return(true)
69
- Command::Runner.best_backend.should be_instance_of Command::Runner::Backends::PosixSpawn
70
- end
71
-
72
- it "takes into account unsafe backends" do
73
- Command::Runner.best_backend(true).should be_unsafe
64
+ allow(Command::Runner::Backends::PosixSpawn).
65
+ to receive(:available?).and_return(false)
66
+ allow(Command::Runner::Backends::Spawn).
67
+ to receive(:available?).and_return(true)
68
+ expect(Command::Runner.best_backend).
69
+ to be_instance_of Command::Runner::Backends::Spawn
70
+
71
+ allow(Command::Runner::Backends::PosixSpawn).
72
+ to receive(:available?).and_return(true)
73
+ expect(Command::Runner.best_backend).
74
+ to be_instance_of Command::Runner::Backends::PosixSpawn
74
75
  end
75
76
  end
76
77
 
@@ -82,7 +83,9 @@ describe Command::Runner do
82
83
  subject.backend = Command::Runner::Backends::Backticks.new
83
84
  end
84
85
 
85
- its(:pass) { should be_no_command }
86
+ it "should result in no command" do
87
+ expect(subject.pass).to be_no_command
88
+ end
86
89
 
87
90
  it "calls the block given" do
88
91
  subject.pass do |message|
data/spec/spawn_spec.rb CHANGED
@@ -2,16 +2,16 @@ describe Command::Runner::Backends::Spawn do
2
2
 
3
3
  next unless Process.respond_to?(:spawn) && !(RUBY_PLATFORM == "java" && RUBY_VERSION =~ /\A1\.9/)
4
4
 
5
- its(:unsafe?) { should be_false }
5
+ it("is safe") { expect(described_class).to_not be_unsafe }
6
6
 
7
7
  it "is available" do
8
- Command::Runner::Backends::Spawn.should be_available
8
+ expect(Command::Runner::Backends::Spawn).to be_available
9
9
  end
10
10
 
11
11
  it "returns a message" do
12
12
  value = subject.call("echo", ["hello"])
13
- value.should be_instance_of Command::Runner::Message
14
- value.should be_executed
13
+ expect(value).to be_instance_of Command::Runner::Message
14
+ expect(value).to be_executed
15
15
  end
16
16
 
17
17
  it "doesn't block" do
@@ -19,8 +19,8 @@ describe Command::Runner::Backends::Spawn do
19
19
  value = subject.call("sleep", ["0.5"])
20
20
  end_time = Time.now
21
21
 
22
- (end_time - start_time).should be_within((1.0/100)).of(0)
23
- value.time.should be_within((3.0/100)).of(0.5)
22
+ expect(end_time - start_time).to be_within((1.0/100)).of(0)
23
+ expect(value.time).to be_within((3.0/100)).of(0.5)
24
24
  end
25
25
 
26
26
  it "doesn't expose arguments to the shell" do
@@ -29,8 +29,14 @@ describe Command::Runner::Backends::Spawn do
29
29
  expect(value.stdout).to eq "`uname -a`\n"
30
30
  end
31
31
 
32
+ it "can be unsafe" do
33
+ value = subject.call("echo", ["`uname -a`"], {}, unsafe: true)
34
+
35
+ expect(value.stdout).to_not eq "`uname -a`\n"
36
+ end
37
+
32
38
  it "can not be available" do
33
- Command::Runner::Backends::Spawn.stub(:available?).and_return(false)
39
+ allow(Command::Runner::Backends::Spawn).to receive(:available?).and_return(false)
34
40
 
35
41
  expect {
36
42
  Command::Runner::Backends::Spawn.new
@@ -0,0 +1,4 @@
1
+ RSpec.configure do |config|
2
+ config.raise_errors_for_deprecations!
3
+ config.order = 'random'
4
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Rodi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-15 00:00:00.000000000 Z
11
+ date: 2014-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: promise
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.3'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -83,13 +97,16 @@ files:
83
97
  - lib/command/runner/backends/posix_spawn.rb
84
98
  - lib/command/runner/backends/spawn.rb
85
99
  - lib/command/runner/backends/ssh.rb
100
+ - lib/command/runner/backends/unsafe_fake.rb
86
101
  - lib/command/runner/exceptions.rb
87
102
  - lib/command/runner/message.rb
88
103
  - lib/command/runner/version.rb
89
104
  - spec/backticks_spec.rb
105
+ - spec/interpolation_spec.rb
90
106
  - spec/messenger_spec.rb
91
107
  - spec/spawn_spec.rb
92
- homepage: http://github.com/redjazz96/command-runner
108
+ - spec/spec_helper.rb
109
+ homepage: http://github.com/medcat/command-runner
93
110
  licenses:
94
111
  - MIT
95
112
  metadata: {}