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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 898367588fc593bddd565ee8626c75cfbcddfce2
4
- data.tar.gz: 24d90fd344a11d8cf3b8098c459eb2c765e933e7
2
+ SHA256:
3
+ metadata.gz: 7861f60d52fbe54891831d9239df3e3e927a33f1f2d00abcb7b2693a8c01097d
4
+ data.tar.gz: c04844369dbc75f40583f3d8aab34cbf2cfcce293b3c30570fa76208755a3157
5
5
  SHA512:
6
- metadata.gz: 4f00c8c74720ba3bf103596601400f57e89d03cdd90b4423debd4c96ed6150a3db0fa8f7407201657cf6a7ee0b2e6586c7a284e1398dd1cf44c523799e1b25be
7
- data.tar.gz: 3a434114b84d7b109e26aec4821b395ad100efda591ad004656c6b5fd8cdba3740e50c171409c726a9770996e0815ef0324c87063d975024642c6f89ba56377e
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
- * Ruby 1.9.2 or greater
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
- Copy from [here](https://github.com/rubyzip/rubyzip/blob/05916bf89181e1955118fd3ea059f18acac28cc8/samples/example_recursive.rb )
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.get_output_stream(zipfile_path) do |f|
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.extract(dest_file)
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
- 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:
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
- 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:
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.force_entry_names_encoding = 'UTF-8'
312
+ Zip.write_zip64_support = true
260
313
  ```
261
314
 
262
- Allowed encoding names are the same as accepted by `String#force_encoding`
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
@@ -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
- cleanpath.expand_path(root).to_s == naive_expanded_path
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
- buf = ''
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
@@ -4,6 +4,7 @@ module Zip
4
4
  class DestinationFileExistsError < Error; end
5
5
  class CompressionMethodError < Error; end
6
6
  class EntryNameError < Error; end
7
+ class EntrySizeError < Error; end
7
8
  class InternalError < Error; end
8
9
  class GPFBit3Error < Error; end
9
10
 
@@ -26,7 +26,7 @@ module Zip
26
26
  end
27
27
 
28
28
  def create_unknown_item
29
- s = ''
29
+ s = ''.dup
30
30
  class << s
31
31
  alias_method :to_c_dir_bin, :to_s
32
32
  alias_method :to_local_bin, :to_s
@@ -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(file_name, create = false, buffer = false, options = {})
67
+ def initialize(path_or_io, create = false, buffer = false, options = {})
68
68
  super()
69
- @name = file_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
- if !buffer && ::File.size?(file_name)
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(file_name).mode
75
- ::File.open(name, 'rb') do |f|
76
- read_from_stream(f)
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?(file_name)
81
- raise Error, "File #{file_name} has zero size. Did you mean to pass the create flag?"
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
- raise Error, "File #{file_name} not found"
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
- if io.is_a?(::String)
123
- require 'stringio'
124
- io = ::StringIO.new(io)
125
- elsif io.respond_to?(:binmode)
126
- # https://github.com/rubyzip/rubyzip/issues/119
127
- io.binmode
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))
@@ -3,7 +3,7 @@ module Zip
3
3
  def initialize(input_stream, decrypter = NullDecrypter.new)
4
4
  super(input_stream)
5
5
  @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
6
- @output_buffer = ''
6
+ @output_buffer = ''.dup
7
7
  @has_returned_empty_string = false
8
8
  @decrypter = decrypter
9
9
  end
@@ -1,3 +1,3 @@
1
1
  module Zip
2
- VERSION = '1.2.2'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -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
@@ -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
- assert(zf.close || true) # Poor man's refute_raises
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 test_close_buffer_with_io
120
- f = File.open('test/data/rubycode.zip')
121
- zf = ::Zip::File.open_buffer f
122
- assert zf.close
123
- f.close
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)
@@ -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.2.2
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: 2018-08-31 00:00:00.000000000 Z
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
- rubyforge_project:
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