minitar 1.0.2 → 1.1.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +285 -0
  3. data/CONTRIBUTING.md +273 -0
  4. data/CONTRIBUTORS.md +27 -0
  5. data/LICENCE.md +39 -0
  6. data/Manifest.txt +29 -6
  7. data/README.md +70 -0
  8. data/Rakefile +74 -19
  9. data/SECURITY.md +64 -0
  10. data/docs/ruby.txt +3 -3
  11. data/lib/minitar/input.rb +69 -56
  12. data/lib/minitar/output.rb +34 -22
  13. data/lib/minitar/pax_header.rb +111 -0
  14. data/lib/minitar/posix_header.rb +96 -57
  15. data/lib/minitar/reader.rb +65 -70
  16. data/lib/minitar/version.rb +5 -0
  17. data/lib/minitar/writer.rb +50 -88
  18. data/lib/minitar.rb +60 -64
  19. data/licenses/bsdl.txt +20 -0
  20. data/licenses/dco.txt +34 -0
  21. data/licenses/ruby.txt +52 -0
  22. data/test/fixtures/issue_46.tar.gz +0 -0
  23. data/test/fixtures/issue_62.tar.gz +0 -0
  24. data/test/fixtures/tar_input.tgz +0 -0
  25. data/test/fixtures/test_input_non_strict_octal.tgz +0 -0
  26. data/test/fixtures/test_input_relative.tgz +0 -0
  27. data/test/fixtures/test_input_space_octal.tgz +0 -0
  28. data/test/fixtures/test_minitar.tar.gz +0 -0
  29. data/test/minitest_helper.rb +12 -1
  30. data/test/support/minitar_test_helpers/fixtures.rb +38 -0
  31. data/test/support/minitar_test_helpers/header.rb +130 -0
  32. data/test/support/minitar_test_helpers/tarball.rb +324 -0
  33. data/test/support/minitar_test_helpers.rb +36 -0
  34. data/test/test_filename_boundary_conditions.rb +74 -0
  35. data/test/test_gnu_tar_compatibility.rb +92 -0
  36. data/test/test_integration_pack_unpack_cycle.rb +38 -0
  37. data/test/test_issue_46.rb +5 -23
  38. data/test/test_issue_62.rb +50 -0
  39. data/test/test_minitar.rb +168 -39
  40. data/test/test_pax_header.rb +104 -0
  41. data/test/test_pax_support.rb +66 -0
  42. data/test/test_tar_header.rb +289 -75
  43. data/test/test_tar_input.rb +14 -61
  44. data/test/test_tar_output.rb +7 -9
  45. data/test/test_tar_reader.rb +17 -18
  46. data/test/test_tar_writer.rb +105 -126
  47. metadata +95 -89
  48. data/Contributing.md +0 -94
  49. data/History.md +0 -236
  50. data/Licence.md +0 -15
  51. data/README.rdoc +0 -92
  52. data/test/support/tar_test_helpers.rb +0 -134
  53. /data/{Code-of-Conduct.md → CODE_OF_CONDUCT.md} +0 -0
data/Manifest.txt CHANGED
@@ -1,22 +1,45 @@
1
- Code-of-Conduct.md
2
- Contributing.md
3
- History.md
4
- Licence.md
1
+ CHANGELOG.md
2
+ CODE_OF_CONDUCT.md
3
+ CONTRIBUTING.md
4
+ CONTRIBUTORS.md
5
+ LICENCE.md
5
6
  Manifest.txt
6
- README.rdoc
7
+ README.md
7
8
  Rakefile
9
+ SECURITY.md
8
10
  docs/bsdl.txt
9
11
  docs/ruby.txt
10
12
  lib/minitar.rb
11
13
  lib/minitar/input.rb
12
14
  lib/minitar/output.rb
15
+ lib/minitar/pax_header.rb
13
16
  lib/minitar/posix_header.rb
14
17
  lib/minitar/reader.rb
18
+ lib/minitar/version.rb
15
19
  lib/minitar/writer.rb
20
+ licenses/bsdl.txt
21
+ licenses/dco.txt
22
+ licenses/ruby.txt
23
+ test/fixtures/issue_46.tar.gz
24
+ test/fixtures/issue_62.tar.gz
25
+ test/fixtures/tar_input.tgz
26
+ test/fixtures/test_input_non_strict_octal.tgz
27
+ test/fixtures/test_input_relative.tgz
28
+ test/fixtures/test_input_space_octal.tgz
29
+ test/fixtures/test_minitar.tar.gz
16
30
  test/minitest_helper.rb
17
- test/support/tar_test_helpers.rb
31
+ test/support/minitar_test_helpers.rb
32
+ test/support/minitar_test_helpers/fixtures.rb
33
+ test/support/minitar_test_helpers/header.rb
34
+ test/support/minitar_test_helpers/tarball.rb
35
+ test/test_filename_boundary_conditions.rb
36
+ test/test_gnu_tar_compatibility.rb
37
+ test/test_integration_pack_unpack_cycle.rb
18
38
  test/test_issue_46.rb
39
+ test/test_issue_62.rb
19
40
  test/test_minitar.rb
41
+ test/test_pax_header.rb
42
+ test/test_pax_support.rb
20
43
  test/test_tar_header.rb
21
44
  test/test_tar_input.rb
22
45
  test/test_tar_output.rb
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # minitar
2
+
3
+ - code :: <https://github.com/halostatue/minitar>
4
+ - issues :: <https://github.com/halostatue/minitar/issues>
5
+ - changelog :: <https://github.com/halostatue/minitar/blob/main/CHANGELOG.md>
6
+
7
+ ## Description
8
+
9
+ The minitar library is a pure-Ruby library that operates on POSIX tar(1) archive
10
+ files.
11
+
12
+ minitar (previously called Archive::Tar::Minitar) is based heavily on code
13
+ originally written by Mauricio Julio Fernández Pradier for the rpa-base project.
14
+
15
+ ## Synopsis
16
+
17
+ Using minitar is easy. The simplest case is:
18
+
19
+ ```ruby
20
+ require 'minitar'
21
+
22
+ # Packs everything that matches Find.find('tests').
23
+ # test.tar will automatically be closed by Minitar.pack.
24
+ Minitar.pack('tests', File.open('test.tar', 'wb'))
25
+
26
+ # Unpacks 'test.tar' to 'x', creating 'x' if necessary.
27
+ Minitar.unpack('test.tar', 'x')
28
+ ```
29
+
30
+ A gzipped tar can be written with:
31
+
32
+ ```ruby
33
+ require 'zlib'
34
+ # test.tgz will be closed automatically.
35
+ Minitar.pack('tests', Zlib::GzipWriter.new(File.open('test.tgz', 'wb'))
36
+
37
+ # test.tgz will be closed automatically.
38
+ Minitar.unpack(Zlib::GzipReader.new(File.open('test.tgz', 'rb')), 'x')
39
+ ```
40
+
41
+ As the case above shows, one need not write to a file. However, it will
42
+ sometimes require that one dive a little deeper into the API, as in the case of
43
+ StringIO objects. Note that I'm not providing a block with Minitar::Output, as
44
+ Minitar::Output#close automatically closes both the Output object and the
45
+ wrapped data stream object.
46
+
47
+ ```ruby
48
+ begin
49
+ sgz = Zlib::GzipWriter.new(StringIO.new(String.new))
50
+ tar = Output.new(sgz)
51
+ Find.find('tests') do |entry|
52
+ Minitar.pack_file(entry, tar)
53
+ end
54
+ ensure
55
+ # Closes both tar and sgz.
56
+ tar.close
57
+ end
58
+ ```
59
+
60
+ ## Minitar and Security
61
+
62
+ See [SECURITY](./SECURITY.md)
63
+
64
+ ## minitar Semantic Versioning
65
+
66
+ The minitar library uses a [Semantic Versioning][semver] scheme with one change:
67
+
68
+ - When PATCH is zero (`0`), it will be omitted from version references.
69
+
70
+ [semver]: http://semver.org/
data/Rakefile CHANGED
@@ -1,42 +1,97 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "rubygems"
4
4
  require "hoe"
5
5
  require "rake/clean"
6
+ require "rdoc/task"
7
+ require "minitest"
8
+ require "minitest/test_task"
6
9
 
7
- $LOAD_PATH.unshift("support")
8
-
9
- Hoe.plugin :doofus
10
- Hoe.plugin :gemspec2
11
- Hoe.plugin :git2
12
- Hoe.plugin :minitest
10
+ Hoe.plugin :halostatue
13
11
  Hoe.plugin :rubygems
14
- Hoe.plugin :cov
15
12
 
16
- Hoe.spec "minitar" do
13
+ Hoe.plugins.delete :debug
14
+ Hoe.plugins.delete :newb
15
+ Hoe.plugins.delete :publish
16
+ Hoe.plugins.delete :signing
17
+ Hoe.plugins.delete :test
18
+
19
+ hoe = Hoe.spec "minitar" do
17
20
  developer("Austin Ziegler", "halostatue@gmail.com")
18
21
 
19
- self.history_file = "History.md"
20
- self.readme_file = "README.rdoc"
22
+ self.trusted_release = ENV["rubygems_release_gem"] == "true"
21
23
 
22
24
  require_ruby_version ">= 3.1"
23
25
 
24
26
  self.licenses = ["Ruby", "BSD-2-Clause"]
25
27
 
26
- spec_extras[:metadata] = ->(val) { val["rubygems_mfa_required"] = "true" }
28
+ spec_extras[:metadata] = ->(val) {
29
+ val["rubygems_mfa_required"] = "true"
30
+ }
27
31
 
28
- extra_dev_deps << ["base64", "~> 0.2"]
29
32
  extra_dev_deps << ["hoe", "~> 4.0"]
30
- extra_dev_deps << ["hoe-doofus", "~> 1.0"]
31
- extra_dev_deps << ["hoe-gemspec2", "~> 1.1"]
32
- extra_dev_deps << ["hoe-git2", "~> 1.7"]
33
- extra_dev_deps << ["hoe-rubygems", "~> 1.0"]
33
+ extra_dev_deps << ["hoe-halostatue", "~> 2.1", ">= 2.1.1"]
34
+ extra_dev_deps << ["irb", "~> 1.0"]
34
35
  extra_dev_deps << ["minitest", "~> 5.16"]
35
36
  extra_dev_deps << ["minitest-autotest", "~> 1.0"]
36
- extra_dev_deps << ["minitest-focus", "~> 1.0"]
37
+ extra_dev_deps << ["minitest-focus", "~> 1.1"]
37
38
  extra_dev_deps << ["rake", ">= 10.0", "< 14"]
38
- extra_dev_deps << ["rdoc", ">= 0.0"]
39
+ extra_dev_deps << ["rdoc", ">= 0.0", "< 7"]
40
+ extra_dev_deps << ["simplecov", "~> 0.22"]
41
+ extra_dev_deps << ["simplecov-lcov", "~> 0.8"]
39
42
  extra_dev_deps << ["standard", "~> 1.0"]
40
43
  extra_dev_deps << ["standard-minitest", "~> 1.0"]
41
44
  extra_dev_deps << ["standard-thread_safety", "~> 1.0"]
42
45
  end
46
+
47
+ Minitest::TestTask.create :test
48
+ Minitest::TestTask.create :coverage do |t|
49
+ formatters = <<-RUBY.split($/).join(" ")
50
+ SimpleCov::Formatter::MultiFormatter.new([
51
+ SimpleCov::Formatter::HTMLFormatter,
52
+ SimpleCov::Formatter::LcovFormatter,
53
+ SimpleCov::Formatter::SimpleFormatter
54
+ ])
55
+ RUBY
56
+ t.test_prelude = <<-RUBY.split($/).join("; ")
57
+ require "simplecov"
58
+ require "simplecov-lcov"
59
+
60
+ SimpleCov::Formatter::LcovFormatter.config do |config|
61
+ config.report_with_single_file = true
62
+ config.lcov_file_name = "lcov.info"
63
+ end
64
+
65
+ SimpleCov.start "test_frameworks" do
66
+ enable_coverage :branch
67
+ primary_coverage :branch
68
+ formatter #{formatters}
69
+ end
70
+ RUBY
71
+ end
72
+
73
+ task default: :test
74
+
75
+ task :version do
76
+ require "color/version"
77
+ puts Color::VERSION
78
+ end
79
+
80
+ RDoc::Task.new do
81
+ _1.title = "minitar"
82
+ _1.main = "lib/minitar.rb"
83
+ _1.rdoc_dir = "doc"
84
+ _1.rdoc_files = hoe.spec.require_paths - ["Manifest.txt"] + hoe.spec.extra_rdoc_files
85
+ _1.markup = "markdown"
86
+ end
87
+ task docs: :rerdoc
88
+
89
+ task :console do
90
+ arguments = %w[irb]
91
+ arguments.push(*hoe.spec.require_paths.map { |dir| "-I#{dir}" })
92
+ arguments.push("-r#{hoe.spec.name.gsub("-", File::SEPARATOR)}")
93
+ unless system(*arguments)
94
+ error "Command failed: #{show_command}"
95
+ abort
96
+ end
97
+ end
data/SECURITY.md ADDED
@@ -0,0 +1,64 @@
1
+ # Minitar Security Policy
2
+
3
+ Minitar aims to be secure by default for the data _inside_ of a tar file.
4
+
5
+ ## LLM-Generated Security Report Policy
6
+
7
+ Absolutely no security reports will be accepted that have been generated by LLM
8
+ agents.
9
+
10
+ ## Supported Versions
11
+
12
+ Security reports are accepted only for the most recent major release. As of
13
+ December 2024, that is the 1.0 release series. Older releases are no longer
14
+ supported.
15
+
16
+ ## Reporting a Vulnerability
17
+
18
+ By preference, use the [Tidelift security contact][tidelift]. Tidelift will
19
+ coordinate the fix and disclosure.
20
+
21
+ Alternatively, Send an email to [minitar@halostatue.ca][email] with the text
22
+ `Minitar` in the subject. Emails sent to this address should be encrypted using
23
+ [age][age] with the following public key:
24
+
25
+ ```
26
+ age1fc6ngxmn02m62fej5cl30lrvwmxn4k3q2atqu53aatekmnqfwumqj4g93w
27
+ ```
28
+
29
+ ## Exclusions
30
+
31
+ There are several classes of potential security issues that will not be accepted
32
+ for Minitar There are several classes of "security" issues which will not be
33
+ accepted for Minitar, because any issues arising from these are a matter of the
34
+ library being used incorrectly.
35
+
36
+ - [CWE-073](https://cwe.mitre.org/data/definitions/73.html)
37
+ - [CWE-078](https://cwe.mitre.org/data/definitions/78.html)
38
+ - [CWE-088](https://cwe.mitre.org/data/definitions/88.html)
39
+
40
+ Minitar does _not_ perform validation or sanitization of path names provided to
41
+ the convenience classes `Minitar::Output` and `Minitar::Input`, which use
42
+ `Kernel.open` for their underlying implementations when not given an IO-like
43
+ object.
44
+
45
+ Improper use of these convenience classes with arbitrary input filenames may
46
+ leave your your software to the same class of vulnerability as reported for
47
+ Net::FTP ([CVE-2017-17405][CVE-2017-17405]). If the input filename argument
48
+ starts with the pipe character (`|`), the command following the pipe character
49
+ is executed.
50
+
51
+ Additionally, the use of the `open-uri` library (which extends `Kernel.open`
52
+ with transparent implementations of `Net::HTTP`, `Net::HTTPS`, and `Net::FTP`),
53
+ there are other possible vulnerabilities when accepting arbitrary input, as
54
+ [detailed][openuri] by Egor Homakov.
55
+
56
+ These security vulnerabilities may be avoided, even with the `Minitar::Output`
57
+ and `Minitar::Input` convenience classes, by providing IO-like objects instead
58
+ of pathname-like objects as the source or destination of these classes.
59
+
60
+ [tidelift]: https://tidelift.com/security
61
+ [email]: mailto:minitar@halostatue.ca
62
+ [age]: https://github.com/FiloSottile/age
63
+ [CVE-2017-17405]: https://nvd.nist.gov/vuln/detail/CVE-2017-17405
64
+ [openuri]: https://sakurity.com/blog/2015/02/28/openuri.html
data/docs/ruby.txt CHANGED
@@ -44,13 +44,13 @@ You can redistribute it and/or modify it under either the terms of the
44
44
  For the list of those files and their copying conditions, see the
45
45
  file LEGAL.
46
46
 
47
- 5. The scripts and library files supplied as input to or produced as
47
+ 5. The scripts and library files supplied as input to or produced as
48
48
  output from the software do not automatically fall under the
49
- copyright of the software, but belong to whomever generated them,
49
+ copyright of the software, but belong to whomever generated them,
50
50
  and may be sold commercially, and may be aggregated with this
51
51
  software.
52
52
 
53
53
  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
54
  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
55
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
- PURPOSE.
56
+ PURPOSE.
data/lib/minitar/input.rb CHANGED
@@ -1,26 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "minitar/reader"
2
4
 
3
5
  class Minitar
4
- # Wraps a Minitar::Reader with convenience methods and wrapped
5
- # stream management; Input only works with data streams that can be rewound.
6
+ # Wraps a Minitar::Reader with convenience methods and wrapped stream management; Input
7
+ # only works with data streams that can be rewound.
8
+ #
9
+ # === Security Notice
10
+ #
11
+ # Constructing a Minitar::Input will use Kernel.open if the provided input is not
12
+ # a readable stream object. Using an untrusted value for input may allow a malicious
13
+ # user to execute arbitrary system commands. It is the caller's responsibility to ensure
14
+ # that the input value is safe.
15
+ #
16
+ # * {CWE-073}[https://cwe.mitre.org/data/definitions/73.html]
17
+ # * {CWE-078}[https://cwe.mitre.org/data/definitions/78.html]
18
+ # * {CWE-088}[https://cwe.mitre.org/data/definitions/88.html]
19
+ #
20
+ # This notice applies to Minitar::Input.open, Minitar::Input.each_entry, and
21
+ # Minitar::Input.new.
6
22
  class Input
7
23
  include Enumerable
8
24
 
9
- # With no associated block, +Input.open+ is a synonym for +Input.new+. If
10
- # the optional code block is given, it will be given the new Input as an
11
- # argument and the Input object will automatically be closed when the block
12
- # terminates (this also closes the wrapped stream object). In this
13
- # instance, +Input.open+ returns the value of the block.
25
+ # With no associated block, +Input.open+ is a synonym for +Input.new+.
26
+ #
27
+ # If a block is given, the new Input will be yielded to the block as an argument and
28
+ # the Input object will automatically be closed when the block terminates (this also
29
+ # closes the wrapped stream object). The return value will be the value of the block.
14
30
  #
15
- # call-seq:
31
+ # :call-seq:
16
32
  # Minitar::Input.open(io) -> input
17
33
  # Minitar::Input.open(io) { |input| block } -> obj
18
34
  def self.open(input)
19
35
  stream = new(input)
20
36
 
21
37
  if block_given?
22
- # This exception context must remain, otherwise the stream closes on
23
- # open even if a block is not given.
38
+ # This exception context must remain, otherwise the stream closes on open even if
39
+ # a block is not given.
24
40
  begin
25
41
  yield stream
26
42
  ensure
@@ -31,8 +47,7 @@ class Minitar
31
47
  end
32
48
  end
33
49
 
34
- # Iterates over each entry in the provided input. This wraps the common
35
- # pattern of:
50
+ # Iterates over each entry in the provided input. This wraps the common pattern of:
36
51
  #
37
52
  # Minitar::Input.open(io) do |i|
38
53
  # inp.each do |entry|
@@ -40,10 +55,9 @@ class Minitar
40
55
  # end
41
56
  # end
42
57
  #
43
- # If a block is not provided, an enumerator will be created with the same
44
- # behaviour.
58
+ # If a block is not provided, an enumerator will be created with the same behaviour.
45
59
  #
46
- # call-seq:
60
+ # :call-seq:
47
61
  # Minitar::Input.each_entry(io) -> enumerator
48
62
  # Minitar::Input.each_entry(io) { |entry| block } -> obj
49
63
  def self.each_entry(input)
@@ -56,33 +70,31 @@ class Minitar
56
70
  end
57
71
  end
58
72
 
59
- # Creates a new Input object. If +input+ is a stream object that responds
60
- # to #read, then it will simply be wrapped. Otherwise, one will be created
61
- # and opened using Kernel#open. When Input#close is called, the stream
62
- # object wrapped will be closed.
73
+ # Creates a new Input object. If +input+ is a stream object that responds to #read,
74
+ # then it will simply be wrapped. Otherwise, one will be created and opened using
75
+ # Kernel#open. When Input#close is called, the stream object wrapped will be closed.
63
76
  #
64
- # An exception will be raised if the stream that is wrapped does not
65
- # support rewinding.
77
+ # An exception will be raised if the stream that is wrapped does not support
78
+ # rewinding.
66
79
  #
67
- # call-seq:
80
+ # :call-seq:
68
81
  # Minitar::Input.new(io) -> input
69
82
  # Minitar::Input.new(path) -> input
70
83
  def initialize(input)
71
- @io = if input.respond_to?(:read)
72
- input
73
- else
74
- ::Kernel.open(input, "rb")
75
- end
84
+ @io =
85
+ if input.respond_to?(:read)
86
+ input
87
+ else
88
+ ::Kernel.open(input, "rb")
89
+ end
76
90
 
77
- unless Minitar.seekable?(@io, :rewind)
78
- raise Minitar::NonSeekableStream
79
- end
91
+ raise Minitar::NonSeekableStream unless Minitar.seekable?(@io, :rewind)
80
92
 
81
93
  @tar = Reader.new(@io)
82
94
  end
83
95
 
84
- # When provided a block, iterates through each entry in the archive. When
85
- # finished, rewinds to the beginning of the stream.
96
+ # When provided a block, iterates through each entry in the archive. When finished,
97
+ # rewinds to the beginning of the stream.
86
98
  #
87
99
  # If not provided a block, creates an enumerator with the same semantics.
88
100
  def each_entry
@@ -96,39 +108,38 @@ class Minitar
96
108
  end
97
109
  alias_method :each, :each_entry
98
110
 
99
- # Extracts the current +entry+ to +destdir+. If a block is provided, it
100
- # yields an +action+ Symbol, the full name of the file being extracted
101
- # (+name+), and a Hash of statistical information (+stats+).
111
+ # Extracts the current +entry+ to +destdir+. If a block is provided, it yields an
112
+ # +action+ Symbol, the full name of the file being extracted (+name+), and a Hash of
113
+ # statistical information (+stats+).
102
114
  #
103
115
  # The +action+ will be one of:
116
+ #
104
117
  # <tt>:dir</tt>:: The +entry+ is a directory.
105
- # <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
106
- # file is just beginning.
107
- # <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
108
- # of the +entry+.
118
+ # <tt>:file_start</tt>:: The +entry+ is a file; the extract of the file is just
119
+ # beginning.
120
+ # <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract of the
121
+ # +entry+.
109
122
  # <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
110
123
  #
111
124
  # The +stats+ hash contains the following keys:
112
- # <tt>:current</tt>:: The current total number of bytes read in the
113
- # +entry+.
114
- # <tt>:currinc</tt>:: The current number of bytes read in this read
115
- # cycle.
116
- # <tt>:entry</tt>:: The entry being extracted; this is a
117
- # Reader::EntryStream, with all methods thereof.
118
- def extract_entry(destdir, entry, options = {}, &) # :yields action, name, stats:
125
+ #
126
+ # <tt>:current</tt>:: The current total number of bytes read in the +entry+.
127
+ # <tt>:currinc</tt>:: The current number of bytes read in this read cycle.
128
+ # <tt>:entry</tt>:: The entry being extracted; this is a Reader::EntryStream, with
129
+ # all methods thereof.
130
+ def extract_entry(destdir, entry, options = {}, &) # :yields: action, name, stats
119
131
  stats = {
120
132
  current: 0,
121
133
  currinc: 0,
122
134
  entry: entry
123
135
  }
124
136
 
125
- # extract_entry is not vulnerable to prefix '/' vulnerabilities, but it
126
- # is vulnerable to relative path directories. This code will break this
127
- # vulnerability. For this version, we are breaking relative paths HARD by
128
- # throwing an exception.
137
+ # extract_entry is not vulnerable to prefix '/' vulnerabilities, but it is
138
+ # vulnerable to relative path directories. This code will break this vulnerability.
139
+ # For this version, we are breaking relative paths HARD by throwing an exception.
129
140
  #
130
- # Future versions may permit relative paths as long as the file does not
131
- # leave +destdir+.
141
+ # Future versions may permit relative paths as long as the file does not leave
142
+ # +destdir+.
132
143
  #
133
144
  # However, squeeze consecutive '/' characters together.
134
145
  full_name = entry.full_name.squeeze("/")
@@ -145,9 +156,7 @@ class Minitar
145
156
  end
146
157
 
147
158
  # Returns false if the wrapped data stream is open.
148
- def closed?
149
- @io.closed?
150
- end
159
+ def closed? = @io.closed?
151
160
 
152
161
  # Returns the Reader object for direct access.
153
162
  attr_reader :tar
@@ -203,7 +212,11 @@ class Minitar
203
212
  File.unlink(destfile) if File.symlink?(destfile)
204
213
 
205
214
  # Errno::ENOENT
206
- FileUtils.chmod(0o600, destfile) rescue nil # standard:disable Style/RescueModifier
215
+ begin
216
+ FileUtils.chmod(0o600, destfile)
217
+ rescue
218
+ nil
219
+ end
207
220
 
208
221
  yield :file_start, full_name, stats if block_given?
209
222
 
@@ -1,18 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "minitar/writer"
2
4
 
3
5
  class Minitar
4
- # Wraps a Minitar::Writer with convenience methods and wrapped
5
- # stream management. If the stream provided to Output does not support random
6
- # access, only Writer#add_file_simple and Writer#mkdir are guaranteed to
7
- # work.
6
+ # Wraps a Minitar::Writer with convenience methods and wrapped stream management. If the
7
+ # stream provided to Output does not support random access, only Writer#add_file_simple
8
+ # and Writer#mkdir are guaranteed to work.
9
+ #
10
+ # === Security Notice
11
+ #
12
+ # Constructing a Minitar::Output will use Kernel.open if the provided output is not
13
+ # a readable stream object. Using an untrusted value for output may allow a malicious
14
+ # user to execute arbitrary system commands. It is the caller's responsibility to ensure
15
+ # that the output value is safe.
16
+ #
17
+ # * {CWE-073}[https://cwe.mitre.org/data/definitions/73.html]
18
+ # * {CWE-078}[https://cwe.mitre.org/data/definitions/78.html]
19
+ # * {CWE-088}[https://cwe.mitre.org/data/definitions/88.html]
20
+ #
21
+ # This notice applies to Minitar::Output.open, Minitar::Output.tar, and
22
+ # Minitar::Output.new.
8
23
  class Output
9
- # With no associated block, +Output.open+ is a synonym for +Output.new+. If
10
- # the optional code block is given, it will be given the new Output as an
11
- # argument and the Output object will automatically be closed when the
12
- # block terminates (this also closes the wrapped stream object). In this
13
- # instance, +Output.open+ returns the value of the block.
24
+ # With no associated block, +Output.open+ is a synonym for +Output.new+.
25
+ #
26
+ # If a block is given, the new Output will be yielded to the block as an argument and
27
+ # the Output object will automatically be closed when the block terminates (this also
28
+ # closes the wrapped stream object). The return value will be the value of the block.
14
29
  #
15
- # call-seq:
30
+ # :call-seq:
16
31
  # Minitar::Output.open(io) -> output
17
32
  # Minitar::Output.open(io) { |output| block } -> obj
18
33
  def self.open(output)
@@ -28,11 +43,11 @@ class Minitar
28
43
  end
29
44
  end
30
45
 
31
- # Output.tar is a wrapper for Output.open that yields the owned tar object
32
- # instead of the Output object. If a block is not provided, an enumerator
33
- # will be created with the same behaviour.
46
+ # Output.tar is a wrapper for Output.open that yields the owned tar object instead of
47
+ # the Output object. If a block is not provided, an enumerator will be created with
48
+ # the same behaviour.
34
49
  #
35
- # call-seq:
50
+ # :call-seq:
36
51
  # Minitar::Output.tar(io) -> enumerator
37
52
  # Minitar::Output.tar(io) { |tar| block } -> obj
38
53
  def self.tar(output)
@@ -43,12 +58,11 @@ class Minitar
43
58
  end
44
59
  end
45
60
 
46
- # Creates a new Output object. If +output+ is a stream object that responds
47
- # to #write, then it will simply be wrapped. Otherwise, one will be created
48
- # and opened using Kernel#open. When Output#close is called, the stream
49
- # object wrapped will be closed.
61
+ # Creates a new Output object. If +output+ is a stream object that responds to #write,
62
+ # then it will simply be wrapped. Otherwise, one will be created and opened using
63
+ # Kernel#open. When Output#close is called, the stream object wrapped will be closed.
50
64
  #
51
- # call-seq:
65
+ # :call-seq:
52
66
  # Minitar::Output.new(io) -> output
53
67
  # Minitar::Output.new(path) -> output
54
68
  def initialize(output)
@@ -64,9 +78,7 @@ class Minitar
64
78
  attr_reader :tar
65
79
 
66
80
  # Returns false if the wrapped data stream is open.
67
- def closed?
68
- @io.closed?
69
- end
81
+ def closed? = @io.closed?
70
82
 
71
83
  # Closes the Writer object and the wrapped data stream.
72
84
  def close