arr-pm 0.0.11 → 0.0.12

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: ceb08ab00b24c659ee4f0fd42d8f5c8ad77416ffff3f89e8ee18ec46ddc58383
4
- data.tar.gz: b53d4d64c24362fb2cf27606842d8352e9f7d184f9ba3b76eb1e33b3e13b2775
3
+ metadata.gz: 1b18d6ce9e276f133f3456d3f680c9727936e0be921d1f2492f9541aeefcbca9
4
+ data.tar.gz: d5bca7b579c4ff1f28b411a0d5195a83d34c023a749d563cbc95b9cb0bb5716b
5
5
  SHA512:
6
- metadata.gz: 441b7c7a0d0851c3c4cadaf7e2e0f20d32388ba4df722ee3f76bfb3a34bb072739daf63739cf19e9d8f221a40a80f999ca47b2825043ded493d1f3363dec3e54
7
- data.tar.gz: 815d8fff66bc8672b7e212f6c138795a9029b5e78032d26051ac114e306084f97d01519060427aeb50e9bca29dd49af5d5567370904d8881fc99179184811cd3
6
+ metadata.gz: 58e476d730ac09d22ea1ee60cf463f3cc26b4cb3cc99f1f8b44d7cd323084d3ca1208a550b069f56afb2414759b9f3e5475716dfee0f2363ae5a10dc0235ae38
7
+ data.tar.gz: 361d8dfca458258a617e71153ff8e757c4f8963abe5d07d9020a120407f52059a2f3b8bd0a3e2495f9764a2cc02f18d4755d2ba532f670d16c1adfc3eb85a3cb
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+
2
+
3
+ # v0.0.12
4
+
5
+ * Security: Fixed a safety problem which would allow for arbitrary shell execution when invoking `RPM::File#extract` or `RPM::File#files`. (Jordan Sissel, @joernchen; #14, #15)
6
+ * This library now has no external dependencies. (Jordan Sissel, #18)
7
+ * `RPM::File#extract` now correctly works when the target directory contains spaces or other special characters. (@joernchen, #19)
8
+ * Listing files (`RPM::File#files`) no longer requires external tools like `cpio` (Jordan Sissel, #17)
9
+
10
+
11
+ # v0.0.11
12
+
13
+ * Support Ruby 3.0 (Alexey Morozov, #12)
14
+ * Fix bug caused when listing config_files on an rpm with no config files. (Daniel Jay Haskin, #7)
15
+
16
+ # Older versions
17
+
18
+ Changelog not tracked for older versions.
data/arr-pm.gemspec CHANGED
@@ -2,14 +2,13 @@ Gem::Specification.new do |spec|
2
2
  files = %x{git ls-files}.split("\n")
3
3
 
4
4
  spec.name = "arr-pm"
5
- spec.version = "0.0.11"
5
+ spec.version = "0.0.12"
6
6
  spec.summary = "RPM reader and writer library"
7
7
  spec.description = "This library allows to you to read and write rpm " \
8
8
  "packages. Written in pure ruby because librpm is not available " \
9
9
  "on all systems"
10
10
  spec.license = "Apache 2"
11
11
 
12
- spec.add_dependency "cabin", ">0" # for logging. apache 2 license
13
12
  spec.files = files
14
13
  spec.require_paths << "lib"
15
14
  spec.bindir = "bin"
@@ -18,6 +17,9 @@ Gem::Specification.new do |spec|
18
17
  spec.email = ["jls@semicomplete.com"]
19
18
 
20
19
  spec.add_development_dependency "flores", ">0"
20
+ spec.add_development_dependency "rspec", ">3.0.0"
21
+ spec.add_development_dependency "stud", ">=0.0.23"
22
+ spec.add_development_dependency "insist", ">=1.0.0"
21
23
  #spec.homepage = "..."
22
24
  end
23
25
 
@@ -1,9 +1,7 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "namespace"))
2
2
  require File.join(File.dirname(__FILE__), "tag")
3
- require "cabin"
4
3
 
5
4
  class RPM::File::Header
6
- include Cabin::Inspectable
7
5
  attr_reader :tags
8
6
  attr_reader :length
9
7
 
@@ -1,8 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "namespace"))
2
- require "cabin"
3
2
 
4
3
  class RPM::File::Lead
5
- include Cabin::Inspectable
6
4
 
7
5
  #struct rpmlead {
8
6
  attr_accessor :magic #unsigned char magic[4];
@@ -1,8 +1,6 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "..", "namespace"))
2
- require "cabin"
3
2
 
4
3
  class RPM::File::Tag
5
- include Cabin::Inspectable
6
4
 
7
5
  attr_accessor :tag
8
6
  attr_accessor :type
data/lib/arr-pm/file.rb CHANGED
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), "file", "header")
3
3
  require File.join(File.dirname(__FILE__), "file", "lead")
4
4
  require File.join(File.dirname(__FILE__), "file", "tag")
5
5
  require "fcntl"
6
+ require "shellwords"
6
7
 
7
8
  # Much of the code here is derived from knowledge gained by reading the rpm
8
9
  # source code, but mostly it started making more sense after reading this site:
@@ -88,6 +89,14 @@ class RPM::File
88
89
  return @payload
89
90
  end # def payload
90
91
 
92
+ def valid_compressor?(name)
93
+ # I scanned rpm's rpmio.c for payload implementation names and found the following.
94
+ # sed -rne '/struct FDIO_s \w+ *= *\{/{ n; s/^.*"(\w+)",$/\1/p }' rpmio/rpmio.c
95
+ # It's possible this misses some supported rpm payload compressors.
96
+
97
+ [ "gzip", "bzip2", "xz", "lzma", "zstd" ].include?(name)
98
+ end
99
+
91
100
  # Extract this RPM to a target directory.
92
101
  #
93
102
  # This should have roughly the same effect as:
@@ -97,8 +106,13 @@ class RPM::File
97
106
  if !File.directory?(target)
98
107
  raise Errno::ENOENT.new(target)
99
108
  end
109
+
110
+ compressor = tags[:payloadcompressor]
111
+ if !valid_compressor?(compressor)
112
+ raise "Cannot decompress. This RPM uses an invalid compressor '#{compressor}'"
113
+ end
100
114
 
101
- extractor = IO.popen("#{tags[:payloadcompressor]} -d | (cd #{target}; cpio -i --quiet --make-directories)", "w")
115
+ extractor = IO.popen("#{compressor} -d | (cd #{Shellwords.escape(target)}; cpio -i --quiet --make-directories)", "w")
102
116
  buffer = ""
103
117
  begin
104
118
  buffer.force_encoding("BINARY")
@@ -195,49 +209,13 @@ class RPM::File
195
209
  #
196
210
  # % rpm2cpio blah.rpm | cpio -it
197
211
  def files
198
- return @files unless @files.nil?
199
-
200
- lister = IO.popen("#{tags[:payloadcompressor]} -d | cpio -it --quiet", "r+")
201
- buffer = ""
202
- begin
203
- buffer.force_encoding("BINARY")
204
- rescue NoMethodError
205
- # Do Nothing
206
- end
207
- payload_fd = payload.clone
208
- output = ""
209
- loop do
210
- data = payload_fd.read(16384, buffer)
211
- break if data.nil? # listerextractor.write(data)
212
- lister.write(data)
213
-
214
- # Read output from the pipe.
215
- begin
216
- output << lister.read_nonblock(16384)
217
- rescue Errno::EAGAIN
218
- # Nothing to read, move on!
219
- end
220
- end
221
- lister.close_write
222
-
223
- # Read remaining output
224
- begin
225
- output << lister.read
226
- rescue Errno::EAGAIN
227
- # Because read_nonblock enables NONBLOCK the 'lister' fd,
228
- # and we may have invoked a read *before* cpio has started
229
- # writing, let's keep retrying this read until we get an EOF
230
- retry
231
- rescue EOFError
232
- # At EOF, hurray! We're done reading.
233
- end
234
-
235
- # Split output by newline and strip leading "."
236
- @files = output.split("\n").collect { |s| s.gsub(/^\./, "") }
237
- return @files
238
- ensure
239
- lister.close unless lister.nil?
240
- payload_fd.close unless payload_fd.nil?
212
+ # RPM stores the file metadata split across multiple tags.
213
+ # A single path's filename (with no directories) is stored in the "basename" tag.
214
+ # The directory a file lives in is stored in the "dirnames" tag
215
+ # We can find out what directory a file is in using the "dirindexes" tag.
216
+ #
217
+ # We can join each entry of dirnames and basenames to make the full filename.
218
+ return tags[:basenames].zip(tags[:dirindexes]).map { |name, i| File.join(tags[:dirnames][i], name) }
241
219
  end # def files
242
220
 
243
221
  def mask?(value, mask)
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ require "arr-pm/file"
4
+ require "stud/temporary"
5
+ require "insist"
6
+
7
+ describe ::RPM::File do
8
+ subject { described_class.new(path) }
9
+
10
+ context "with a known good rpm" do
11
+ let(:path) { File.join(File.dirname(__FILE__), "../fixtures/pagure-mirror-5.13.2-5.fc35.noarch.rpm") }
12
+
13
+ context "#files" do
14
+ let(:files) { [
15
+ "/usr/lib/systemd/system/pagure_mirror.service",
16
+ "/usr/share/licenses/pagure-mirror",
17
+ "/usr/share/licenses/pagure-mirror/LICENSE"
18
+ ]}
19
+
20
+ it "should have the correct list of files" do
21
+ expect(subject.files).to eq(files)
22
+ end
23
+ end
24
+ end
25
+
26
+ context "#extract" do
27
+ # This RPM should be correctly built, but we will modify the tags at runtime to force an error.
28
+ let(:path) { File.join(File.dirname(__FILE__), "../fixtures/example-1.0-1.x86_64.rpm") }
29
+ let(:dir) { dir = Stud::Temporary.directory }
30
+
31
+ after do
32
+ FileUtils.rm_rf(dir)
33
+ end
34
+
35
+ context "with an invalid payloadcompressor" do
36
+ before do
37
+ subject.tags[:payloadcompressor] = "some invalid | string"
38
+ end
39
+
40
+ it "should raise an error" do
41
+ insist { subject.extract(dir) }.raises(RuntimeError)
42
+ end
43
+ end
44
+
45
+ [ "gzip", "bzip2", "xz", "lzma", "zstd" ].each do |name|
46
+ context "with a '#{name}' payloadcompressor" do
47
+ before do
48
+ subject.tags[:payloadcompressor] = name
49
+ end
50
+
51
+ it "should succeed" do
52
+ reject { subject.extract(dir) }.raises(RuntimeError)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
metadata CHANGED
@@ -1,23 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arr-pm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Sissel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-15 00:00:00.000000000 Z
11
+ date: 2022-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: cabin
14
+ name: flores
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- type: :runtime
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
@@ -25,19 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: flores
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 3.0.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: stud
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.23
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.23
55
+ - !ruby/object:Gem::Dependency
56
+ name: insist
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
41
69
  description: This library allows to you to read and write rpm packages. Written in
42
70
  pure ruby because librpm is not available on all systems
43
71
  email:
@@ -46,11 +74,10 @@ executables: []
46
74
  extensions: []
47
75
  extra_rdoc_files: []
48
76
  files:
49
- - ".batcave/manifest"
50
77
  - ".gitignore"
51
78
  - ".rubocop.yml"
79
+ - CHANGELOG.md
52
80
  - Gemfile
53
- - Guardfile
54
81
  - LICENSE
55
82
  - Makefile
56
83
  - README.md
@@ -77,6 +104,8 @@ files:
77
104
  - spec/arr-pm/v2/lead_spec.rb
78
105
  - spec/fixtures/example-1.0-1.x86_64.rpm
79
106
  - spec/fixtures/example.json
107
+ - spec/fixtures/pagure-mirror-5.13.2-5.fc35.noarch.rpm
108
+ - spec/rpm/file_spec.rb
80
109
  homepage:
81
110
  licenses:
82
111
  - Apache 2
@@ -97,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
126
  - !ruby/object:Gem::Version
98
127
  version: '0'
99
128
  requirements: []
100
- rubygems_version: 3.2.15
129
+ rubygems_version: 3.3.3
101
130
  signing_key:
102
131
  specification_version: 4
103
132
  summary: RPM reader and writer library
data/.batcave/manifest DELETED
@@ -1,5 +0,0 @@
1
- ---
2
- things:
3
- ruby:
4
- args:
5
- - arr-pm
data/Guardfile DELETED
@@ -1,77 +0,0 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features)
6
-
7
- ## Uncomment to clear the screen before every task
8
- # clearing :on
9
-
10
- ## Guard internally checks for changes in the Guardfile and exits.
11
- ## If you want Guard to automatically start up again, run guard in a
12
- ## shell loop, e.g.:
13
- ##
14
- ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
- ##
16
- ## Note: if you are using the `directories` clause above and you are not
17
- ## watching the project directory ('.'), then you will want to move
18
- ## the Guardfile to a watched dir and symlink it back, e.g.
19
- #
20
- # $ mkdir config
21
- # $ mv Guardfile config/
22
- # $ ln -s config/Guardfile .
23
- #
24
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
-
26
- # Note: The cmd option is now required due to the increasing number of ways
27
- # rspec may be run, below are examples of the most common uses.
28
- # * bundler: 'bundle exec rspec'
29
- # * bundler binstubs: 'bin/rspec'
30
- # * spring: 'bin/rspec' (This will use spring if running and you have
31
- # installed the spring binstubs per the docs)
32
- # * zeus: 'zeus rspec' (requires the server to be started separately)
33
- # * 'just' rspec: 'rspec'
34
-
35
- guard :rspec, cmd: "bundle exec rspec" do
36
- require "guard/rspec/dsl"
37
- dsl = Guard::RSpec::Dsl.new(self)
38
-
39
- # Feel free to open issues for suggestions and improvements
40
-
41
- # RSpec files
42
- rspec = dsl.rspec
43
- watch(rspec.spec_helper) { rspec.spec_dir }
44
- watch(rspec.spec_support) { rspec.spec_dir }
45
- watch(rspec.spec_files)
46
-
47
- # Ruby files
48
- ruby = dsl.ruby
49
- dsl.watch_spec_files_for(ruby.lib_files)
50
-
51
- # Rails files
52
- rails = dsl.rails(view_extensions: %w(erb haml slim))
53
- dsl.watch_spec_files_for(rails.app_files)
54
- dsl.watch_spec_files_for(rails.views)
55
-
56
- watch(rails.controllers) do |m|
57
- [
58
- rspec.spec.("routing/#{m[1]}_routing"),
59
- rspec.spec.("controllers/#{m[1]}_controller"),
60
- rspec.spec.("acceptance/#{m[1]}")
61
- ]
62
- end
63
-
64
- # Rails config changes
65
- watch(rails.spec_helper) { rspec.spec_dir }
66
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
67
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
68
-
69
- # Capybara features specs
70
- watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
71
-
72
- # Turnip features and steps
73
- watch(%r{^spec/acceptance/(.+)\.feature$})
74
- watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
75
- Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
76
- end
77
- end