Upgrade to 2.0.0 version
This commit is contained in:
parent
5961434aca
commit
c0bde8f1cb
@ -1,242 +0,0 @@
|
||||
From 4167f0ce67e42b082605bca75c7bdfd01eb23804 Mon Sep 17 00:00:00 2001
|
||||
From: John Lees-Miller <jdleesmiller@gmail.com>
|
||||
Date: Thu, 12 Sep 2019 22:01:38 +0100
|
||||
Subject: [PATCH 1/2] Validate entry sizes when extracting
|
||||
|
||||
---
|
||||
README.md | 67 +++++++++++++++++++++++++++++++--------
|
||||
lib/zip.rb | 4 ++-
|
||||
lib/zip/entry.rb | 7 ++++
|
||||
lib/zip/errors.rb | 1 +
|
||||
test/file_extract_test.rb | 62 ++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 127 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/README.md b/README.md
|
||||
index 321b811..a193c24 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -129,12 +129,15 @@ After this entries in zip archive will be saved in ordered state.
|
||||
### Reading a Zip file
|
||||
|
||||
```ruby
|
||||
+MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this)
|
||||
Zip::File.open('foo.zip') do |zip_file|
|
||||
# Handle entries one by one
|
||||
zip_file.each do |entry|
|
||||
- # Extract to file/directory/symlink
|
||||
puts "Extracting #{entry.name}"
|
||||
- entry.extract(dest_file)
|
||||
+ raise 'File too large when extracted' if entry.size > MAX_SIZE
|
||||
+
|
||||
+ # Extract to file or directory based on name in the archive
|
||||
+ entry.extract
|
||||
|
||||
# Read into memory
|
||||
content = entry.get_input_stream.read
|
||||
@@ -142,6 +145,7 @@ Zip::File.open('foo.zip') do |zip_file|
|
||||
|
||||
# Find specific entry
|
||||
entry = zip_file.glob('*.csv').first
|
||||
+ raise 'File too large when extracted' if entry.size > MAX_SIZE
|
||||
puts entry.get_input_stream.read
|
||||
end
|
||||
```
|
||||
@@ -212,6 +216,29 @@ In some zip date of files stored in incorrect format. You can hide warning about
|
||||
Zip.warn_invalid_date = false
|
||||
```
|
||||
|
||||
+By default, `rubyzip`'s `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:
|
||||
+
|
||||
+```ruby
|
||||
+MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
|
||||
+MAX_FILES = 100
|
||||
+Zip::File.open('foo.zip') do |zip_file|
|
||||
+ num_files = 0
|
||||
+ zip_file.each do |entry|
|
||||
+ num_files += 1 if entry.file?
|
||||
+ raise 'Too many extracted files' if num_files > MAX_FILES
|
||||
+ raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
|
||||
+ entry.extract
|
||||
+ end
|
||||
+end
|
||||
+```
|
||||
+
|
||||
+If you need to extract zip files that report incorrect uncompressed sizes and you really trust them not too be too large, you can disable this setting with
|
||||
+```ruby
|
||||
+Zip.validate_entry_sizes = false
|
||||
+```
|
||||
+
|
||||
+Note that if you use the lower level `Zip::InputStream` interface, `rubyzip` does *not* check the entry `size`s. In this case, the caller is responsible for making sure it does not read more data than expected from the input stream.
|
||||
+
|
||||
You can set the default compression level like so:
|
||||
|
||||
```ruby
|
||||
diff --git a/lib/zip.rb b/lib/zip.rb
|
||||
index e6ebad3..25d3764 100644
|
||||
--- a/lib/zip.rb
|
||||
+++ b/lib/zip.rb
|
||||
@@ -37,7 +37,7 @@ end
|
||||
|
||||
module Zip
|
||||
extend self
|
||||
- attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date
|
||||
+ attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date, :validate_entry_sizes
|
||||
|
||||
def reset!
|
||||
@_ran_once = false
|
||||
@@ -48,6 +48,7 @@ module Zip
|
||||
@default_compression = ::Zlib::DEFAULT_COMPRESSION
|
||||
@write_zip64_support = false
|
||||
@warn_invalid_date = true
|
||||
+ @validate_entry_sizes = true
|
||||
end
|
||||
|
||||
def setup
|
||||
diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb
|
||||
index 80160b5..bd3e4f3 100644
|
||||
--- a/lib/zip/entry.rb
|
||||
+++ b/lib/zip/entry.rb
|
||||
@@ -583,9 +583,16 @@ def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exi
|
||||
get_input_stream do |is|
|
||||
set_extra_attributes_on_path(dest_path)
|
||||
|
||||
+ bytes_written = 0
|
||||
buf = ''
|
||||
while buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)
|
||||
os << buf
|
||||
+
|
||||
+ next unless ::Zip.validate_entry_sizes
|
||||
+ bytes_written += buf.bytesize
|
||||
+ if bytes_written > size
|
||||
+ raise ::Zip::EntrySizeError, "Entry #{name} should be #{size}B but is larger when inflated"
|
||||
+ end
|
||||
end
|
||||
end
|
||||
end
|
||||
diff --git a/lib/zip/errors.rb b/lib/zip/errors.rb
|
||||
index b2bcccd..364c6ee 100644
|
||||
--- a/lib/zip/errors.rb
|
||||
+++ b/lib/zip/errors.rb
|
||||
@@ -4,6 +4,7 @@ class EntryExistsError < Error; end
|
||||
class DestinationFileExistsError < Error; end
|
||||
class CompressionMethodError < Error; end
|
||||
class EntryNameError < Error; end
|
||||
+ class EntrySizeError < Error; end
|
||||
class InternalError < Error; end
|
||||
|
||||
# Backwards compatibility with v1 (delete in v2)
|
||||
diff --git a/test/file_extract_test.rb b/test/file_extract_test.rb
|
||||
index 57833fc..6103aea 100644
|
||||
--- a/test/file_extract_test.rb
|
||||
+++ b/test/file_extract_test.rb
|
||||
@@ -10,6 +10,10 @@ def setup
|
||||
::File.delete(EXTRACTED_FILENAME) if ::File.exist?(EXTRACTED_FILENAME)
|
||||
end
|
||||
|
||||
+ def teardown
|
||||
+ ::Zip.reset!
|
||||
+ end
|
||||
+
|
||||
def test_extract
|
||||
::Zip::File.open(TEST_ZIP.zip_name) {
|
||||
|zf|
|
||||
@@ -87,4 +91,61 @@ def test_extractNonEntry2
|
||||
assert(!File.exist?(outFile))
|
||||
end
|
||||
|
||||
+ def test_extract_incorrect_size
|
||||
+ # The uncompressed size fields in the zip file cannot be trusted. This makes
|
||||
+ # it harder for callers to validate the sizes of the files they are
|
||||
+ # extracting, which can lead to denial of service. See also
|
||||
+ # https://en.wikipedia.org/wiki/Zip_bomb
|
||||
+ Dir.mktmpdir do |tmp|
|
||||
+ real_zip = File.join(tmp, 'real.zip')
|
||||
+ fake_zip = File.join(tmp, 'fake.zip')
|
||||
+ file_name = 'a'
|
||||
+ true_size = 500_000
|
||||
+ fake_size = 1
|
||||
+
|
||||
+ ::Zip::File.open(real_zip, ::Zip::File::CREATE) do |zf|
|
||||
+ zf.get_output_stream(file_name) do |os|
|
||||
+ os.write 'a' * true_size
|
||||
+ end
|
||||
+ end
|
||||
+
|
||||
+ compressed_size = nil
|
||||
+ ::Zip::File.open(real_zip) do |zf|
|
||||
+ a_entry = zf.find_entry(file_name)
|
||||
+ compressed_size = a_entry.compressed_size
|
||||
+ assert_equal true_size, a_entry.size
|
||||
+ end
|
||||
+
|
||||
+ true_size_bytes = [compressed_size, true_size, file_name.size].pack('LLS')
|
||||
+ fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('LLS')
|
||||
+
|
||||
+ data = File.binread(real_zip)
|
||||
+ assert data.include?(true_size_bytes)
|
||||
+ data.gsub! true_size_bytes, fake_size_bytes
|
||||
+
|
||||
+ File.open(fake_zip, 'wb') do |file|
|
||||
+ file.write data
|
||||
+ end
|
||||
+
|
||||
+ Dir.chdir tmp do
|
||||
+ ::Zip::File.open(fake_zip) do |zf|
|
||||
+ a_entry = zf.find_entry(file_name)
|
||||
+ assert_equal fake_size, a_entry.size
|
||||
+
|
||||
+ ::Zip.validate_entry_sizes = false
|
||||
+ a_entry.extract
|
||||
+ assert_equal true_size, File.size(file_name)
|
||||
+ FileUtils.rm file_name
|
||||
+
|
||||
+ ::Zip.validate_entry_sizes = true
|
||||
+ error = assert_raises ::Zip::EntrySizeError do
|
||||
+ a_entry.extract
|
||||
+ end
|
||||
+ assert_equal \
|
||||
+ 'Entry a should be 1B but is larger when inflated',
|
||||
+ error.message
|
||||
+ end
|
||||
+ end
|
||||
+ end
|
||||
+ end
|
||||
end
|
||||
|
||||
From 97cb6aefe6d12bd2429d7a2e119ccb26f259d71d Mon Sep 17 00:00:00 2001
|
||||
From: John Lees-Miller <jdleesmiller@gmail.com>
|
||||
Date: Wed, 18 Sep 2019 18:34:23 +0100
|
||||
Subject: [PATCH 2/2] Warn when an entry size is invalid
|
||||
|
||||
---
|
||||
lib/zip/entry.rb | 13 +++++++++----
|
||||
1 file changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb
|
||||
index bd3e4f3..677e49e 100644
|
||||
--- a/lib/zip/entry.rb
|
||||
+++ b/lib/zip/entry.rb
|
||||
@@ -604,14 +604,19 @@ def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exi
|
||||
set_extra_attributes_on_path(dest_path)
|
||||
|
||||
bytes_written = 0
|
||||
+ warned = false
|
||||
buf = ''
|
||||
while buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)
|
||||
os << buf
|
||||
-
|
||||
- next unless ::Zip.validate_entry_sizes
|
||||
bytes_written += buf.bytesize
|
||||
- if bytes_written > size
|
||||
- raise ::Zip::EntrySizeError, "Entry #{name} should be #{size}B but is larger when inflated"
|
||||
+ if bytes_written > size && !warned
|
||||
+ message = "Entry #{name} should be #{size}B but is larger when inflated"
|
||||
+ if ::Zip.validate_entry_sizes
|
||||
+ raise ::Zip::EntrySizeError, message
|
||||
+ else
|
||||
+ puts "WARNING: #{message}"
|
||||
+ warned = true
|
||||
+ end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,7 +1,7 @@
|
||||
%global gem_name rubyzip
|
||||
Name: rubygem-%{gem_name}
|
||||
Version: 1.1.7
|
||||
Release: 2
|
||||
Version: 2.0.0
|
||||
Release: 1
|
||||
Summary: A ruby module for reading and writing zip files
|
||||
License: Ruby or BSD
|
||||
URL: http://github.com/rubyzip/rubyzip
|
||||
@ -9,7 +9,6 @@ Source0: https://rubygems.org/gems/%{gem_name}-%{version}.gem
|
||||
# CVE-2019-16892 denial of service via crafted ZIP file.
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1771298
|
||||
# https://github.com/rubyzip/rubyzip/pull/403
|
||||
Patch0: rubygem-rubyzip-1.3.0-CVE-2019-16892-Validate-entry-sizes-when-extracting.patch
|
||||
BuildRequires: ruby(release) rubygems-devel ruby rubygem(minitest)
|
||||
BuildArch: noarch
|
||||
%description
|
||||
@ -26,7 +25,6 @@ Documentation for %{name}.
|
||||
%setup -q -c -T
|
||||
%gem_install -n %{SOURCE0}
|
||||
pushd .%{gem_instdir}
|
||||
%patch0 -p1
|
||||
rm -rf test/encryption_test.rb
|
||||
popd
|
||||
|
||||
@ -42,7 +40,6 @@ rm -f %{buildroot}%{gem_instdir}/samples/write_simple.rb
|
||||
|
||||
%check
|
||||
pushd .%{gem_instdir}
|
||||
sed -i '/simplecov/ s/^/#/' test/test_helper.rb
|
||||
ruby -Ilib:test -e 'Dir.glob "./test/**/*_test.rb", &method(:require)'
|
||||
popd
|
||||
|
||||
@ -58,9 +55,12 @@ popd
|
||||
%{gem_instdir}/Rakefile
|
||||
%doc %{gem_instdir}/TODO
|
||||
%{gem_instdir}/samples
|
||||
%{gem_instdir}/test
|
||||
|
||||
%changelog
|
||||
* Wed Dec 01 2021 houyingchao <houyingchao@huawei.com> - 2.0.0-1
|
||||
- Upgrade to 2.0.0 version
|
||||
- Fix the compilation failed of rubygem-capybara
|
||||
|
||||
* Sat Sep 5 2020 yanan li <liyanan032@huawei.com> - 1.1.7-2
|
||||
- Fix build fail
|
||||
|
||||
|
||||
Binary file not shown.
BIN
rubyzip-2.0.0.gem
Normal file
BIN
rubyzip-2.0.0.gem
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user