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

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