arr-pm 0.0.11 → 0.0.12

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