bwrap 1.0.0.pre.alpha4 → 1.0.0.pre.alpha5

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
  SHA256:
3
- metadata.gz: 5101d7848dccd6da3f68fc02f08b37ec1cfadc516b45cb964265a3f0a60e8ea3
4
- data.tar.gz: 6dbf98ccc5faba3385ce8fe6f9890f9e7369860531e4161c8092611c18a8ca02
3
+ metadata.gz: dfb5f9bdbcf68df6068aebca204e72ef9272829b1f8db11c34e5a4da469cc2c7
4
+ data.tar.gz: 5f19c25ecad9b7f4923f80a6d61c87c6382671667f6b39eed0198befbb8eccd2
5
5
  SHA512:
6
- metadata.gz: 690e7e3f4911d80d5384aa10fc8c7dab6ed86c597dc691a66a396efccf15dc06e3ee29586afe687e4f92670eea049d2e76e034a2147eed9a9abcb9b894c99e67
7
- data.tar.gz: d0a5312ab4ef5814885fd928f1359fd9ba69d7f36224c8671d79396ab635f1f8c23fd21a16712e62ca4b566cc51138b0f654e5cd8e3304f30cf10d492bbdc991
6
+ metadata.gz: c9682aff2b43180fee0e8d7b2b7234d4bb516e86c85dcabc5b40a1047284f9f5bf9e0641dff80483e4645b8aa4bd06c9b9de8ba1b43cd78af3b77de1060c8ad6
7
+ data.tar.gz: cadd731077ff175b86cf0d56f17dd1c2cb3123e5d9b1dcff17156da5d706d7da12274bc59297ae925c1f07ea5b3a325c999c942048c2c5b165755934ae16d2f8
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changes
2
2
 
3
+ ## 1.0.0-alpha5 (29.11.2021)
4
+
5
+ * Execution#command_available?: support absolute paths
6
+ * Execution#which: support absolute paths
7
+ * Many miscellaneous fixes
8
+
3
9
  ## 1.0.0-alpha4 (22.11.2021)
4
10
 
5
11
  * Allow use without home directory set
data/README.md CHANGED
@@ -31,3 +31,5 @@ Please see [API documentation](https://www.rubydoc.info/gems/bwrap) for usage in
31
31
  ## Contributing
32
32
 
33
33
  Bug reports and pull requests are welcome at https://git.sr.ht/~smar/ruby-bwrap.
34
+
35
+ Gerrit instance https://gerrit.smar.fi/admin/repos/ruby-bwrap can also be used.
@@ -52,7 +52,7 @@ class Bwrap::Args::Bind
52
52
  shebang = File.open @executable_path, &:readline
53
53
  if shebang[0..1] != "#!"
54
54
  warn "Executable #{@executable_name} was recognized as #{mime_type} but does not have " \
55
- "proper shebang line. Skipping automatic library mounts."
55
+ "proper shebang line. Skipping automatic library mounts."
56
56
  return false
57
57
  end
58
58
 
@@ -173,7 +173,8 @@ class Bwrap::Args::Bind
173
173
  # @warning scanelf does not play with spaces in names well. This method assumes that libraries
174
174
  # have no spaces in names, though binaries can have.
175
175
  #
176
- # @todo Ensure scanelf is available (and throw proper error if it is not, telling to not use full_system_mounts option.)
176
+ # @todo Ensure scanelf is available (and throw proper error if it is not, telling to not use
177
+ # full_system_mounts option.)
177
178
  private def libs_command_requires
178
179
  executable_name = @command.is_a?(String) && @command || @command[0]
179
180
  executable_path = which executable_name
@@ -189,11 +190,14 @@ class Bwrap::Args::Bind
189
190
  library_mounts = []
190
191
 
191
192
  library_object = Bwrap::Args::Library.new
192
- library_object.needed_libraries(mime.executable_path).each do |library|
193
+ libraries = library_object.libraries_needed_by mime.executable_path
194
+
195
+ # TODO: following is bad?
196
+ #library_object.needed_libraries(mime.executable_path).each do |library|
197
+ libraries.each do |library|
193
198
  library_mounts << "--ro-bind" << library << library
194
199
  end
195
200
 
196
201
  @args.append library_mounts
197
202
  end
198
-
199
203
  end
@@ -10,6 +10,39 @@ require_relative "library"
10
10
  class Bwrap::Args::Features < Hash
11
11
  include Bwrap::Output
12
12
 
13
+ # @api internal
14
+ class RubyBinds
15
+ attr_reader :mounts
16
+
17
+ # Bind system paths so scripts works inside sandbox.
18
+ def sitedir_mounts
19
+ mounts = []
20
+ mounts << "--ro-bind" << RbConfig::CONFIG["sitedir"] << RbConfig::CONFIG["sitedir"]
21
+ mounts << "--ro-bind" << RbConfig::CONFIG["rubyhdrdir"] << RbConfig::CONFIG["rubyhdrdir"]
22
+ mounts << "--ro-bind" << RbConfig::CONFIG["rubylibdir"] << RbConfig::CONFIG["rubylibdir"]
23
+ mounts << "--ro-bind" << RbConfig::CONFIG["vendordir"] << RbConfig::CONFIG["vendordir"]
24
+
25
+ mounts
26
+ end
27
+
28
+ # Create binds for required system libraries.
29
+ #
30
+ # These are in path like /usr/lib64/ruby/2.5.0/x86_64-linux-gnu/,
31
+ # and as they are mostly shared libraries, they may have some extra
32
+ # dependencies that also need to be bound inside the sandbox.
33
+ def stdlib_mounts stdlib
34
+ library_mounts = []
35
+ library = Bwrap::Args::Library.new
36
+ stdlib.each do |lib|
37
+ path = "#{RbConfig::CONFIG["rubyarchdir"]}/#{lib}.so"
38
+
39
+ library.needed_libraries(path).each do |requisite_library|
40
+ library_mounts << "--ro-bind" << requisite_library << requisite_library
41
+ end
42
+ end
43
+ end
44
+ end
45
+
13
46
  # {Array} of parameters passed to bwrap.
14
47
  attr_writer :args
15
48
 
@@ -20,36 +53,14 @@ class Bwrap::Args::Features < Hash
20
53
  ruby_binds
21
54
  end
22
55
 
23
- # @note This does not allow development headers needed for compilation for now. I’ll look at it after I have an use for it.
56
+ # @note This does not allow development headers needed for compilation for now.
57
+ # I’ll look at it after I have an use for it.
24
58
  private def ruby_binds
25
59
  return unless @config.features.ruby.enabled?
26
60
 
27
- # Bind system paths so scripts works inside sandbox.
28
-
29
- mounts = []
30
- mounts << "--ro-bind" << RbConfig::CONFIG["sitedir"] << RbConfig::CONFIG["sitedir"]
31
- mounts << "--ro-bind" << RbConfig::CONFIG["rubyhdrdir"] << RbConfig::CONFIG["rubyhdrdir"]
32
- mounts << "--ro-bind" << RbConfig::CONFIG["rubylibdir"] << RbConfig::CONFIG["rubylibdir"]
33
- mounts << "--ro-bind" << RbConfig::CONFIG["vendordir"] << RbConfig::CONFIG["vendordir"]
34
-
35
- @args.append mounts
36
-
37
- # Create binds for required system libraries.
38
- #
39
- # These are in path like /usr/lib64/ruby/2.5.0/x86_64-linux-gnu/,
40
- # and as they are mostly shared libraries, they may have some extra
41
- # dependencies that also need to be bound inside the sandbox.
42
-
43
- library_mounts = []
44
- library = Bwrap::Args::Library.new
45
- @config.features.ruby.stdlib.each do |lib|
46
- path = "#{RbConfig::CONFIG["rubyarchdir"]}/#{lib}.so"
47
-
48
- library.needed_libraries(path).each do |library|
49
- library_mounts << "--ro-bind" << library << library
50
- end
51
- end
61
+ binds = RubyBinds.new
52
62
 
53
- @args.append library_mounts
63
+ @args.append binds.sitedir_mounts
64
+ @args.append binds.stdlib_mounts @config.features.ruby.stdlib
54
65
  end
55
66
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require "bwrap/execution"
3
4
  require "bwrap/output"
@@ -10,14 +11,65 @@ class Bwrap::Args::Library
10
11
  include Bwrap::Execution
11
12
  include Bwrap::Output
12
13
 
14
+ # NOTE: This caching can be made more efficient, but need to look at it later, what to do about it.
15
+ @@needed_libraries_cache ||= []
16
+
17
+ # Otherwise similar to {#needed_libraries}, but checks used libc to handle musl executables.
18
+ #
19
+ # @param executable [String] Path to the executable to find dependencies for
20
+ def libraries_needed_by executable
21
+ # %i == interpreter, the library used to load the executable by kernel.
22
+ # %F == Path to given file.
23
+ output_format = "%i::SEPARATOR::%F"
24
+ scanelf_command = %W{ scanelf --nobanner --quiet --format #{output_format} }
25
+ scanelf_command << executable
26
+
27
+ data = execvalue scanelf_command
28
+ data = data.strip
29
+ interpreter, _executable_path = data.split "::SEPARATOR::"
30
+ interpreter = Pathname.new interpreter
31
+
32
+ if interpreter.basename.to_s[0..6] == "ld-musl"
33
+ musl_needed_libraries executable
34
+ else
35
+ # For glibc, scanelf can return full paths for us most of time.
36
+ needed_libraries executable
37
+ end
38
+ end
39
+
40
+ # @param binary_paths [String, Array] one or more paths to be resolved
41
+ #
42
+ # @todo Maybe caching should be done here too?
43
+ def musl_needed_libraries binary_paths
44
+ trace "Finding musl libraries #{binary_paths} requires"
45
+ @needed_libraries = []
46
+
47
+ if binary_paths.is_a? String
48
+ binary_paths = [ binary_paths ]
49
+ end
50
+
51
+ binary_paths.each do |binary_path|
52
+ output = execvalue %W{ ldd #{binary_path} }
53
+ lines = output.split "\n"
54
+ _interpreter_line = lines.shift
55
+
56
+ lines.each do |line|
57
+ parse_ldd_line line
58
+ end
59
+ end
60
+
61
+ @needed_libraries
62
+ end
63
+
13
64
  # Used by {Bwrap::Args::Bind#libs_command_requires}.
65
+ #
66
+ # @param binary_paths [String, Array] one or more paths to be resolved
14
67
  def needed_libraries binary_paths
15
68
  trace "Finding libraries #{binary_paths} requires"
16
69
  @needed_libraries = []
17
- # NOTE: This caching can be made more efficient, but need to look at it later, what to do about it.
18
- @@needed_libraries_cache ||= []
19
70
 
20
- output_format = "%F:libraries:%n"
71
+ # %i == interpreter, the library used to load the executable by kernel.
72
+ output_format = "%F::SEPARATOR::%n"
21
73
  scanelf_command = %W{ scanelf --nobanner --quiet --format #{output_format} --ldcache --needed }
22
74
 
23
75
  if binary_paths.is_a? String
@@ -44,7 +96,7 @@ class Bwrap::Args::Library
44
96
 
45
97
  # Used by {#needed_libraries}.
46
98
  private def parse_scanelf_line line
47
- binary_path, libraries_line = line.split ":libraries:"
99
+ binary_path, libraries_line = line.split "::SEPARATOR::"
48
100
  libraries = libraries_line.split ","
49
101
 
50
102
  @needed_libraries += libraries
@@ -59,5 +111,20 @@ class Bwrap::Args::Library
59
111
  @@needed_libraries_cache << library
60
112
  end
61
113
  end
114
+
115
+ # Used by {#musl_needed_libraries}.
116
+ private def parse_ldd_line line
117
+ line = line.strip
118
+ _library_name, library_data = line.split " => "
119
+
120
+ matches = library_data.match(/(.*) \(0x[0-9a-f]+\)/)
121
+ library_path = matches[1]
122
+
123
+ @needed_libraries << library_path
124
+
125
+ # Also check if requisite libraries needs some libraries.
126
+ inner = Bwrap::Args::Library.new
127
+ @needed_libraries += inner.musl_needed_libraries library_path
128
+ end
62
129
  end
63
130
  # class Library ended
data/lib/bwrap/config.rb CHANGED
@@ -100,7 +100,8 @@ class Bwrap::Config
100
100
  #
101
101
  # Among others, binds RbConfig::CONFIG["sitedir"] so scripts works.
102
102
  #
103
- # @note This does not allow development headers needed for compilation for now. I’ll look at it after I have an use for it.
103
+ # @note This does not allow development headers needed for compilation for now.
104
+ # I’ll look at it after I have an use for it.
104
105
  def enable
105
106
  @enabled = true
106
107
  end
@@ -95,7 +95,7 @@ class Bwrap::Execution::Execute
95
95
  # Stub to instruct implementation in subclass.
96
96
  def self.prepend_rootcmd command, rootcmd:
97
97
  raise NotImplementedError, "If rootcmd execution is necessary, monkey patch Bwrap::Execution::Execute " \
98
- "to add “self.prepend_rootcmd(command, rootcmd:)” method."
98
+ "to add “self.prepend_rootcmd(command, rootcmd:)” method."
99
99
  end
100
100
 
101
101
  # Used by `#handle_logging`.
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bwrap/output"
4
+
5
+ # Path checking methods.
6
+ module Bwrap::Execution::Path
7
+ # @api internal
8
+ class Environment
9
+ def self.each_env_path command
10
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [ "" ]
11
+
12
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |env_path|
13
+ exts.each do |ext|
14
+ exe = File.join(env_path, "#{command}#{ext}")
15
+ yield exe
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # Check if requested program can be found.
22
+ #
23
+ # Should work cross-platform and in restricted environents pretty well.
24
+ private def command_available? command
25
+ # Special handling for absolute paths.
26
+ path = Pathname.new command
27
+ if path.absolute?
28
+ if path.executable? && !path.directory?
29
+ return true
30
+ end
31
+
32
+ return false
33
+ end
34
+
35
+ Bwrap::Execution::Path::Environment.each_env_path command do |exe|
36
+ return true if File.executable?(exe) && !File.directory?(exe)
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ # Returns path to given executable.
43
+ private def which command, fail: true
44
+ # Special handling for absolute paths.
45
+ path = Pathname.new command
46
+ if path.absolute?
47
+ if path.executable?
48
+ return command
49
+ end
50
+
51
+ raise CommandNotFound.new command: command if fail
52
+
53
+ return nil
54
+ end
55
+
56
+ Bwrap::Execution::Path::Environment.each_env_path command do |exe|
57
+ return exe if File.executable?(exe) && !File.directory?(exe)
58
+ end
59
+
60
+ return nil unless fail
61
+
62
+ raise CommandNotFound.new command: command
63
+ end
64
+ end
@@ -3,12 +3,14 @@
3
3
  require "bwrap/version"
4
4
  require "bwrap/output"
5
5
  require_relative "execution/execute"
6
+ require_relative "execution/path"
6
7
 
7
8
  # @abstract Module to be included in a class that needs to execute commands.
8
9
  #
9
10
  # Methods to execute processes.
10
11
  module Bwrap::Execution
11
12
  include Bwrap::Output
13
+ include Bwrap::Execution::Path
12
14
 
13
15
  # Unspecified execution related error.
14
16
  class CommandError < StandardError
@@ -18,6 +20,19 @@ module Bwrap::Execution
18
20
  class ExecutionFailed < CommandError
19
21
  end
20
22
 
23
+ # Thrown if given command was not found.
24
+ class CommandNotFound < CommandError
25
+ # Command that was looked at.
26
+ attr_reader :command
27
+
28
+ def initialize command:
29
+ @command = command
30
+ msg = "Failed to find #{command} from PATH."
31
+
32
+ super msg
33
+ end
34
+ end
35
+
21
36
  # Actual implementation of execution command. Can be used when static method is needed.
22
37
  #
23
38
  # @note When an array is given as a command, empty strings are passed as empty arguments.
@@ -83,33 +98,6 @@ module Bwrap::Execution
83
98
  @last_status
84
99
  end
85
100
 
86
- # Check if requested program can be found.
87
- #
88
- # Should work cross-platform and in restricted environents pretty well.
89
- private def command_available? command
90
- exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [ "" ]
91
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
92
- exts.each do |ext|
93
- exe = File.join(path, "#{command}#{ext}")
94
- return true if File.executable?(exe) && !File.directory?(exe)
95
- end
96
- end
97
- false
98
- end
99
-
100
- # Returns path to given executable.
101
- private def which command, fail: true
102
- exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [ "" ]
103
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
104
- exts.each do |ext|
105
- exe = File.join(path, "#{command}#{ext}")
106
- return exe if File.executable?(exe) && !File.directory?(exe)
107
- end
108
- end
109
- error "Failed to find #{command} from PATH." if fail
110
- nil
111
- end
112
-
113
101
  # Execute a command.
114
102
  #
115
103
  # This method can be used by including Execution module in a class that should be able to
data/lib/bwrap/output.rb CHANGED
@@ -81,13 +81,16 @@ module Bwrap::Output
81
81
  # Aborts current process.
82
82
  #
83
83
  # Use this instead of Ruby’s #exit in order to filter out dummy #exit calls from the code.
84
- def self.error_output str = nil, label: :unspecified, log_callback: 1
84
+ def self.error_output str = nil, label: :unspecified, log_callback: 1, raise_exception: false
85
85
  unless str.nil?
86
86
  out = Bwrap::Output::Levels.error_print_formatted str, log_callback: (log_callback + 1)
87
87
  Bwrap::Output::Log.puts_to_log out
88
88
  end
89
89
 
90
- exit Bwrap::Execution::Labels.resolve_exit_code(label)
90
+ exit_code = Bwrap::Execution::Labels.resolve_exit_code(label)
91
+ raise str if raise_exception
92
+
93
+ exit exit_code
91
94
  end
92
95
 
93
96
  # @return true if --verbose, --debug or --trace has been passed, false if not.
@@ -147,7 +150,8 @@ module Bwrap::Output
147
150
  #
148
151
  # @param str String to be outputted
149
152
  # @param label [Symbol] Exit label accepted by {Bwrap::Execution.resolve_exit_code}
150
- private def error str = nil, label: :unspecified
151
- Bwrap::Output.error_output(str, label: label, log_callback: 2)
153
+ # @param raise [Boolean] if true, an exception is raised instead of just existing with exit code.
154
+ private def error str = nil, label: :unspecified, raise_exception: false
155
+ Bwrap::Output.error_output(str, label: label, log_callback: 2, raise_exception: raise_exception)
152
156
  end
153
157
  end
data/lib/bwrap/version.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # bwrap command runner tools.
4
4
  module Bwrap
5
5
  # Current version of bwrap.
6
- VERSION = "1.0.0-alpha4"
6
+ VERSION = "1.0.0-alpha5"
7
7
  end
8
8
 
9
9
  require "deep-cover" if ENV["DEEP_COVER"]
data/lib/bwrap.rb CHANGED
@@ -19,7 +19,7 @@ class Bwrap::Bwrap
19
19
 
20
20
  # Parses command line arguments given to caller script.
21
21
  def parse_command_line_arguments
22
- options = optimist_cli_args
22
+ options = parse_options
23
23
 
24
24
  Bwrap::Output.handle_output_options options
25
25
  end
@@ -44,8 +44,12 @@ class Bwrap::Bwrap
44
44
  end
45
45
 
46
46
  # Parses global bwrap flags using Optimist.
47
- private def optimist_cli_args
48
- Optimist.options do
47
+ #
48
+ # Sets instance variable `@cli_args`.
49
+ #
50
+ # @return [Hash] options parsed by {Optimist.options}
51
+ private def parse_options
52
+ options = Optimist.options do
49
53
  version ::Bwrap::VERSION
50
54
 
51
55
  banner "Usage:"
@@ -71,5 +75,7 @@ class Bwrap::Bwrap
71
75
  end
72
76
 
73
77
  @cli_args = ARGV.dup
78
+
79
+ options
74
80
  end
75
81
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bwrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha4
4
+ version: 1.0.0.pre.alpha5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samu Voutilainen
@@ -34,7 +34,7 @@ cert_chain:
34
34
  X4ioQwEn1/9tHs19VO1CLF58451HgEo1BXd7eWLmV1V5cqw0YWok1ly4L/Su/Phf
35
35
  MRxVMHiVAqY=
36
36
  -----END CERTIFICATE-----
37
- date: 2021-11-22 00:00:00.000000000 Z
37
+ date: 2021-11-29 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: optimist
@@ -56,14 +56,14 @@ dependencies:
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: '1.17'
59
+ version: '1.16'
60
60
  type: :development
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: '1.17'
66
+ version: '1.16'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +145,7 @@ files:
145
145
  - lib/bwrap/execution/execute.rb
146
146
  - lib/bwrap/execution/execution.rb
147
147
  - lib/bwrap/execution/labels.rb
148
+ - lib/bwrap/execution/path.rb
148
149
  - lib/bwrap/output.rb
149
150
  - lib/bwrap/output/colors.rb
150
151
  - lib/bwrap/output/levels.rb
@@ -158,6 +159,7 @@ metadata:
158
159
  homepage_uri: https://git.sr.ht/~smar/ruby-bwrap
159
160
  source_code_uri: https://git.sr.ht/~smar/ruby-bwrap
160
161
  changelog_uri: https://git.sr.ht/~smar/ruby-bwrap/tree/master/item/CHANGELOG.md
162
+ rubygems_mfa_required: 'false'
161
163
  post_install_message:
162
164
  rdoc_options: []
163
165
  require_paths:
metadata.gz.sig CHANGED
Binary file