CVE-2019-16892
Description
In Rubyzip before 1.3.0, a crafted ZIP file can bypass application checks on ZIP entry sizes because data about the uncompressed size can be spoofed. This allows attackers to cause a denial of service (disk consumption).
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Rubyzip before 1.3.0, a crafted ZIP file can bypass size checks, allowing denial of service via disk consumption.
Rubyzip before version 1.3.0 contains a vulnerability in its handling of ZIP file entries. The library fails to properly validate the uncompressed size specified in the ZIP central directory, allowing an attacker to spoof this value. This bypasses application checks that rely on the uncompressed size to limit resource usage [1][4].
An attacker can craft a malicious ZIP file with a small declared uncompressed size but containing a large amount of compressed data. When the application using Rubyzip processes the archive, it may allocate disk space based on the spoofed size, but the actual uncompressed data consumes significantly more space. Exploitation requires only the ability to supply a specially crafted ZIP file to an application using the vulnerable Rubyzip version [4].
The primary impact is a denial of service through excessive disk consumption, potentially filling up storage and affecting system availability. No code execution or data corruption is reported [4].
Users should upgrade to Rubyzip 1.3.0 or later, which includes the fix. Red Hat has released errata (RHSA-2019:4201) for affected products [1]. No workarounds are mentioned beyond updating the library.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
rubyzipRubyGems | < 1.3.0 | 1.3.0 |
Affected products
3- Rubyzip/Rubyzipdescription
- ghsa-coords2 versions
< 1.3.0+ 1 more
- (no CPE)range: < 1.3.0
- (no CPE)range: < 2.2.18-1.3
Patches
2e79d9ea2922bMerge pull request #407 from rubyzip/v1-3-0
2 files changed · +2 −2
Changelog.md+1 −1 modified@@ -2,7 +2,7 @@ - -# 1.3.0 (Next) +# 1.3.0 (2019-09-25) Security
lib/zip/version.rb+1 −1 modified@@ -1,3 +1,3 @@ module Zip - VERSION = '1.2.4' + VERSION = '1.3.0' end
d65fe7bd283eMerge pull request #403 from rubyzip/check-size
6 files changed · +149 −14
Changelog.md+11 −0 modified@@ -1,5 +1,16 @@ # X.X.X (Next) +- + +# 1.3.0 (Next) + +Security + +- Add `validate_entry_sizes` option so that callers can trust an entry's reported size when using `extract` [#403](https://github.com/rubyzip/rubyzip/pull/403) + - This option defaults to `false` for backward compatibility in this release, but you are strongly encouraged to set it to `true`. It will default to `true` in rubyzip 2.0. + +New Feature + - Add `add_stored` method to simplify adding entries without compression [#366](https://github.com/rubyzip/rubyzip/pull/366) Tooling / Documentation
lib/zip/entry.rb+12 −0 modified@@ -603,9 +603,21 @@ 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 + warned = false buf = ''.dup while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)) os << buf + bytes_written += buf.bytesize + 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 end
lib/zip/errors.rb+1 −0 modified@@ -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 class GPFBit3Error < Error; end
lib/zip.rb+3 −1 modified@@ -42,7 +42,8 @@ module Zip :write_zip64_support, :warn_invalid_date, :case_insensitive_match, - :force_entry_names_encoding + :force_entry_names_encoding, + :validate_entry_sizes def reset! @_ran_once = false @@ -54,6 +55,7 @@ def reset! @write_zip64_support = false @warn_invalid_date = true @case_insensitive_match = false + @validate_entry_sizes = false end def setup
README.md+60 −13 modified@@ -152,19 +152,23 @@ When modifying a zip archive the file permissions of the archive are preserved. ### 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 end # 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 ``` @@ -219,6 +223,8 @@ File.open(new_path, "wb") {|f| f.write(buffer.string) } ## Configuration +### Existing Files + By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so: ```ruby @@ -233,18 +239,63 @@ Additionally, if you want to configure rubyzip to overwrite existing files while Zip.continue_on_exists_proc = true ``` +### Non-ASCII Names + If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option: ```ruby Zip.unicode_names = true ``` +Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using `find_entry` then you can force assumed encoding like so: + +```ruby +Zip.force_entry_names_encoding = 'UTF-8' +``` + +Allowed encoding names are the same as accepted by `String#force_encoding` + +### Date Validation + Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting: ```ruby Zip.warn_invalid_date = false ``` +### Size Validation + +**This setting defaults to `false` in rubyzip 1.3 for backward compatibility, but it will default to `true` in rubyzip 2.0.** + +If you set +``` +Zip.validate_entry_sizes = true +``` +then `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. + +### Default Compression + You can set the default compression level like so: ```ruby @@ -253,13 +304,17 @@ Zip.default_compression = Zlib::DEFAULT_COMPRESSION It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION` -Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using `find_entry` then you can force assumed encoding like so: +### Zip64 Support + +By default, Zip64 support is disabled for writing. To enable it do this: ```ruby -Zip.force_entry_names_encoding = 'UTF-8' +Zip.write_zip64_support = true ``` -Allowed encoding names are the same as accepted by `String#force_encoding` +_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive. + +### Block Form You can set multiple settings at the same time by using a block: @@ -272,14 +327,6 @@ You can set multiple settings at the same time by using a block: end ``` -By default, Zip64 support is disabled for writing. To enable it do this: - -```ruby -Zip.write_zip64_support = true -``` - -_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive. - ## Developing To run the test you need to do this:
test/file_extract_test.rb+62 −0 modified@@ -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) do |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) @@ -80,4 +84,62 @@ def test_extract_non_entry_2 end 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
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
17- access.redhat.com/errata/RHBA-2019:4047ghsavendor-advisoryWEB
- access.redhat.com/errata/RHSA-2019:4201ghsavendor-advisoryWEB
- github.com/advisories/GHSA-5m2v-hc64-56h6ghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/J45KSFPP6DFVWLC7Z73L7SX735CKZYO6/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/MWWPORMSBHZTMP4PGF4DQD22TTKBQMMC/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/X255K6ZBAQC462PQN2ND5HOTTQEJ2G2X/mitrevendor-advisory
- nvd.nist.gov/vuln/detail/CVE-2019-16892ghsaADVISORY
- github.com/jdleesmiller/ruby-advisory-db/blob/master/gems/rubyzip/CVE-2019-16892.ymlghsaWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/rubyzip/CVE-2019-16892.ymlghsaWEB
- github.com/rubyzip/rubyzip/commit/d65fe7bd283ec94f9d6dc7605f61a6b0dd00f55eghsaWEB
- github.com/rubyzip/rubyzip/pull/403ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/J45KSFPP6DFVWLC7Z73L7SX735CKZYO6ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/MWWPORMSBHZTMP4PGF4DQD22TTKBQMMCghsaWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/X255K6ZBAQC462PQN2ND5HOTTQEJ2G2XghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/J45KSFPP6DFVWLC7Z73L7SX735CKZYO6ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/MWWPORMSBHZTMP4PGF4DQD22TTKBQMMCghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/X255K6ZBAQC462PQN2ND5HOTTQEJ2G2XghsaWEB
News mentions
0No linked articles in our index yet.