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.
- checksums.yaml +4 -4
- data/README.md +24 -17
- data/lib/zip.rb +9 -1
- data/lib/zip/central_directory.rb +3 -3
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +3 -3
- data/lib/zip/crypto/null_encryption.rb +2 -4
- data/lib/zip/decompressor.rb +1 -1
- data/lib/zip/dos_time.rb +1 -1
- data/lib/zip/entry.rb +46 -49
- data/lib/zip/entry_set.rb +3 -3
- data/lib/zip/extra_field.rb +1 -1
- data/lib/zip/extra_field/generic.rb +1 -1
- data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
- data/lib/zip/file.rb +12 -12
- data/lib/zip/filesystem.rb +17 -13
- data/lib/zip/inflater.rb +1 -1
- data/lib/zip/input_stream.rb +10 -7
- data/lib/zip/ioextras/abstract_input_stream.rb +1 -1
- data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
- data/lib/zip/output_stream.rb +5 -5
- data/lib/zip/pass_thru_decompressor.rb +1 -1
- data/lib/zip/streamable_stream.rb +1 -1
- data/lib/zip/version.rb +1 -1
- data/samples/example_recursive.rb +14 -15
- data/samples/gtk_ruby_zip.rb +1 -1
- data/samples/qtzip.rb +1 -1
- data/samples/zipfind.rb +2 -2
- data/test/central_directory_entry_test.rb +1 -1
- data/test/data/gpbit3stored.zip +0 -0
- data/test/data/path_traversal/Makefile +10 -0
- data/test/data/path_traversal/jwilk/README.md +5 -0
- data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
- data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
- data/test/data/path_traversal/jwilk/relative0.zip +0 -0
- data/test/data/path_traversal/jwilk/relative2.zip +0 -0
- data/test/data/path_traversal/jwilk/symlink.zip +0 -0
- data/test/data/path_traversal/relative1.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/README.md +3 -0
- data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/errors_test.rb +1 -0
- data/test/file_permissions_test.rb +11 -15
- data/test/file_test.rb +27 -9
- data/test/filesystem/dir_iterator_test.rb +1 -1
- data/test/filesystem/directory_test.rb +29 -11
- data/test/filesystem/file_mutating_test.rb +3 -4
- data/test/filesystem/file_nonmutating_test.rb +31 -31
- data/test/filesystem/file_stat_test.rb +4 -4
- data/test/gentestfiles.rb +13 -13
- data/test/input_stream_test.rb +6 -6
- data/test/ioextras/abstract_output_stream_test.rb +2 -2
- data/test/path_traversal_test.rb +134 -0
- data/test/test_helper.rb +2 -2
- data/test/unicode_file_names_and_comments_test.rb +12 -0
- data/test/zip64_full_test.rb +2 -2
- metadata +95 -49
data/lib/zip/file.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
78
|
+
elsif @create
|
80
79
|
@entry_set = EntrySet.new
|
81
|
-
|
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
|
-
|
155
|
-
when MIN_SEGMENT_SIZE > segment_size
|
153
|
+
if MIN_SEGMENT_SIZE > segment_size
|
156
154
|
MIN_SEGMENT_SIZE
|
157
|
-
|
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
|
166
|
-
|
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,
|
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
|
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 =
|
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?('/')
|
data/lib/zip/filesystem.rb
CHANGED
@@ -142,9 +142,9 @@ module Zip
|
|
142
142
|
|
143
143
|
def ftype
|
144
144
|
if file?
|
145
|
-
|
145
|
+
'file'
|
146
146
|
elsif directory?
|
147
|
-
|
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,
|
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,
|
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,
|
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,
|
216
|
+
unix_mode_cmp(fileName, 0o4000)
|
217
217
|
end
|
218
218
|
|
219
219
|
def setgid?(fileName)
|
220
|
-
unix_mode_cmp(fileName,
|
220
|
+
unix_mode_cmp(fileName, 0o2000)
|
221
221
|
end
|
222
222
|
|
223
223
|
def sticky?(fileName)
|
224
|
-
unix_mode_cmp(fileName,
|
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 =
|
241
|
-
openMode.
|
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
|
-
|
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 =
|
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 =
|
593
|
+
def mkdir(fileName, permissionInt = 0o755)
|
590
594
|
@zipFile.mkdir(expand_to_entry(fileName), permissionInt)
|
591
595
|
end
|
592
596
|
|
data/lib/zip/inflater.rb
CHANGED
data/lib/zip/input_stream.rb
CHANGED
@@ -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 && !@
|
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
|
137
|
+
@decompressor = get_decompressor
|
138
138
|
flush
|
139
139
|
@current_entry
|
140
140
|
end
|
141
141
|
|
142
142
|
def get_decompressor
|
143
|
-
|
144
|
-
when @current_entry.nil?
|
143
|
+
if @current_entry.nil?
|
145
144
|
::Zip::NullDecompressor
|
146
|
-
|
147
|
-
|
148
|
-
|
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)
|
data/lib/zip/output_stream.rb
CHANGED
@@ -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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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)
|
data/lib/zip/version.rb
CHANGED
@@ -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 |
|
25
|
-
write_entries entries, '',
|
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,
|
32
|
+
def write_entries(entries, path, zipfile)
|
33
33
|
entries.each do |e|
|
34
|
-
|
35
|
-
disk_file_path = File.join(@input_dir,
|
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,
|
38
|
+
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
40
39
|
else
|
41
|
-
put_into_archive(disk_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,
|
47
|
-
|
48
|
-
subdir = Dir.entries(disk_file_path) - %w
|
49
|
-
write_entries subdir,
|
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,
|
53
|
-
|
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
|
data/samples/gtk_ruby_zip.rb
CHANGED
@@ -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
|
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)
|
data/samples/qtzip.rb
CHANGED
@@ -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.
|
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)
|
data/samples/zipfind.rb
CHANGED
@@ -31,7 +31,7 @@ module Zip
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
if
|
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
|
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
|
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)
|
Binary file
|
@@ -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
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/data/rubycode.zip
CHANGED
Binary file
|
data/test/errors_test.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class FilePermissionsTest < MiniTest::Test
|
4
|
-
|
5
|
-
|
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(
|
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(
|
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(
|
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 =
|
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
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|