rubyzip 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +70 -23
- data/lib/zip.rb +3 -1
- data/lib/zip/entry.rb +22 -9
- data/lib/zip/errors.rb +1 -0
- data/lib/zip/extra_field.rb +1 -1
- data/lib/zip/file.rb +37 -17
- data/lib/zip/inflater.rb +1 -1
- data/lib/zip/version.rb +1 -1
- data/test/data/path_traversal/tilde.zip +0 -0
- data/test/file_extract_test.rb +62 -0
- data/test/file_test.rb +71 -6
- data/test/path_traversal_test.rb +7 -0
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7861f60d52fbe54891831d9239df3e3e927a33f1f2d00abcb7b2693a8c01097d
|
4
|
+
data.tar.gz: c04844369dbc75f40583f3d8aab34cbf2cfcce293b3c30570fa76208755a3157
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9fa8a9f92c4789f06a1691eee6201918a753f1356eb6f2ef99fe9c777d46d661b7a8f96ce71b7a18406a4e1a4098f81b8a282babe2a7284ae0c8c04d03cf758
|
7
|
+
data.tar.gz: e4650b6f572ef4bcdb33cff4bd0c7ddbd399b04278bd644d11ea29defe057510706d15d0b8ac918df28280d13f4bdfec28526aaad258ff52136ce209f29a35dd
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# rubyzip
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip)
|
3
4
|
[![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip)
|
@@ -19,9 +20,10 @@ gem 'zip-zip' # will load compatibility for old rubyzip API.
|
|
19
20
|
|
20
21
|
## Requirements
|
21
22
|
|
22
|
-
|
23
|
+
- Ruby 1.9.2 or greater
|
23
24
|
|
24
25
|
## Installation
|
26
|
+
|
25
27
|
Rubyzip is available on RubyGems:
|
26
28
|
|
27
29
|
```
|
@@ -59,7 +61,8 @@ end
|
|
59
61
|
```
|
60
62
|
|
61
63
|
### Zipping a directory recursively
|
62
|
-
|
64
|
+
|
65
|
+
Copy from [here](https://github.com/rubyzip/rubyzip/blob/9d891f7353e66052283562d3e252fe380bb4b199/samples/example_recursive.rb)
|
63
66
|
|
64
67
|
```ruby
|
65
68
|
require 'zip'
|
@@ -83,7 +86,7 @@ class ZipFileGenerator
|
|
83
86
|
|
84
87
|
# Zip the input directory.
|
85
88
|
def write
|
86
|
-
entries = Dir.entries(@input_dir) - %w
|
89
|
+
entries = Dir.entries(@input_dir) - %w[. ..]
|
87
90
|
|
88
91
|
::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
|
89
92
|
write_entries entries, '', zipfile
|
@@ -97,7 +100,6 @@ class ZipFileGenerator
|
|
97
100
|
entries.each do |e|
|
98
101
|
zipfile_path = path == '' ? e : File.join(path, e)
|
99
102
|
disk_file_path = File.join(@input_dir, zipfile_path)
|
100
|
-
puts "Deflating #{disk_file_path}"
|
101
103
|
|
102
104
|
if File.directory? disk_file_path
|
103
105
|
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
@@ -109,14 +111,12 @@ class ZipFileGenerator
|
|
109
111
|
|
110
112
|
def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
111
113
|
zipfile.mkdir zipfile_path
|
112
|
-
subdir = Dir.entries(disk_file_path) - %w
|
114
|
+
subdir = Dir.entries(disk_file_path) - %w[. ..]
|
113
115
|
write_entries subdir, zipfile_path, zipfile
|
114
116
|
end
|
115
117
|
|
116
118
|
def put_into_archive(disk_file_path, zipfile, zipfile_path)
|
117
|
-
zipfile.
|
118
|
-
f.write(File.open(disk_file_path, 'rb').read)
|
119
|
-
end
|
119
|
+
zipfile.add(zipfile_path, disk_file_path)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
```
|
@@ -152,12 +152,15 @@ When modifying a zip archive the file permissions of the archive are preserved.
|
|
152
152
|
### Reading a Zip file
|
153
153
|
|
154
154
|
```ruby
|
155
|
+
MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this)
|
155
156
|
Zip::File.open('foo.zip') do |zip_file|
|
156
157
|
# Handle entries one by one
|
157
158
|
zip_file.each do |entry|
|
158
|
-
# Extract to file/directory/symlink
|
159
159
|
puts "Extracting #{entry.name}"
|
160
|
-
entry.
|
160
|
+
raise 'File too large when extracted' if entry.size > MAX_SIZE
|
161
|
+
|
162
|
+
# Extract to file or directory based on name in the archive
|
163
|
+
entry.extract
|
161
164
|
|
162
165
|
# Read into memory
|
163
166
|
content = entry.get_input_stream.read
|
@@ -165,6 +168,7 @@ Zip::File.open('foo.zip') do |zip_file|
|
|
165
168
|
|
166
169
|
# Find specific entry
|
167
170
|
entry = zip_file.glob('*.csv').first
|
171
|
+
raise 'File too large when extracted' if entry.size > MAX_SIZE
|
168
172
|
puts entry.get_input_stream.read
|
169
173
|
end
|
170
174
|
```
|
@@ -177,7 +181,6 @@ But there is one exception when it is not working - General Purpose Flag Bit 3.
|
|
177
181
|
|
178
182
|
> If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data
|
179
183
|
|
180
|
-
|
181
184
|
If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception.
|
182
185
|
|
183
186
|
### Password Protection (Experimental)
|
@@ -220,7 +223,9 @@ File.open(new_path, "wb") {|f| f.write(buffer.string) }
|
|
220
223
|
|
221
224
|
## Configuration
|
222
225
|
|
223
|
-
|
226
|
+
### Existing Files
|
227
|
+
|
228
|
+
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:
|
224
229
|
|
225
230
|
```ruby
|
226
231
|
Zip.on_exists_proc = true
|
@@ -234,32 +239,82 @@ Additionally, if you want to configure rubyzip to overwrite existing files while
|
|
234
239
|
Zip.continue_on_exists_proc = true
|
235
240
|
```
|
236
241
|
|
242
|
+
### Non-ASCII Names
|
243
|
+
|
237
244
|
If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option:
|
238
245
|
|
239
246
|
```ruby
|
240
247
|
Zip.unicode_names = true
|
241
248
|
```
|
242
249
|
|
250
|
+
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:
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
Zip.force_entry_names_encoding = 'UTF-8'
|
254
|
+
```
|
255
|
+
|
256
|
+
Allowed encoding names are the same as accepted by `String#force_encoding`
|
257
|
+
|
258
|
+
### Date Validation
|
259
|
+
|
243
260
|
Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:
|
244
261
|
|
245
262
|
```ruby
|
246
263
|
Zip.warn_invalid_date = false
|
247
264
|
```
|
248
265
|
|
266
|
+
### Size Validation
|
267
|
+
|
268
|
+
**This setting defaults to `false` in rubyzip 1.3 for backward compatibility, but it will default to `true` in rubyzip 2.0.**
|
269
|
+
|
270
|
+
If you set
|
271
|
+
```
|
272
|
+
Zip.validate_entry_sizes = true
|
273
|
+
```
|
274
|
+
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:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
|
278
|
+
MAX_FILES = 100
|
279
|
+
Zip::File.open('foo.zip') do |zip_file|
|
280
|
+
num_files = 0
|
281
|
+
zip_file.each do |entry|
|
282
|
+
num_files += 1 if entry.file?
|
283
|
+
raise 'Too many extracted files' if num_files > MAX_FILES
|
284
|
+
raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
|
285
|
+
entry.extract
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
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
|
291
|
+
```ruby
|
292
|
+
Zip.validate_entry_sizes = false
|
293
|
+
```
|
294
|
+
|
295
|
+
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.
|
296
|
+
|
297
|
+
### Default Compression
|
298
|
+
|
249
299
|
You can set the default compression level like so:
|
250
300
|
|
251
301
|
```ruby
|
252
302
|
Zip.default_compression = Zlib::DEFAULT_COMPRESSION
|
253
303
|
```
|
304
|
+
|
254
305
|
It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
|
255
306
|
|
256
|
-
|
307
|
+
### Zip64 Support
|
308
|
+
|
309
|
+
By default, Zip64 support is disabled for writing. To enable it do this:
|
257
310
|
|
258
311
|
```ruby
|
259
|
-
Zip.
|
312
|
+
Zip.write_zip64_support = true
|
260
313
|
```
|
261
314
|
|
262
|
-
|
315
|
+
_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
|
316
|
+
|
317
|
+
### Block Form
|
263
318
|
|
264
319
|
You can set multiple settings at the same time by using a block:
|
265
320
|
|
@@ -272,14 +327,6 @@ You can set multiple settings at the same time by using a block:
|
|
272
327
|
end
|
273
328
|
```
|
274
329
|
|
275
|
-
By default, Zip64 support is disabled for writing. To enable it do this:
|
276
|
-
|
277
|
-
```ruby
|
278
|
-
Zip.write_zip64_support = true
|
279
|
-
```
|
280
|
-
|
281
|
-
_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
|
282
|
-
|
283
330
|
## Developing
|
284
331
|
|
285
332
|
To run the test you need to do this:
|
data/lib/zip.rb
CHANGED
@@ -42,7 +42,8 @@ module Zip
|
|
42
42
|
:write_zip64_support,
|
43
43
|
:warn_invalid_date,
|
44
44
|
:case_insensitive_match,
|
45
|
-
:force_entry_names_encoding
|
45
|
+
:force_entry_names_encoding,
|
46
|
+
:validate_entry_sizes
|
46
47
|
|
47
48
|
def reset!
|
48
49
|
@_ran_once = false
|
@@ -54,6 +55,7 @@ module Zip
|
|
54
55
|
@write_zip64_support = false
|
55
56
|
@warn_invalid_date = true
|
56
57
|
@case_insensitive_match = false
|
58
|
+
@validate_entry_sizes = false
|
57
59
|
end
|
58
60
|
|
59
61
|
def setup
|
data/lib/zip/entry.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'pathname'
|
1
2
|
module Zip
|
2
3
|
class Entry
|
3
4
|
STORED = 0
|
@@ -117,7 +118,7 @@ module Zip
|
|
117
118
|
return false unless cleanpath.relative?
|
118
119
|
root = ::File::SEPARATOR
|
119
120
|
naive_expanded_path = ::File.join(root, cleanpath.to_s)
|
120
|
-
|
121
|
+
::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
|
121
122
|
end
|
122
123
|
|
123
124
|
def local_entry_offset #:nodoc:all
|
@@ -275,10 +276,10 @@ module Zip
|
|
275
276
|
zip64 = @extra['Zip64']
|
276
277
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
277
278
|
@version_needed_to_extract, # version needed to extract
|
278
|
-
@gp_flags, # @gp_flags
|
279
|
+
@gp_flags, # @gp_flags
|
279
280
|
@compression_method,
|
280
|
-
@time.to_binary_dos_time, # @last_mod_time
|
281
|
-
@time.to_binary_dos_date, # @last_mod_date
|
281
|
+
@time.to_binary_dos_time, # @last_mod_time
|
282
|
+
@time.to_binary_dos_date, # @last_mod_date
|
282
283
|
@crc,
|
283
284
|
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
284
285
|
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
@@ -432,11 +433,11 @@ module Zip
|
|
432
433
|
@header_signature,
|
433
434
|
@version, # version of encoding software
|
434
435
|
@fstype, # filesystem type
|
435
|
-
@version_needed_to_extract, # @versionNeededToExtract
|
436
|
-
@gp_flags, # @gp_flags
|
436
|
+
@version_needed_to_extract, # @versionNeededToExtract
|
437
|
+
@gp_flags, # @gp_flags
|
437
438
|
@compression_method,
|
438
|
-
@time.to_binary_dos_time, # @last_mod_time
|
439
|
-
@time.to_binary_dos_date, # @last_mod_date
|
439
|
+
@time.to_binary_dos_time, # @last_mod_time
|
440
|
+
@time.to_binary_dos_date, # @last_mod_date
|
440
441
|
@crc,
|
441
442
|
zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
|
442
443
|
zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
|
@@ -602,9 +603,21 @@ module Zip
|
|
602
603
|
get_input_stream do |is|
|
603
604
|
set_extra_attributes_on_path(dest_path)
|
604
605
|
|
605
|
-
|
606
|
+
bytes_written = 0
|
607
|
+
warned = false
|
608
|
+
buf = ''.dup
|
606
609
|
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
607
610
|
os << buf
|
611
|
+
bytes_written += buf.bytesize
|
612
|
+
if bytes_written > size && !warned
|
613
|
+
message = "Entry #{name} should be #{size}B but is larger when inflated"
|
614
|
+
if ::Zip.validate_entry_sizes
|
615
|
+
raise ::Zip::EntrySizeError, message
|
616
|
+
else
|
617
|
+
puts "WARNING: #{message}"
|
618
|
+
warned = true
|
619
|
+
end
|
620
|
+
end
|
608
621
|
end
|
609
622
|
end
|
610
623
|
end
|
data/lib/zip/errors.rb
CHANGED
data/lib/zip/extra_field.rb
CHANGED
data/lib/zip/file.rb
CHANGED
@@ -64,24 +64,38 @@ module Zip
|
|
64
64
|
|
65
65
|
# Opens a zip archive. Pass true as the second parameter to create
|
66
66
|
# a new archive if it doesn't exist already.
|
67
|
-
def initialize(
|
67
|
+
def initialize(path_or_io, create = false, buffer = false, options = {})
|
68
68
|
super()
|
69
|
-
@name =
|
69
|
+
@name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
|
70
70
|
@comment = ''
|
71
71
|
@create = create ? true : false # allow any truthy value to mean true
|
72
|
-
|
72
|
+
|
73
|
+
if ::File.size?(@name.to_s)
|
74
|
+
# There is a file, which exists, that is associated with this zip.
|
73
75
|
@create = false
|
74
|
-
@file_permissions = ::File.stat(
|
75
|
-
|
76
|
-
|
76
|
+
@file_permissions = ::File.stat(@name).mode
|
77
|
+
|
78
|
+
if buffer
|
79
|
+
read_from_stream(path_or_io)
|
80
|
+
else
|
81
|
+
::File.open(@name, 'rb') do |f|
|
82
|
+
read_from_stream(f)
|
83
|
+
end
|
77
84
|
end
|
85
|
+
elsif buffer && path_or_io.size > 0
|
86
|
+
# This zip is probably a non-empty StringIO.
|
87
|
+
read_from_stream(path_or_io)
|
78
88
|
elsif @create
|
89
|
+
# This zip is completely new/empty and is to be created.
|
79
90
|
@entry_set = EntrySet.new
|
80
|
-
elsif ::File.zero?(
|
81
|
-
|
91
|
+
elsif ::File.zero?(@name)
|
92
|
+
# A file exists, but it is empty.
|
93
|
+
raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?"
|
82
94
|
else
|
83
|
-
|
95
|
+
# Everything is wrong.
|
96
|
+
raise Error, "File #{@name} not found"
|
84
97
|
end
|
98
|
+
|
85
99
|
@stored_entries = @entry_set.dup
|
86
100
|
@stored_comment = @comment
|
87
101
|
@restore_ownership = options[:restore_ownership] || false
|
@@ -119,17 +133,16 @@ module Zip
|
|
119
133
|
unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.is_a?(String)
|
120
134
|
raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
|
121
135
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
136
|
+
|
137
|
+
io = ::StringIO.new(io) if io.is_a?(::String)
|
138
|
+
|
139
|
+
# https://github.com/rubyzip/rubyzip/issues/119
|
140
|
+
io.binmode if io.respond_to?(:binmode)
|
141
|
+
|
129
142
|
zf = ::Zip::File.new(io, true, true, options)
|
130
|
-
zf.read_from_stream(io)
|
131
143
|
return zf unless block_given?
|
132
144
|
yield zf
|
145
|
+
|
133
146
|
begin
|
134
147
|
zf.write_buffer(io)
|
135
148
|
rescue IOError => e
|
@@ -274,6 +287,13 @@ module Zip
|
|
274
287
|
@entry_set << new_entry
|
275
288
|
end
|
276
289
|
|
290
|
+
# Convenience method for adding the contents of a file to the archive
|
291
|
+
# in Stored format (uncompressed)
|
292
|
+
def add_stored(entry, src_path, &continue_on_exists_proc)
|
293
|
+
entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED)
|
294
|
+
add(entry, src_path, &continue_on_exists_proc)
|
295
|
+
end
|
296
|
+
|
277
297
|
# Removes the specified entry.
|
278
298
|
def remove(entry)
|
279
299
|
@entry_set.delete(get_entry(entry))
|
data/lib/zip/inflater.rb
CHANGED
data/lib/zip/version.rb
CHANGED
Binary file
|
data/test/file_extract_test.rb
CHANGED
@@ -10,6 +10,10 @@ class ZipFileExtractTest < MiniTest::Test
|
|
10
10
|
::File.delete(EXTRACTED_FILENAME) if ::File.exist?(EXTRACTED_FILENAME)
|
11
11
|
end
|
12
12
|
|
13
|
+
def teardown
|
14
|
+
::Zip.reset!
|
15
|
+
end
|
16
|
+
|
13
17
|
def test_extract
|
14
18
|
::Zip::File.open(TEST_ZIP.zip_name) do |zf|
|
15
19
|
zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME)
|
@@ -80,4 +84,62 @@ class ZipFileExtractTest < MiniTest::Test
|
|
80
84
|
end
|
81
85
|
assert(!File.exist?(outFile))
|
82
86
|
end
|
87
|
+
|
88
|
+
def test_extract_incorrect_size
|
89
|
+
# The uncompressed size fields in the zip file cannot be trusted. This makes
|
90
|
+
# it harder for callers to validate the sizes of the files they are
|
91
|
+
# extracting, which can lead to denial of service. See also
|
92
|
+
# https://en.wikipedia.org/wiki/Zip_bomb
|
93
|
+
Dir.mktmpdir do |tmp|
|
94
|
+
real_zip = File.join(tmp, 'real.zip')
|
95
|
+
fake_zip = File.join(tmp, 'fake.zip')
|
96
|
+
file_name = 'a'
|
97
|
+
true_size = 500_000
|
98
|
+
fake_size = 1
|
99
|
+
|
100
|
+
::Zip::File.open(real_zip, ::Zip::File::CREATE) do |zf|
|
101
|
+
zf.get_output_stream(file_name) do |os|
|
102
|
+
os.write 'a' * true_size
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
compressed_size = nil
|
107
|
+
::Zip::File.open(real_zip) do |zf|
|
108
|
+
a_entry = zf.find_entry(file_name)
|
109
|
+
compressed_size = a_entry.compressed_size
|
110
|
+
assert_equal true_size, a_entry.size
|
111
|
+
end
|
112
|
+
|
113
|
+
true_size_bytes = [compressed_size, true_size, file_name.size].pack('LLS')
|
114
|
+
fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('LLS')
|
115
|
+
|
116
|
+
data = File.binread(real_zip)
|
117
|
+
assert data.include?(true_size_bytes)
|
118
|
+
data.gsub! true_size_bytes, fake_size_bytes
|
119
|
+
|
120
|
+
File.open(fake_zip, 'wb') do |file|
|
121
|
+
file.write data
|
122
|
+
end
|
123
|
+
|
124
|
+
Dir.chdir tmp do
|
125
|
+
::Zip::File.open(fake_zip) do |zf|
|
126
|
+
a_entry = zf.find_entry(file_name)
|
127
|
+
assert_equal fake_size, a_entry.size
|
128
|
+
|
129
|
+
::Zip.validate_entry_sizes = false
|
130
|
+
a_entry.extract
|
131
|
+
assert_equal true_size, File.size(file_name)
|
132
|
+
FileUtils.rm file_name
|
133
|
+
|
134
|
+
::Zip.validate_entry_sizes = true
|
135
|
+
error = assert_raises ::Zip::EntrySizeError do
|
136
|
+
a_entry.extract
|
137
|
+
end
|
138
|
+
assert_equal \
|
139
|
+
'Entry a should be 1B but is larger when inflated',
|
140
|
+
error.message
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
83
145
|
end
|
data/test/file_test.rb
CHANGED
@@ -103,6 +103,13 @@ class ZipFileTest < MiniTest::Test
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
+
def test_open_buffer_with_string
|
107
|
+
string = File.read('test/data/rubycode.zip')
|
108
|
+
::Zip::File.open_buffer string do |zf|
|
109
|
+
assert zf.entries.map { |e| e.name }.include?('zippedruby1.rb')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
106
113
|
def test_open_buffer_with_stringio
|
107
114
|
string_io = StringIO.new File.read('test/data/rubycode.zip')
|
108
115
|
::Zip::File.open_buffer string_io do |zf|
|
@@ -113,14 +120,52 @@ class ZipFileTest < MiniTest::Test
|
|
113
120
|
def test_close_buffer_with_stringio
|
114
121
|
string_io = StringIO.new File.read('test/data/rubycode.zip')
|
115
122
|
zf = ::Zip::File.open_buffer string_io
|
116
|
-
|
123
|
+
assert_nil zf.close
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_open_buffer_no_op_does_not_change_file
|
127
|
+
Dir.mktmpdir do |tmp|
|
128
|
+
test_zip = File.join(tmp, 'test.zip')
|
129
|
+
FileUtils.cp 'test/data/rubycode.zip', test_zip
|
130
|
+
|
131
|
+
# Note: this may change the file if it is opened with r+b instead of rb.
|
132
|
+
# The 'extra fields' in this particular zip file get reordered.
|
133
|
+
File.open(test_zip, 'rb') do |file|
|
134
|
+
Zip::File.open_buffer(file) do |zf|
|
135
|
+
nil # do nothing
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
assert_equal \
|
140
|
+
File.binread('test/data/rubycode.zip'),
|
141
|
+
File.binread(test_zip)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_open_buffer_close_does_not_change_file
|
146
|
+
Dir.mktmpdir do |tmp|
|
147
|
+
test_zip = File.join(tmp, 'test.zip')
|
148
|
+
FileUtils.cp 'test/data/rubycode.zip', test_zip
|
149
|
+
|
150
|
+
File.open(test_zip, 'rb') do |file|
|
151
|
+
zf = Zip::File.open_buffer(file)
|
152
|
+
refute zf.commit_required?
|
153
|
+
assert_nil zf.close
|
154
|
+
end
|
155
|
+
|
156
|
+
assert_equal \
|
157
|
+
File.binread('test/data/rubycode.zip'),
|
158
|
+
File.binread(test_zip)
|
159
|
+
end
|
117
160
|
end
|
118
161
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
162
|
+
def test_open_buffer_with_io_and_block
|
163
|
+
File.open('test/data/rubycode.zip') do |io|
|
164
|
+
io.set_encoding(Encoding::BINARY) # not strictly required but can be set
|
165
|
+
Zip::File.open_buffer(io) do |zip_io|
|
166
|
+
# left empty on purpose
|
167
|
+
end
|
168
|
+
end
|
124
169
|
end
|
125
170
|
|
126
171
|
def test_open_buffer_without_block
|
@@ -159,6 +204,26 @@ class ZipFileTest < MiniTest::Test
|
|
159
204
|
zfRead.get_input_stream(entryName) { |zis| zis.read })
|
160
205
|
end
|
161
206
|
|
207
|
+
def test_add_stored
|
208
|
+
srcFile = 'test/data/file2.txt'
|
209
|
+
entryName = 'newEntryName.rb'
|
210
|
+
assert(::File.exist?(srcFile))
|
211
|
+
zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE)
|
212
|
+
zf.add_stored(entryName, srcFile)
|
213
|
+
zf.close
|
214
|
+
|
215
|
+
zfRead = ::Zip::File.new(EMPTY_FILENAME)
|
216
|
+
entry = zfRead.entries.first
|
217
|
+
assert_equal('', zfRead.comment)
|
218
|
+
assert_equal(1, zfRead.entries.length)
|
219
|
+
assert_equal(entryName, entry.name)
|
220
|
+
assert_equal(File.size(srcFile), entry.size)
|
221
|
+
assert_equal(entry.size, entry.compressed_size)
|
222
|
+
assert_equal(::Zip::Entry::STORED, entry.compression_method)
|
223
|
+
AssertEntry.assert_contents(srcFile,
|
224
|
+
zfRead.get_input_stream(entryName) { |zis| zis.read })
|
225
|
+
end
|
226
|
+
|
162
227
|
def test_recover_permissions_after_add_files_to_archive
|
163
228
|
srcZip = TEST_ZIP.zip_name
|
164
229
|
::File.chmod(0o664, srcZip)
|
data/test/path_traversal_test.rb
CHANGED
@@ -131,4 +131,11 @@ class PathTraversalTest < MiniTest::Test
|
|
131
131
|
refute File.exist?('/tmp/file.txt')
|
132
132
|
end
|
133
133
|
end
|
134
|
+
|
135
|
+
def test_entry_name_with_tilde
|
136
|
+
in_tmpdir do
|
137
|
+
extract_path_traversal_zip 'tilde.zip'
|
138
|
+
assert File.exist?('~tilde~')
|
139
|
+
end
|
140
|
+
end
|
134
141
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyzip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Simonov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -164,6 +164,7 @@ files:
|
|
164
164
|
- test/data/path_traversal/jwilk/relative2.zip
|
165
165
|
- test/data/path_traversal/jwilk/symlink.zip
|
166
166
|
- test/data/path_traversal/relative1.zip
|
167
|
+
- test/data/path_traversal/tilde.zip
|
167
168
|
- test/data/path_traversal/tuzovakaoff/README.md
|
168
169
|
- test/data/path_traversal/tuzovakaoff/absolutepath.zip
|
169
170
|
- test/data/path_traversal/tuzovakaoff/symlink.zip
|
@@ -210,7 +211,12 @@ files:
|
|
210
211
|
homepage: http://github.com/rubyzip/rubyzip
|
211
212
|
licenses:
|
212
213
|
- BSD 2-Clause
|
213
|
-
metadata:
|
214
|
+
metadata:
|
215
|
+
bug_tracker_uri: https://github.com/rubyzip/rubyzip/issues
|
216
|
+
changelog_uri: https://github.com/rubyzip/rubyzip/blob/v1.3.0/Changelog.md
|
217
|
+
documentation_uri: https://www.rubydoc.info/gems/rubyzip/1.3.0
|
218
|
+
source_code_uri: https://github.com/rubyzip/rubyzip/tree/v1.3.0
|
219
|
+
wiki_uri: https://github.com/rubyzip/rubyzip/wiki
|
214
220
|
post_install_message:
|
215
221
|
rdoc_options: []
|
216
222
|
require_paths:
|
@@ -226,8 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
232
|
- !ruby/object:Gem::Version
|
227
233
|
version: '0'
|
228
234
|
requirements: []
|
229
|
-
|
230
|
-
rubygems_version: 2.6.13
|
235
|
+
rubygems_version: 3.0.3
|
231
236
|
signing_key:
|
232
237
|
specification_version: 4
|
233
238
|
summary: rubyzip is a ruby module for reading and writing zip files
|
@@ -280,6 +285,7 @@ test_files:
|
|
280
285
|
- test/data/rubycode2.zip
|
281
286
|
- test/data/mimetype
|
282
287
|
- test/data/zipWithEncryption.zip
|
288
|
+
- test/data/path_traversal/tilde.zip
|
283
289
|
- test/data/path_traversal/Makefile
|
284
290
|
- test/data/path_traversal/relative1.zip
|
285
291
|
- test/data/path_traversal/jwilk/dirsymlink.zip
|