command-runner 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: {}