backticks 0.4.0 → 0.5.0

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: 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