rubyzip 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubyzip might be problematic. Click here for more details.

Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -17
  3. data/lib/zip.rb +9 -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 +46 -49
  11. data/lib/zip/entry_set.rb +3 -3
  12. data/lib/zip/extra_field.rb +1 -1
  13. data/lib/zip/extra_field/generic.rb +1 -1
  14. data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
  15. data/lib/zip/file.rb +12 -12
  16. data/lib/zip/filesystem.rb +17 -13
  17. data/lib/zip/inflater.rb +1 -1
  18. data/lib/zip/input_stream.rb +10 -7
  19. data/lib/zip/ioextras/abstract_input_stream.rb +1 -1
  20. data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
  21. data/lib/zip/output_stream.rb +5 -5
  22. data/lib/zip/pass_thru_decompressor.rb +1 -1
  23. data/lib/zip/streamable_stream.rb +1 -1
  24. data/lib/zip/version.rb +1 -1
  25. data/samples/example_recursive.rb +14 -15
  26. data/samples/gtk_ruby_zip.rb +1 -1
  27. data/samples/qtzip.rb +1 -1
  28. data/samples/zipfind.rb +2 -2
  29. data/test/central_directory_entry_test.rb +1 -1
  30. data/test/data/gpbit3stored.zip +0 -0
  31. data/test/data/path_traversal/Makefile +10 -0
  32. data/test/data/path_traversal/jwilk/README.md +5 -0
  33. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  34. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  35. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  36. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  37. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  38. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  39. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  40. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  41. data/test/data/path_traversal/relative1.zip +0 -0
  42. data/test/data/path_traversal/tuzovakaoff/README.md +3 -0
  43. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  44. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  45. data/test/data/rubycode.zip +0 -0
  46. data/test/errors_test.rb +1 -0
  47. data/test/file_permissions_test.rb +11 -15
  48. data/test/file_test.rb +27 -9
  49. data/test/filesystem/dir_iterator_test.rb +1 -1
  50. data/test/filesystem/directory_test.rb +29 -11
  51. data/test/filesystem/file_mutating_test.rb +3 -4
  52. data/test/filesystem/file_nonmutating_test.rb +31 -31
  53. data/test/filesystem/file_stat_test.rb +4 -4
  54. data/test/gentestfiles.rb +13 -13
  55. data/test/input_stream_test.rb +6 -6
  56. data/test/ioextras/abstract_output_stream_test.rb +2 -2
  57. data/test/path_traversal_test.rb +134 -0
  58. data/test/test_helper.rb +2 -2
  59. data/test/unicode_file_names_and_comments_test.rb +12 -0
  60. data/test/zip64_full_test.rb +2 -2
  61. metadata +95 -49
@@ -69,16 +69,15 @@ module Zip
69
69
  @name = file_name
70
70
  @comment = ''
71
71
  @create = create ? true : false # allow any truthy value to mean true
72
- case
73
- when !buffer && ::File.size?(file_name)
72
+ if !buffer && ::File.size?(file_name)
74
73
  @create = false
75
74
  @file_permissions = ::File.stat(file_name).mode
76
75
  ::File.open(name, 'rb') do |f|
77
76
  read_from_stream(f)
78
77
  end
79
- when @create
78
+ elsif @create
80
79
  @entry_set = EntrySet.new
81
- when ::File.zero?(file_name)
80
+ elsif ::File.zero?(file_name)
82
81
  raise Error, "File #{file_name} has zero size. Did you mean to pass the create flag?"
83
82
  else
84
83
  raise Error, "File #{file_name} not found"
@@ -151,10 +150,9 @@ module Zip
151
150
  end
152
151
 
153
152
  def get_segment_size_for_split(segment_size)
154
- case
155
- when MIN_SEGMENT_SIZE > segment_size
153
+ if MIN_SEGMENT_SIZE > segment_size
156
154
  MIN_SEGMENT_SIZE
157
- when MAX_SEGMENT_SIZE < segment_size
155
+ elsif MAX_SEGMENT_SIZE < segment_size
158
156
  MAX_SEGMENT_SIZE
159
157
  else
160
158
  segment_size
@@ -162,8 +160,10 @@ module Zip
162
160
  end
163
161
 
164
162
  def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
165
- partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
166
- partial_zip_file_name + ::File.extname(zip_file_name)) unless partial_zip_file_name.nil?
163
+ unless partial_zip_file_name.nil?
164
+ partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
165
+ partial_zip_file_name + ::File.extname(zip_file_name))
166
+ end
167
167
  partial_zip_file_name ||= zip_file_name
168
168
  partial_zip_file_name
169
169
  end
@@ -237,7 +237,7 @@ module Zip
237
237
  # specified. If a block is passed the stream object is passed to the block and
238
238
  # the stream is automatically closed afterwards just as with ruby's builtin
239
239
  # File.open method.
240
- def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
240
+ def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
241
241
  new_entry =
242
242
  if entry.kind_of?(Entry)
243
243
  entry
@@ -306,7 +306,7 @@ module Zip
306
306
  # Commits changes that has been made since the previous commit to
307
307
  # the zip archive.
308
308
  def commit
309
- return unless commit_required?
309
+ return if name.is_a?(StringIO) || !commit_required?
310
310
  on_success_replace do |tmp_file|
311
311
  ::Zip::OutputStream.open(tmp_file) do |zos|
312
312
  @entry_set.each do |e|
@@ -366,7 +366,7 @@ module Zip
366
366
  end
367
367
 
368
368
  # Creates a directory
369
- def mkdir(entryName, permissionInt = 0755)
369
+ def mkdir(entryName, permissionInt = 0o755)
370
370
  raise Errno::EEXIST, "File exists - #{entryName}" if find_entry(entryName)
371
371
  entryName = entryName.dup.to_s
372
372
  entryName << '/' unless entryName.end_with?('/')
@@ -142,9 +142,9 @@ module Zip
142
142
 
143
143
  def ftype
144
144
  if file?
145
- return 'file'
145
+ 'file'
146
146
  elsif directory?
147
- return 'directory'
147
+ 'directory'
148
148
  else
149
149
  raise StandardError, 'Unknown file type'
150
150
  end
@@ -198,30 +198,30 @@ module Zip
198
198
  alias grpowned? exists?
199
199
 
200
200
  def readable?(fileName)
201
- unix_mode_cmp(fileName, 0444)
201
+ unix_mode_cmp(fileName, 0o444)
202
202
  end
203
203
  alias readable_real? readable?
204
204
 
205
205
  def writable?(fileName)
206
- unix_mode_cmp(fileName, 0222)
206
+ unix_mode_cmp(fileName, 0o222)
207
207
  end
208
208
  alias writable_real? writable?
209
209
 
210
210
  def executable?(fileName)
211
- unix_mode_cmp(fileName, 0111)
211
+ unix_mode_cmp(fileName, 0o111)
212
212
  end
213
213
  alias executable_real? executable?
214
214
 
215
215
  def setuid?(fileName)
216
- unix_mode_cmp(fileName, 04000)
216
+ unix_mode_cmp(fileName, 0o4000)
217
217
  end
218
218
 
219
219
  def setgid?(fileName)
220
- unix_mode_cmp(fileName, 02000)
220
+ unix_mode_cmp(fileName, 0o2000)
221
221
  end
222
222
 
223
223
  def sticky?(fileName)
224
- unix_mode_cmp(fileName, 01000)
224
+ unix_mode_cmp(fileName, 0o1000)
225
225
  end
226
226
 
227
227
  def umask(*args)
@@ -237,8 +237,8 @@ module Zip
237
237
  expand_path(fileName) == '/' || (!entry.nil? && entry.directory?)
238
238
  end
239
239
 
240
- def open(fileName, openMode = 'r', permissionInt = 0644, &block)
241
- openMode.gsub!('b', '') # ignore b option
240
+ def open(fileName, openMode = 'r', permissionInt = 0o644, &block)
241
+ openMode.delete!('b') # ignore b option
242
242
  case openMode
243
243
  when 'r'
244
244
  @mappedZip.get_input_stream(fileName, &block)
@@ -260,7 +260,7 @@ module Zip
260
260
  # Returns nil for not found and nil for directories
261
261
  def size?(fileName)
262
262
  entry = @mappedZip.find_entry(fileName)
263
- (entry.nil? || entry.directory?) ? nil : entry.size
263
+ entry.nil? || entry.directory? ? nil : entry.size
264
264
  end
265
265
 
266
266
  def chown(ownerInt, groupInt, *filenames)
@@ -498,7 +498,7 @@ module Zip
498
498
  alias rmdir delete
499
499
  alias unlink delete
500
500
 
501
- def mkdir(entryName, permissionInt = 0755)
501
+ def mkdir(entryName, permissionInt = 0o755)
502
502
  @mappedZip.mkdir(entryName, permissionInt)
503
503
  end
504
504
 
@@ -573,6 +573,10 @@ module Zip
573
573
  @zipFile.get_output_stream(expand_to_entry(fileName), permissionInt, &aProc)
574
574
  end
575
575
 
576
+ def glob(pattern, *flags, &block)
577
+ @zipFile.glob(expand_to_entry(pattern), *flags, &block)
578
+ end
579
+
576
580
  def read(fileName)
577
581
  @zipFile.read(expand_to_entry(fileName))
578
582
  end
@@ -586,7 +590,7 @@ module Zip
586
590
  &continueOnExistsProc)
587
591
  end
588
592
 
589
- def mkdir(fileName, permissionInt = 0755)
593
+ def mkdir(fileName, permissionInt = 0o755)
590
594
  @zipFile.mkdir(expand_to_entry(fileName), permissionInt)
591
595
  end
592
596
 
@@ -5,7 +5,7 @@ module Zip
5
5
  @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
6
6
  @output_buffer = ''
7
7
  @has_returned_empty_string = false
8
- @decrypter = decrypter
8
+ @decrypter = decrypter
9
9
  end
10
10
 
11
11
  def sysread(number_of_bytes = nil, buf = '')
@@ -129,23 +129,26 @@ module Zip
129
129
  end
130
130
  if @current_entry && @current_entry.gp_flags & 8 == 8 && @current_entry.crc == 0 \
131
131
  && @current_entry.compressed_size == 0 \
132
- && @current_entry.size == 0 && !@internal
132
+ && @current_entry.size == 0 && !@complete_entry
133
133
  raise GPFBit3Error,
134
134
  'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \
135
135
  'Please use ::Zip::File instead of ::Zip::InputStream'
136
136
  end
137
- @decompressor = get_decompressor
137
+ @decompressor = get_decompressor
138
138
  flush
139
139
  @current_entry
140
140
  end
141
141
 
142
142
  def get_decompressor
143
- case
144
- when @current_entry.nil?
143
+ if @current_entry.nil?
145
144
  ::Zip::NullDecompressor
146
- when @current_entry.compression_method == ::Zip::Entry::STORED
147
- ::Zip::PassThruDecompressor.new(@archive_io, @current_entry.size)
148
- when @current_entry.compression_method == ::Zip::Entry::DEFLATED
145
+ elsif @current_entry.compression_method == ::Zip::Entry::STORED
146
+ if @current_entry.gp_flags & 8 == 8 && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry
147
+ ::Zip::PassThruDecompressor.new(@archive_io, @complete_entry.size)
148
+ else
149
+ ::Zip::PassThruDecompressor.new(@archive_io, @current_entry.size)
150
+ end
151
+ elsif @current_entry.compression_method == ::Zip::Entry::DEFLATED
149
152
  header = @archive_io.read(@decrypter.header_bytesize)
150
153
  @decrypter.reset!(header)
151
154
  ::Zip::Inflater.new(@archive_io, @decrypter)
@@ -33,7 +33,7 @@ module Zip
33
33
  sysread(number_of_bytes, buf)
34
34
  end
35
35
 
36
- if tbuf.nil? || tbuf.length == 0
36
+ if tbuf.nil? || tbuf.empty?
37
37
  return nil if number_of_bytes
38
38
  return ''
39
39
  end
@@ -15,7 +15,7 @@ module Zip
15
15
  end
16
16
 
17
17
  def printf(a_format_string, *params)
18
- self << sprintf(a_format_string, *params)
18
+ self << format(a_format_string, *params)
19
19
  end
20
20
 
21
21
  def putc(an_object)
@@ -87,11 +87,11 @@ module Zip
87
87
  # +entry+ can be a ZipEntry object or a string.
88
88
  def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression)
89
89
  raise Error, 'zip stream is closed' if @closed
90
- if entry_name.kind_of?(Entry)
91
- new_entry = entry_name
92
- else
93
- new_entry = Entry.new(@file_name, entry_name.to_s)
94
- end
90
+ new_entry = if entry_name.kind_of?(Entry)
91
+ entry_name
92
+ else
93
+ Entry.new(@file_name, entry_name.to_s)
94
+ end
95
95
  new_entry.comment = comment unless comment.nil?
96
96
  unless extra.nil?
97
97
  new_entry.extra = extra.is_a?(ExtraField) ? extra : ExtraField.new(extra.to_s)
@@ -1,5 +1,5 @@
1
1
  module Zip
2
- class PassThruDecompressor < Decompressor #:nodoc:all
2
+ class PassThruDecompressor < Decompressor #:nodoc:all
3
3
  def initialize(input_stream, chars_to_read)
4
4
  super(input_stream)
5
5
  @chars_to_read = chars_to_read
@@ -5,7 +5,7 @@ module Zip
5
5
  dirname = if zipfile.is_a?(::String)
6
6
  ::File.dirname(zipfile)
7
7
  else
8
- '.'
8
+ nil
9
9
  end
10
10
  @temp_file = Tempfile.new(::File.basename(name), dirname)
11
11
  @temp_file.binmode
@@ -1,3 +1,3 @@
1
1
  module Zip
2
- VERSION = '1.2.1'
2
+ VERSION = '1.2.2'
3
3
  end
@@ -19,37 +19,36 @@ class ZipFileGenerator
19
19
 
20
20
  # Zip the input directory.
21
21
  def write
22
- entries = Dir.entries(@input_dir) - %w(. ..)
22
+ entries = Dir.entries(@input_dir) - %w[. ..]
23
23
 
24
- ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
25
- write_entries entries, '', io
24
+ ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
25
+ write_entries entries, '', zipfile
26
26
  end
27
27
  end
28
28
 
29
29
  private
30
30
 
31
31
  # A helper method to make the recursion work.
32
- def write_entries(entries, path, io)
32
+ def write_entries(entries, path, zipfile)
33
33
  entries.each do |e|
34
- zip_file_path = path == '' ? e : File.join(path, e)
35
- disk_file_path = File.join(@input_dir, zip_file_path)
36
- puts "Deflating #{disk_file_path}"
34
+ zipfile_path = path == '' ? e : File.join(path, e)
35
+ disk_file_path = File.join(@input_dir, zipfile_path)
37
36
 
38
37
  if File.directory? disk_file_path
39
- recursively_deflate_directory(disk_file_path, io, zip_file_path)
38
+ recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
40
39
  else
41
- put_into_archive(disk_file_path, io, zip_file_path)
40
+ put_into_archive(disk_file_path, zipfile, zipfile_path)
42
41
  end
43
42
  end
44
43
  end
45
44
 
46
- def recursively_deflate_directory(disk_file_path, io, zip_file_path)
47
- io.mkdir zip_file_path
48
- subdir = Dir.entries(disk_file_path) - %w(. ..)
49
- write_entries subdir, zip_file_path, io
45
+ def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
46
+ zipfile.mkdir zipfile_path
47
+ subdir = Dir.entries(disk_file_path) - %w[. ..]
48
+ write_entries subdir, zipfile_path, zipfile
50
49
  end
51
50
 
52
- def put_into_archive(disk_file_path, io, zip_file_path)
53
- io.add(zip_file_path, disk_file_path)
51
+ def put_into_archive(disk_file_path, zipfile, zipfile_path)
52
+ zipfile.add(zipfile_path, disk_file_path)
54
53
  end
55
54
  end
@@ -31,7 +31,7 @@ class MainApp < Gtk::Window
31
31
  sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
32
32
  box.pack_start(sw, true, true, 0)
33
33
 
34
- @clist = Gtk::CList.new(%w(Name Size Compression))
34
+ @clist = Gtk::CList.new(%w[Name Size Compression])
35
35
  @clist.set_selection_mode(Gtk::SELECTION_BROWSE)
36
36
  @clist.set_column_width(0, 120)
37
37
  @clist.set_column_width(1, 120)
@@ -65,7 +65,7 @@ class ZipDialog < ZipDialogUI
65
65
  end
66
66
  puts "selected_items.size = #{selected_items.size}"
67
67
  puts "unselected_items.size = #{unselected_items.size}"
68
- items = selected_items.size > 0 ? selected_items : unselected_items
68
+ items = !selected_items.empty? ? selected_items : unselected_items
69
69
  puts "items.size = #{items.size}"
70
70
 
71
71
  d = Qt::FileDialog.get_existing_directory(nil, self)
@@ -31,7 +31,7 @@ module Zip
31
31
  end
32
32
  end
33
33
 
34
- if __FILE__ == $0
34
+ if $0 == __FILE__
35
35
  module ZipFindConsoleRunner
36
36
  PATH_ARG_INDEX = 0
37
37
  FILENAME_PATTERN_ARG_INDEX = 1
@@ -47,7 +47,7 @@ if __FILE__ == $0
47
47
  end
48
48
 
49
49
  def self.check_args(args)
50
- if (args.size != 3)
50
+ if args.size != 3
51
51
  usage
52
52
  exit
53
53
  end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class ZipCentralDirectoryEntryTest < MiniTest::Test
4
4
  def test_read_from_stream
5
- File.open('test/data/testDirectory.bin', 'rb') do |file|
5
+ File.open('test/data/testDirectory.bin', 'rb') do |file|
6
6
  entry = ::Zip::Entry.read_c_dir_entry(file)
7
7
 
8
8
  assert_equal('longAscii.txt', entry.name)
@@ -0,0 +1,10 @@
1
+ # Based on 'relative2' in https://github.com/jwilk/path-traversal-samples,
2
+ # but create the local `tmp` folder before adding the symlink. Otherwise
3
+ # we may bail out before we get to trying to create the file.
4
+ all: relative1.zip
5
+ relative1.zip:
6
+ rm -f $(@)
7
+ mkdir -p -m 755 tmp/tmp
8
+ umask 022 && echo moo > moo
9
+ cd tmp && zip -X ../$(@) tmp tmp/../../moo
10
+ rm -rf tmp moo
@@ -0,0 +1,5 @@
1
+ # Path Traversal Samples
2
+
3
+ Copied from https://github.com/jwilk/path-traversal-samples on 2018-08-26.
4
+
5
+ License: MIT
@@ -0,0 +1,3 @@
1
+ # Path Traversal Samples
2
+
3
+ Copied from https://github.com/tuzovakaoff/zip_path_traversal on 2018-08-25.
Binary file
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  require 'test_helper'
3
4
 
4
5
  class ErrorsTest < MiniTest::Test
@@ -1,9 +1,8 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class FilePermissionsTest < MiniTest::Test
4
-
5
- ZIPNAME = File.join(File.dirname(__FILE__), "umask.zip")
6
- FILENAME = File.join(File.dirname(__FILE__), "umask.txt")
4
+ ZIPNAME = File.join(File.dirname(__FILE__), 'umask.zip')
5
+ FILENAME = File.join(File.dirname(__FILE__), 'umask.txt')
7
6
 
8
7
  def teardown
9
8
  ::File.unlink(ZIPNAME)
@@ -16,7 +15,7 @@ class FilePermissionsTest < MiniTest::Test
16
15
  end
17
16
 
18
17
  def test_umask_000
19
- set_umask(0000) do
18
+ set_umask(0o000) do
20
19
  create_files
21
20
  end
22
21
 
@@ -24,7 +23,7 @@ class FilePermissionsTest < MiniTest::Test
24
23
  end
25
24
 
26
25
  def test_umask_066
27
- set_umask(0066) do
26
+ set_umask(0o066) do
28
27
  create_files
29
28
  end
30
29
 
@@ -32,7 +31,7 @@ class FilePermissionsTest < MiniTest::Test
32
31
  end
33
32
 
34
33
  def test_umask_027
35
- set_umask(0027) do
34
+ set_umask(0o027) do
36
35
  create_files
37
36
  end
38
37
 
@@ -48,7 +47,7 @@ class FilePermissionsTest < MiniTest::Test
48
47
 
49
48
  def create_files
50
49
  ::Zip::File.open(ZIPNAME, ::Zip::File::CREATE) do |zip|
51
- zip.comment = "test"
50
+ zip.comment = 'test'
52
51
  end
53
52
 
54
53
  ::File.open(FILENAME, 'w') do |file|
@@ -57,13 +56,10 @@ class FilePermissionsTest < MiniTest::Test
57
56
  end
58
57
 
59
58
  # If anything goes wrong, make sure the umask is restored.
60
- def set_umask(umask, &block)
61
- begin
62
- saved_umask = ::File.umask(umask)
63
- yield
64
- ensure
65
- ::File.umask(saved_umask)
66
- end
59
+ def set_umask(umask)
60
+ saved_umask = ::File.umask(umask)
61
+ yield
62
+ ensure
63
+ ::File.umask(saved_umask)
67
64
  end
68
-
69
65
  end