backticks 0.4.0 → 0.5.0

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: 156e6dd6cd59ca1201a48b9679e1f6151caf6b43
4
- data.tar.gz: 225c905201c500af8cde4a7561490befaf797caa
3
+ metadata.gz: 8199c03291828b81d31df59a9e3341d20b13b27a
4
+ data.tar.gz: 5a4058b1ada73100e623660cf0c201f1aba3accb
5
5
  SHA512:
6
- metadata.gz: b7230c03f69e71a6aeadb1cd0f3d43f0a12ad166231d0aecaea8c9c41bf8373e3cb5e3fbdfaf8ddb08be35c4e0632404edafb4b9de36e55213e6166f19134a1d
7
- data.tar.gz: bb4bfa7199f80179fba4147379667b9e4e80ad2b8d3208e4d072953aaffbc2d42e4c1c8f52ce46059bec9c0b978600c3661b0650c23f2b7330da1fac7d9ddd8f
6
+ metadata.gz: 943ceaf445259b9c4fc67e94652fa5be8d613496abf198578842b81a97d5bb782d371bc5ac1b519c8143850fce1290a7f8e1245989c1a3cc71c76c4da237c279
7
+ data.tar.gz: 2c6ac11905c9a76836acb555d8819f74947fbf90fa42a0104bdb8823b2b3dff36d44bda9df56206cd9676ced6d5df65a24b201d1aa2d23295968e8d62b8f9c43
data/README.md CHANGED
@@ -111,6 +111,18 @@ include Backticks::Ext
111
111
 
112
112
  If you do this, I will hunt you down and scoff at you. You have been warned!
113
113
 
114
+ ## Security
115
+
116
+ Backticks avoids using your OS shell, which helps prevent security bugs.
117
+ This also means that you can't pass strings such as "$HOME" to commands;
118
+ Backticks does not perform shell substitution. Pass ENV['HOME'] instead.
119
+
120
+ Be careful about the commands you pass to Backticks! Never run commands that
121
+ you read from an untrusted source, e.g. the network.
122
+
123
+ In the future, Backticks may integrate with Ruby's $SAFE level to provide smart
124
+ escaping and shell safety.
125
+
114
126
  ## Development
115
127
 
116
128
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -21,7 +21,10 @@ module Backticks
21
21
  attr_reader :pid
22
22
 
23
23
  # @return [String] all data captured (so far) from child's stdin/stdout/stderr
24
- attr_reader :captured_input, :captured_output, :captured_error, :status
24
+ attr_reader :captured_input, :captured_output, :captured_error
25
+
26
+ # @return [nil,Process::Status] result of command if it has ended; nil if still running
27
+ attr_reader :status
25
28
 
26
29
  # Watch a running command.
27
30
  def initialize(pid, stdin, stdout, stderr)
@@ -93,12 +96,12 @@ module Backticks
93
96
  # proxy STDIN to child's stdin
94
97
  if ready && ready.include?(STDIN)
95
98
  input = STDIN.readpartial(CHUNK) rescue nil
96
- @captured_input << input
97
99
  if input
100
+ @captured_input << input
98
101
  @stdin.write(input)
99
102
  else
100
103
  # our own STDIN got closed; proxy this fact to the child
101
- @stdin.close
104
+ @stdin.close unless @stdin.closed?
102
105
  end
103
106
  end
104
107
 
@@ -1,12 +1,16 @@
1
1
  require 'pty'
2
+ require 'open3'
2
3
 
3
4
  module Backticks
4
5
  # An easy-to-use interface for invoking commands and capturing their output.
5
6
  # Instances of Runner can be interactive, which prints the command's output
6
7
  # to the terminal and also allows the user to interact with the command.
7
- # They can also be unbuffered, which uses a pseudo-tty to capture the
8
- # command's output with no delay or
8
+ # By default commands are unbuffered, using a pseudoterminal to capture
9
+ # the output with no delay.
9
10
  class Runner
11
+ # Default streams to buffer if someone calls bufferered= with Boolean.
12
+ BUFFERED = [:stdin, :stdout, :stderr].freeze
13
+
10
14
  # If true, commands will have their stdio streams tied to the parent
11
15
  # process so the user can view their output and send input to them.
12
16
  # Commands' output is still captured normally when they are interactive.
@@ -21,18 +25,25 @@ module Backticks
21
25
  # @return [Boolean]
22
26
  attr_accessor :interactive
23
27
 
24
- # If true, commands will be invoked with a pseudo-TTY for stdout in order
25
- # to capture output as it is generated instead of waiting for pipe buffers
26
- # to fill.
28
+ # List of I/O streams that should be captured using a pipe instead of
29
+ # a pseudoterminal.
27
30
  #
28
- # @return [Boolean]
29
- attr_accessor :buffered
31
+ # This may be a Boolean, or it may be an Array of stream names from the
32
+ # set [:stdin, stdout, stderr].
33
+ #
34
+ # @return [Array] list of symbolic stream names
35
+ attr_reader :buffered
30
36
 
31
37
  # @return [#parameters] the CLI-translation object used by this runner
32
38
  attr_reader :cli
33
39
 
34
40
  # Create an instance of Runner.
35
- # @param [#parameters] cli object used to convert Ruby method parameters into command-line parameters
41
+ # @option [#include?,Boolean] buffered list of names; true/false for all/none
42
+ # @option [#parameters] cli command-line parameter translator
43
+ # @option [Boolean] interactive true to tie parent stdout/stdin to child
44
+ #
45
+ # @example buffer stdout
46
+ # Runner.new(buffered:[:stdout])
36
47
  def initialize(options={})
37
48
  options = {
38
49
  :buffered => false,
@@ -40,9 +51,19 @@ module Backticks
40
51
  :interactive => false,
41
52
  }.merge(options)
42
53
 
43
- @buffered = options[:buffered]
44
54
  @cli = options[:cli]
45
- @interactive = options[:interactive]
55
+ self.buffered = options[:buffered]
56
+ self.interactive = options[:interactive]
57
+ end
58
+
59
+ # @param [Array,Boolean] buffered list of symbolic stream names; true/false for all/none
60
+ def buffered=(b)
61
+ @buffered = case b
62
+ when true then BUFFERED
63
+ when false, nil then []
64
+ else
65
+ b
66
+ end
46
67
  end
47
68
 
48
69
  # @deprecated
@@ -79,50 +100,32 @@ module Backticks
79
100
  # remaining elements are parameters and flags
80
101
  # @return [Command] the running command
81
102
  def run_without_sugar(argv)
82
- if self.buffered
83
- run_buffered(argv)
103
+ stdin_r, stdin = if buffered.include?(:stdin) && !interactive
104
+ IO.pipe
84
105
  else
85
- run_unbuffered(argv)
106
+ PTY.open
107
+ end
108
+ stdout, stdout_w = if buffered.include?(:stdout) && !interactive
109
+ IO.pipe
110
+ else
111
+ PTY.open
112
+ end
113
+ stderr, stderr_w = if buffered.include?(:stderr)
114
+ IO.pipe
115
+ else
116
+ PTY.open
86
117
  end
87
- end
88
118
 
89
- # Run a command. Use a pty to capture the unbuffered output.
90
- #
91
- # @param [Array] argv command to run; argv[0] is program name and the
92
- # remaining elements are parameters and flags
93
- # @return [Command] the running command
94
- private
95
- def run_unbuffered(argv)
96
- stdout, stdout_w = PTY.open
97
- stdin_r, stdin = PTY.open
98
- stderr, stderr_w = PTY.open
99
119
  pid = spawn(*argv, in: stdin_r, out: stdout_w, err: stderr_w)
100
120
  stdin_r.close
101
121
  stdout_w.close
102
122
  stderr_w.close
103
- unless @interactive
123
+ unless interactive
104
124
  stdin.close
105
125
  stdin = nil
106
126
  end
107
127
 
108
128
  Command.new(pid, stdin, stdout, stderr)
109
129
  end
110
-
111
- # Run a command. Perform no translation or substitution. Use a pipe
112
- # to read the output, which may be buffered by the OS. Return the program's
113
- # exit status and stdout.
114
- #
115
- # @param [Array] argv command to run; argv[0] is program name and the
116
- # remaining elements are command-line arguments.
117
- # @return [Command] the running command
118
- def run_buffered(argv)
119
- stdin, stdout, stderr, thr = Open3.popen3(*argv)
120
- unless @interactive
121
- stdin.close
122
- stdin = nil
123
- end
124
-
125
- Command.new(thr.pid, stdin, stdout, stderr)
126
- end
127
130
  end
128
131
  end
@@ -1,3 +1,3 @@
1
1
  module Backticks
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backticks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Spataro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-27 00:00:00.000000000 Z
11
+ date: 2016-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler