rubyzip 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +83 -35
  3. data/lib/zip.rb +11 -1
  4. data/lib/zip/central_directory.rb +3 -3
  5. data/lib/zip/compressor.rb +1 -2
  6. data/lib/zip/constants.rb +3 -3
  7. data/lib/zip/crypto/null_encryption.rb +2 -4
  8. data/lib/zip/decompressor.rb +1 -1
  9. data/lib/zip/dos_time.rb +1 -1
  10. data/lib/zip/entry.rb +67 -57
  11. data/lib/zip/entry_set.rb +3 -3
  12. data/lib/zip/errors.rb +1 -0
  13. data/lib/zip/extra_field.rb +2 -2
  14. data/lib/zip/extra_field/generic.rb +1 -1
  15. data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
  16. data/lib/zip/file.rb +47 -27
  17. data/lib/zip/filesystem.rb +17 -13
  18. data/lib/zip/inflater.rb +2 -2
  19. data/lib/zip/input_stream.rb +10 -7
  20. data/lib/zip/ioextras/abstract_input_stream.rb +1 -1
  21. data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
  22. data/lib/zip/output_stream.rb +5 -5
  23. data/lib/zip/pass_thru_decompressor.rb +1 -1
  24. data/lib/zip/streamable_stream.rb +1 -1
  25. data/lib/zip/version.rb +1 -1
  26. data/samples/example_recursive.rb +14 -15
  27. data/samples/gtk_ruby_zip.rb +1 -1
  28. data/samples/qtzip.rb +1 -1
  29. data/samples/zipfind.rb +2 -2
  30. metadata +25 -121
  31. data/test/basic_zip_file_test.rb +0 -60
  32. data/test/case_sensitivity_test.rb +0 -69
  33. data/test/central_directory_entry_test.rb +0 -69
  34. data/test/central_directory_test.rb +0 -100
  35. data/test/crypto/null_encryption_test.rb +0 -57
  36. data/test/crypto/traditional_encryption_test.rb +0 -80
  37. data/test/data/WarnInvalidDate.zip +0 -0
  38. data/test/data/file1.txt +0 -46
  39. data/test/data/file1.txt.deflatedData +0 -0
  40. data/test/data/file2.txt +0 -1504
  41. data/test/data/globTest.zip +0 -0
  42. data/test/data/globTest/foo.txt +0 -0
  43. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  44. data/test/data/globTest/food.txt +0 -0
  45. data/test/data/mimetype +0 -1
  46. data/test/data/notzippedruby.rb +0 -7
  47. data/test/data/ntfs.zip +0 -0
  48. data/test/data/oddExtraField.zip +0 -0
  49. data/test/data/rubycode.zip +0 -0
  50. data/test/data/rubycode2.zip +0 -0
  51. data/test/data/test.xls +0 -0
  52. data/test/data/testDirectory.bin +0 -0
  53. data/test/data/zip64-sample.zip +0 -0
  54. data/test/data/zipWithDirs.zip +0 -0
  55. data/test/data/zipWithEncryption.zip +0 -0
  56. data/test/deflater_test.rb +0 -65
  57. data/test/encryption_test.rb +0 -42
  58. data/test/entry_set_test.rb +0 -163
  59. data/test/entry_test.rb +0 -154
  60. data/test/errors_test.rb +0 -34
  61. data/test/extra_field_test.rb +0 -76
  62. data/test/file_extract_directory_test.rb +0 -54
  63. data/test/file_extract_test.rb +0 -83
  64. data/test/file_permissions_test.rb +0 -69
  65. data/test/file_split_test.rb +0 -57
  66. data/test/file_test.rb +0 -583
  67. data/test/filesystem/dir_iterator_test.rb +0 -58
  68. data/test/filesystem/directory_test.rb +0 -121
  69. data/test/filesystem/file_mutating_test.rb +0 -88
  70. data/test/filesystem/file_nonmutating_test.rb +0 -508
  71. data/test/filesystem/file_stat_test.rb +0 -64
  72. data/test/gentestfiles.rb +0 -126
  73. data/test/inflater_test.rb +0 -14
  74. data/test/input_stream_test.rb +0 -182
  75. data/test/ioextras/abstract_input_stream_test.rb +0 -102
  76. data/test/ioextras/abstract_output_stream_test.rb +0 -106
  77. data/test/ioextras/fake_io_test.rb +0 -18
  78. data/test/local_entry_test.rb +0 -154
  79. data/test/output_stream_test.rb +0 -128
  80. data/test/pass_thru_compressor_test.rb +0 -30
  81. data/test/pass_thru_decompressor_test.rb +0 -14
  82. data/test/samples/example_recursive_test.rb +0 -37
  83. data/test/settings_test.rb +0 -95
  84. data/test/test_helper.rb +0 -234
  85. data/test/unicode_file_names_and_comments_test.rb +0 -50
  86. data/test/zip64_full_test.rb +0 -51
  87. data/test/zip64_support_test.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1b7ea085db9b27be420d541113214a73ee044c9f
4
- data.tar.gz: ea28ff723bfd70f2e6f7a02f4fe28b805aa4a47e
2
+ SHA256:
3
+ metadata.gz: 4e2be8aa3dea85f1e99fa26b5b2530ede874228f35379181d5219eb3864a0e33
4
+ data.tar.gz: '0985b8cd0b421a0bdf83953ba907ddad52d236948a81a242f322f0f5fa2145a6'
5
5
  SHA512:
6
- metadata.gz: 121d7129aa37b72da82b32882401b7837c9cface1edf1b8bfb911cd20a55b2ecdacd308de9b7ed0cb6bc0b45d9d974faf28826efe191ddc36d3eb8431f8fe6f0
7
- data.tar.gz: b1ef9770bcd133de33f61642665d511a11aff21aba880cf1226df0c8e1b3602833679384d76bf6877b19567406f7bd9a60de8bd78aded3f303660fbb80b31bb7
6
+ metadata.gz: c870bef8352ddb4e706a847f66da596fe556938d1b1422c3541f8ab24aa10bbdb6be577cd031853459101e24a4290aa816436834d1cae66dd26238b451f41d15
7
+ data.tar.gz: bb5c8ca7d286346bfad64a6e1bd637cb8a40a99974bc62fbd76ea1a5ee139d16dcf52a8480cc219e48e311d84046feafab2c497cfbeb0721940a06a5c7a163a2
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 2.4 or greater (for rubyzip 2.0; use 1.x for older rubies)
23
24
 
24
25
  ## Installation
26
+
25
27
  Rubyzip is available on RubyGems:
26
28
 
27
29
  ```
@@ -52,14 +54,15 @@ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
52
54
  # Two arguments:
53
55
  # - The name of the file as it will appear in the archive
54
56
  # - The original file, including the path to find it
55
- zipfile.add(filename, folder + '/' + filename)
57
+ zipfile.add(filename, File.join(folder, filename))
56
58
  end
57
- zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
59
+ zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" }
58
60
  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,40 +86,37 @@ 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
- ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
89
- write_entries entries, '', io
91
+ ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
92
+ write_entries entries, '', zipfile
90
93
  end
91
94
  end
92
95
 
93
96
  private
94
97
 
95
98
  # A helper method to make the recursion work.
96
- def write_entries(entries, path, io)
99
+ def write_entries(entries, path, zipfile)
97
100
  entries.each do |e|
98
- zip_file_path = path == '' ? e : File.join(path, e)
99
- disk_file_path = File.join(@input_dir, zip_file_path)
100
- puts "Deflating #{disk_file_path}"
101
+ zipfile_path = path == '' ? e : File.join(path, e)
102
+ disk_file_path = File.join(@input_dir, zipfile_path)
101
103
 
102
104
  if File.directory? disk_file_path
103
- recursively_deflate_directory(disk_file_path, io, zip_file_path)
105
+ recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
104
106
  else
105
- put_into_archive(disk_file_path, io, zip_file_path)
107
+ put_into_archive(disk_file_path, zipfile, zipfile_path)
106
108
  end
107
109
  end
108
110
  end
109
111
 
110
- def recursively_deflate_directory(disk_file_path, io, zip_file_path)
111
- io.mkdir zip_file_path
112
- subdir = Dir.entries(disk_file_path) - %w(. ..)
113
- write_entries subdir, zip_file_path, io
112
+ def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
113
+ zipfile.mkdir zipfile_path
114
+ subdir = Dir.entries(disk_file_path) - %w[. ..]
115
+ write_entries subdir, zipfile_path, zipfile
114
116
  end
115
117
 
116
- def put_into_archive(disk_file_path, io, zip_file_path)
117
- io.get_output_stream(zip_file_path) do |f|
118
- f.write(File.open(disk_file_path, 'rb').read)
119
- end
118
+ def put_into_archive(disk_file_path, zipfile, zipfile_path)
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
  ```
@@ -175,9 +179,7 @@ end
175
179
 
176
180
  But there is one exception when it is not working - General Purpose Flag Bit 3.
177
181
 
178
- ```
179
- 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
180
- ```
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
181
183
 
182
184
  If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception.
183
185
 
@@ -221,7 +223,9 @@ File.open(new_path, "wb") {|f| f.write(buffer.string) }
221
223
 
222
224
  ## Configuration
223
225
 
224
- 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:
225
229
 
226
230
  ```ruby
227
231
  Zip.on_exists_proc = true
@@ -235,25 +239,77 @@ Additionally, if you want to configure rubyzip to overwrite existing files while
235
239
  Zip.continue_on_exists_proc = true
236
240
  ```
237
241
 
242
+ ### Non-ASCII Names
243
+
238
244
  If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option:
239
245
 
240
246
  ```ruby
241
247
  Zip.unicode_names = true
242
248
  ```
243
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
+
244
260
  Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:
245
261
 
246
262
  ```ruby
247
263
  Zip.warn_invalid_date = false
248
264
  ```
249
265
 
266
+ ### Size Validation
267
+
268
+ By default (in rubyzip >= 2.0), 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:
269
+
270
+ ```ruby
271
+ MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
272
+ MAX_FILES = 100
273
+ Zip::File.open('foo.zip') do |zip_file|
274
+ num_files = 0
275
+ zip_file.each do |entry|
276
+ num_files += 1 if entry.file?
277
+ raise 'Too many extracted files' if num_files > MAX_FILES
278
+ raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
279
+ entry.extract
280
+ end
281
+ end
282
+ ```
283
+
284
+ 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
285
+ ```ruby
286
+ Zip.validate_entry_sizes = false
287
+ ```
288
+
289
+ 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.
290
+
291
+ ### Default Compression
292
+
250
293
  You can set the default compression level like so:
251
294
 
252
295
  ```ruby
253
296
  Zip.default_compression = Zlib::DEFAULT_COMPRESSION
254
297
  ```
298
+
255
299
  It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
256
300
 
301
+ ### Zip64 Support
302
+
303
+ By default, Zip64 support is disabled for writing. To enable it do this:
304
+
305
+ ```ruby
306
+ Zip.write_zip64_support = true
307
+ ```
308
+
309
+ _NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
310
+
311
+ ### Block Form
312
+
257
313
  You can set multiple settings at the same time by using a block:
258
314
 
259
315
  ```ruby
@@ -265,14 +321,6 @@ You can set multiple settings at the same time by using a block:
265
321
  end
266
322
  ```
267
323
 
268
- By default, Zip64 support is disabled for writing. To enable it do this:
269
-
270
- ```ruby
271
- Zip.write_zip64_support = true
272
- ```
273
-
274
- _NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
275
-
276
324
  ## Developing
277
325
 
278
326
  To run the test you need to do this:
data/lib/zip.rb CHANGED
@@ -34,7 +34,16 @@ require 'zip/errors'
34
34
 
35
35
  module Zip
36
36
  extend self
37
- attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date, :case_insensitive_match
37
+ attr_accessor :unicode_names,
38
+ :on_exists_proc,
39
+ :continue_on_exists_proc,
40
+ :sort_entries,
41
+ :default_compression,
42
+ :write_zip64_support,
43
+ :warn_invalid_date,
44
+ :case_insensitive_match,
45
+ :force_entry_names_encoding,
46
+ :validate_entry_sizes
38
47
 
39
48
  def reset!
40
49
  @_ran_once = false
@@ -46,6 +55,7 @@ module Zip
46
55
  @write_zip64_support = false
47
56
  @warn_invalid_date = true
48
57
  @case_insensitive_match = false
58
+ @validate_entry_sizes = true
49
59
  end
50
60
 
51
61
  def setup
@@ -96,7 +96,7 @@ module Zip
96
96
  @size_in_bytes = Entry.read_zip_64_long(buf)
97
97
  @cdir_offset = Entry.read_zip_64_long(buf)
98
98
  @zip_64_extensible = buf.slice!(0, buf.bytesize)
99
- raise Error, 'Zip consistency problem while reading eocd structure' unless buf.size == 0
99
+ raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
100
100
  end
101
101
 
102
102
  def read_e_o_c_d(buf) #:nodoc:
@@ -113,7 +113,7 @@ module Zip
113
113
  else
114
114
  buf.read(comment_length)
115
115
  end
116
- raise Error, 'Zip consistency problem while reading eocd structure' unless buf.size == 0
116
+ raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
117
117
  end
118
118
 
119
119
  def read_central_directory_entries(io) #:nodoc:
@@ -130,7 +130,7 @@ module Zip
130
130
 
131
131
  def read_from_stream(io) #:nodoc:
132
132
  buf = start_buf(io)
133
- if self.zip64_file?(buf)
133
+ if zip64_file?(buf)
134
134
  read_64_e_o_c_d(buf)
135
135
  else
136
136
  read_e_o_c_d(buf)
@@ -1,7 +1,6 @@
1
1
  module Zip
2
2
  class Compressor #:nodoc:all
3
- def finish
4
- end
3
+ def finish; end
5
4
  end
6
5
  end
7
6
 
@@ -11,9 +11,9 @@ module Zip
11
11
  VERSION_NEEDED_TO_EXTRACT = 20
12
12
  VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
13
13
 
14
- FILE_TYPE_FILE = 010
15
- FILE_TYPE_DIR = 004
16
- FILE_TYPE_SYMLINK = 012
14
+ FILE_TYPE_FILE = 0o10
15
+ FILE_TYPE_DIR = 0o04
16
+ FILE_TYPE_SYMLINK = 0o12
17
17
 
18
18
  FSTYPE_FAT = 0
19
19
  FSTYPE_AMIGA = 1
@@ -24,8 +24,7 @@ module Zip
24
24
  ''
25
25
  end
26
26
 
27
- def reset!
28
- end
27
+ def reset!; end
29
28
  end
30
29
 
31
30
  class NullDecrypter < Decrypter
@@ -35,8 +34,7 @@ module Zip
35
34
  data
36
35
  end
37
36
 
38
- def reset!(_header)
39
- end
37
+ def reset!(_header); end
40
38
  end
41
39
  end
42
40
 
@@ -1,5 +1,5 @@
1
1
  module Zip
2
- class Decompressor #:nodoc:all
2
+ class Decompressor #:nodoc:all
3
3
  CHUNK_SIZE = 32_768
4
4
  def initialize(input_stream)
5
5
  super()
@@ -19,7 +19,7 @@ module Zip
19
19
  end
20
20
 
21
21
  def to_binary_dos_date
22
- (day) +
22
+ day +
23
23
  (month << 5) +
24
24
  ((year - 1980) << 9)
25
25
  end
@@ -1,3 +1,4 @@
1
+ require 'pathname'
1
2
  module Zip
2
3
  class Entry
3
4
  STORED = 0
@@ -99,7 +100,7 @@ module Zip
99
100
  end
100
101
 
101
102
  # Dynamic checkers
102
- %w(directory file symlink).each do |k|
103
+ %w[directory file symlink].each do |k|
103
104
  define_method "#{k}?" do
104
105
  file_type_is?(k.to_sym)
105
106
  end
@@ -109,6 +110,17 @@ module Zip
109
110
  @name.end_with?('/')
110
111
  end
111
112
 
113
+ # Is the name a relative path, free of `..` patterns that could lead to
114
+ # path traversal attacks? This does NOT handle symlinks; if the path
115
+ # contains symlinks, this check is NOT enough to guarantee safety.
116
+ def name_safe?
117
+ cleanpath = Pathname.new(@name).cleanpath
118
+ return false unless cleanpath.relative?
119
+ root = ::File::SEPARATOR
120
+ naive_expanded_path = ::File.join(root, cleanpath.to_s)
121
+ ::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
122
+ end
123
+
112
124
  def local_entry_offset #:nodoc:all
113
125
  local_header_offset + @local_header_size
114
126
  end
@@ -147,14 +159,17 @@ module Zip
147
159
  end
148
160
 
149
161
  # Extracts entry to file dest_path (defaults to @name).
150
- def extract(dest_path = @name, &block)
151
- block ||= proc { ::Zip.on_exists_proc }
152
-
153
- if @name.squeeze('/') =~ /\.{2}(?:\/|\z)/
154
- puts "WARNING: skipped \"../\" path component(s) in #{@name}"
162
+ # NB: The caller is responsible for making sure dest_path is safe, if it
163
+ # is passed.
164
+ def extract(dest_path = nil, &block)
165
+ if dest_path.nil? && !name_safe?
166
+ puts "WARNING: skipped #{@name} as unsafe"
155
167
  return self
156
168
  end
157
169
 
170
+ dest_path ||= @name
171
+ block ||= proc { ::Zip.on_exists_proc }
172
+
158
173
  if directory? || file? || symlink?
159
174
  __send__("create_#{@ftype}", dest_path, &block)
160
175
  else
@@ -239,7 +254,10 @@ module Zip
239
254
  @name = io.read(@name_length)
240
255
  extra = io.read(@extra_length)
241
256
 
242
- @name.gsub!('\\', '/')
257
+ @name.tr!('\\', '/')
258
+ if ::Zip.force_entry_names_encoding
259
+ @name.force_encoding(::Zip.force_entry_names_encoding)
260
+ end
243
261
 
244
262
  if extra && extra.bytesize != @extra_length
245
263
  raise ::Zip::Error, 'Truncated local zip entry header'
@@ -258,13 +276,13 @@ module Zip
258
276
  zip64 = @extra['Zip64']
259
277
  [::Zip::LOCAL_ENTRY_SIGNATURE,
260
278
  @version_needed_to_extract, # version needed to extract
261
- @gp_flags, # @gp_flags ,
279
+ @gp_flags, # @gp_flags
262
280
  @compression_method,
263
- @time.to_binary_dos_time, # @last_mod_time ,
264
- @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
265
283
  @crc,
266
- (zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
267
- (zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
284
+ zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
285
+ zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
268
286
  name_size,
269
287
  @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
270
288
  end
@@ -308,7 +326,7 @@ module Zip
308
326
  def set_ftype_from_c_dir_entry
309
327
  @ftype = case @fstype
310
328
  when ::Zip::FSTYPE_UNIX
311
- @unix_perms = (@external_file_attributes >> 16) & 07777
329
+ @unix_perms = (@external_file_attributes >> 16) & 0o7777
312
330
  case (@external_file_attributes >> 28)
313
331
  when ::Zip::FILE_TYPE_DIR
314
332
  :directory
@@ -364,6 +382,9 @@ module Zip
364
382
  check_c_dir_entry_signature
365
383
  set_time(@last_mod_date, @last_mod_time)
366
384
  @name = io.read(@name_length)
385
+ if ::Zip.force_entry_names_encoding
386
+ @name.force_encoding(::Zip.force_entry_names_encoding)
387
+ end
367
388
  read_c_dir_extra_field(io)
368
389
  @comment = io.read(@comment_length)
369
390
  check_c_dir_entry_comment_size
@@ -384,14 +405,14 @@ module Zip
384
405
  stat = file_stat(path)
385
406
  @unix_uid = stat.uid
386
407
  @unix_gid = stat.gid
387
- @unix_perms = stat.mode & 07777
408
+ @unix_perms = stat.mode & 0o7777
388
409
  end
389
410
 
390
411
  def set_unix_permissions_on_path(dest_path)
391
412
  # BUG: does not update timestamps into account
392
413
  # ignore setuid/setgid bits by default. honor if @restore_ownership
393
- unix_perms_mask = 01777
394
- unix_perms_mask = 07777 if @restore_ownership
414
+ unix_perms_mask = 0o1777
415
+ unix_perms_mask = 0o7777 if @restore_ownership
395
416
  ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
396
417
  ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
397
418
  # File::utimes()
@@ -412,21 +433,21 @@ module Zip
412
433
  @header_signature,
413
434
  @version, # version of encoding software
414
435
  @fstype, # filesystem type
415
- @version_needed_to_extract, # @versionNeededToExtract ,
416
- @gp_flags, # @gp_flags ,
436
+ @version_needed_to_extract, # @versionNeededToExtract
437
+ @gp_flags, # @gp_flags
417
438
  @compression_method,
418
- @time.to_binary_dos_time, # @last_mod_time ,
419
- @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
420
441
  @crc,
421
- (zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
422
- (zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
442
+ zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size,
443
+ zip64 && zip64.original_size ? 0xFFFFFFFF : @size,
423
444
  name_size,
424
445
  @extra ? @extra.c_dir_size : 0,
425
446
  comment_size,
426
- (zip64 && zip64.disk_start_number) ? 0xFFFF : 0, # disk number start
447
+ zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start
427
448
  @internal_file_attributes, # file type (binary=0, text=1)
428
449
  @external_file_attributes, # native filesystem attributes
429
- (zip64 && zip64.relative_header_offset) ? 0xFFFFFFFF : @local_header_offset,
450
+ zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset,
430
451
  @name,
431
452
  @extra,
432
453
  @comment
@@ -439,18 +460,18 @@ module Zip
439
460
  when ::Zip::FSTYPE_UNIX
440
461
  ft = case @ftype
441
462
  when :file
442
- @unix_perms ||= 0644
463
+ @unix_perms ||= 0o644
443
464
  ::Zip::FILE_TYPE_FILE
444
465
  when :directory
445
- @unix_perms ||= 0755
466
+ @unix_perms ||= 0o755
446
467
  ::Zip::FILE_TYPE_DIR
447
468
  when :symlink
448
- @unix_perms ||= 0755
469
+ @unix_perms ||= 0o755
449
470
  ::Zip::FILE_TYPE_SYMLINK
450
471
  end
451
472
 
452
473
  unless ft.nil?
453
- @external_file_attributes = (ft << 12 | (@unix_perms & 07777)) << 16
474
+ @external_file_attributes = (ft << 12 | (@unix_perms & 0o7777)) << 16
454
475
  end
455
476
  end
456
477
 
@@ -464,7 +485,7 @@ module Zip
464
485
  def ==(other)
465
486
  return false unless other.class == self.class
466
487
  # Compares contents of local entry and exposed fields
467
- keys_equal = %w(compression_method crc compressed_size size name extra filepath).all? do |k|
488
+ keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k|
468
489
  other.__send__(k.to_sym) == __send__(k.to_sym)
469
490
  end
470
491
  keys_equal && time.dos_equals(other.time)
@@ -494,7 +515,7 @@ module Zip
494
515
  end
495
516
  else
496
517
  zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
497
- zis.instance_variable_set(:@internal, true)
518
+ zis.instance_variable_set(:@complete_entry, self)
498
519
  zis.get_next_entry
499
520
  if block_given?
500
521
  begin
@@ -582,9 +603,21 @@ module Zip
582
603
  get_input_stream do |is|
583
604
  set_extra_attributes_on_path(dest_path)
584
605
 
585
- buf = ''
606
+ bytes_written = 0
607
+ warned = false
608
+ buf = ''.dup
586
609
  while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
587
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
588
621
  end
589
622
  end
590
623
  end
@@ -607,32 +640,9 @@ module Zip
607
640
 
608
641
  # BUG: create_symlink() does not use &block
609
642
  def create_symlink(dest_path)
610
- stat = nil
611
- begin
612
- stat = ::File.lstat(dest_path)
613
- rescue Errno::ENOENT
614
- end
615
-
616
- io = get_input_stream
617
- linkto = io.read
618
-
619
- if stat
620
- if stat.symlink?
621
- if ::File.readlink(dest_path) == linkto
622
- return
623
- else
624
- raise ::Zip::DestinationFileExistsError,
625
- "Cannot create symlink '#{dest_path}'. " \
626
- 'A symlink already exists with that name'
627
- end
628
- else
629
- raise ::Zip::DestinationFileExistsError,
630
- "Cannot create symlink '#{dest_path}'. " \
631
- 'A file already exists with that name'
632
- end
633
- end
634
-
635
- ::File.symlink(linkto, dest_path)
643
+ # TODO: Symlinks pose security challenges. Symlink support temporarily
644
+ # removed in view of https://github.com/rubyzip/rubyzip/issues/369 .
645
+ puts "WARNING: skipped symlink #{dest_path}"
636
646
  end
637
647
 
638
648
  # apply missing data from the zip64 extra information field, if present