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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +285 -0
- data/CONTRIBUTING.md +273 -0
- data/CONTRIBUTORS.md +27 -0
- data/LICENCE.md +39 -0
- data/Manifest.txt +29 -6
- data/README.md +70 -0
- data/Rakefile +74 -19
- data/SECURITY.md +64 -0
- data/docs/ruby.txt +3 -3
- data/lib/minitar/input.rb +69 -56
- data/lib/minitar/output.rb +34 -22
- data/lib/minitar/pax_header.rb +111 -0
- data/lib/minitar/posix_header.rb +96 -57
- data/lib/minitar/reader.rb +65 -70
- data/lib/minitar/version.rb +5 -0
- data/lib/minitar/writer.rb +50 -88
- data/lib/minitar.rb +60 -64
- data/licenses/bsdl.txt +20 -0
- data/licenses/dco.txt +34 -0
- data/licenses/ruby.txt +52 -0
- data/test/fixtures/issue_46.tar.gz +0 -0
- data/test/fixtures/issue_62.tar.gz +0 -0
- data/test/fixtures/tar_input.tgz +0 -0
- data/test/fixtures/test_input_non_strict_octal.tgz +0 -0
- data/test/fixtures/test_input_relative.tgz +0 -0
- data/test/fixtures/test_input_space_octal.tgz +0 -0
- data/test/fixtures/test_minitar.tar.gz +0 -0
- data/test/minitest_helper.rb +12 -1
- data/test/support/minitar_test_helpers/fixtures.rb +38 -0
- data/test/support/minitar_test_helpers/header.rb +130 -0
- data/test/support/minitar_test_helpers/tarball.rb +324 -0
- data/test/support/minitar_test_helpers.rb +36 -0
- data/test/test_filename_boundary_conditions.rb +74 -0
- data/test/test_gnu_tar_compatibility.rb +92 -0
- data/test/test_integration_pack_unpack_cycle.rb +38 -0
- data/test/test_issue_46.rb +5 -23
- data/test/test_issue_62.rb +50 -0
- data/test/test_minitar.rb +168 -39
- data/test/test_pax_header.rb +104 -0
- data/test/test_pax_support.rb +66 -0
- data/test/test_tar_header.rb +289 -75
- data/test/test_tar_input.rb +14 -61
- data/test/test_tar_output.rb +7 -9
- data/test/test_tar_reader.rb +17 -18
- data/test/test_tar_writer.rb +105 -126
- metadata +95 -89
- data/Contributing.md +0 -94
- data/History.md +0 -236
- data/Licence.md +0 -15
- data/README.rdoc +0 -92
- data/test/support/tar_test_helpers.rb +0 -134
- /data/{Code-of-Conduct.md → CODE_OF_CONDUCT.md} +0 -0
data/Manifest.txt
CHANGED
@@ -1,22 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
CHANGELOG.md
|
2
|
+
CODE_OF_CONDUCT.md
|
3
|
+
CONTRIBUTING.md
|
4
|
+
CONTRIBUTORS.md
|
5
|
+
LICENCE.md
|
5
6
|
Manifest.txt
|
6
|
-
README.
|
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/
|
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
|
-
#
|
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
|
-
|
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.
|
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.
|
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) {
|
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-
|
31
|
-
extra_dev_deps << ["
|
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.
|
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
|
-
#
|
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+.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
|
-
#
|
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
|
-
#
|
61
|
-
#
|
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
|
-
#
|
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 =
|
72
|
-
input
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
#
|
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
|
-
#
|
101
|
-
#
|
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
|
-
#
|
107
|
-
# <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
|
108
|
-
#
|
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
|
-
#
|
113
|
-
#
|
114
|
-
# <tt>:currinc</tt>:: The current number of bytes read in this read
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
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
|
-
#
|
127
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
|
data/lib/minitar/output.rb
CHANGED
@@ -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
|
6
|
-
#
|
7
|
-
#
|
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+.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
|
-
#
|
33
|
-
#
|
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
|
-
#
|
48
|
-
#
|
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
|