1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
require 'test_helper'
class ZipFileExtractTest < MiniTest::Test
include CommonZipFileFixture
EXTRACTED_FILENAME = 'test/data/generated/extEntry'
ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entry_names.reverse
def setup
super
::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)
assert(File.exist?(EXTRACTED_FILENAME))
AssertEntry.assert_contents(EXTRACTED_FILENAME,
zf.get_input_stream(ENTRY_TO_EXTRACT) { |is| is.read })
::File.unlink(EXTRACTED_FILENAME)
entry = zf.get_entry(ENTRY_TO_EXTRACT)
entry.extract(EXTRACTED_FILENAME)
assert(File.exist?(EXTRACTED_FILENAME))
AssertEntry.assert_contents(EXTRACTED_FILENAME,
entry.get_input_stream { |is| is.read })
end
end
def test_extract_exists
writtenText = 'written text'
::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(writtenText) }
assert_raises(::Zip::DestinationFileExistsError) do
::Zip::File.open(TEST_ZIP.zip_name) do |zf|
zf.extract(zf.entries.first, EXTRACTED_FILENAME)
end
end
File.open(EXTRACTED_FILENAME, 'r') do |f|
assert_equal(writtenText, f.read)
end
end
def test_extract_exists_overwrite
writtenText = 'written text'
::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(writtenText) }
gotCalledCorrectly = false
::Zip::File.open(TEST_ZIP.zip_name) do |zf|
zf.extract(zf.entries.first, EXTRACTED_FILENAME) do |entry, extractLoc|
gotCalledCorrectly = zf.entries.first == entry &&
extractLoc == EXTRACTED_FILENAME
true
end
end
assert(gotCalledCorrectly)
::File.open(EXTRACTED_FILENAME, 'r') do |f|
assert(writtenText != f.read)
end
end
def test_extract_non_entry
zf = ::Zip::File.new(TEST_ZIP.zip_name)
assert_raises(Errno::ENOENT) { zf.extract('nonExistingEntry', 'nonExistingEntry') }
ensure
zf.close if zf
end
def test_extract_non_entry_2
outFile = 'outfile'
assert_raises(Errno::ENOENT) do
zf = ::Zip::File.new(TEST_ZIP.zip_name)
nonEntry = 'hotdog-diddelidoo'
assert(!zf.entries.include?(nonEntry))
zf.extract(nonEntry, outFile)
zf.close
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('V3')
fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('V3')
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
|