diff --git a/.travis.yml b/.travis.yml index ad59d61e..b197c86b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ matrix: - rvm: ruby-head - rvm: rbx-3 - rvm: jruby-head + - rvm: jruby before_install: - gem update --system - gem install bundler diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 4d8e1751..28d60091 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -147,14 +147,21 @@ def next_header_offset #:nodoc:all end # Extracts entry to file dest_path (defaults to @name). - def extract(dest_path = @name, &block) - block ||= proc { ::Zip.on_exists_proc } - - if @name.squeeze('/') =~ /\.{2}(?:\/|\z)/ + def extract(dest_path = nil, &block) + if dest_path.nil? && Pathname.new(@name).absolute? + puts "WARNING: skipped absolute path in #{@name}" + return self + elsif @name.squeeze('/') =~ /\.{2}(?:\/|\z)/ puts "WARNING: skipped \"../\" path component(s) in #{@name}" return self + elsif symlink? && get_input_stream.read =~ %r{../..} + puts "WARNING: skipped \"#{get_input_stream.read}\" symlink path in #{@name}" + return self end + dest_path ||= @name + block ||= proc { ::Zip.on_exists_proc } + if directory? || file? || symlink? __send__("create_#{@ftype}", dest_path, &block) else diff --git a/test/data/absolutepath.zip b/test/data/absolutepath.zip new file mode 100644 index 00000000..59fceed7 Binary files /dev/null and b/test/data/absolutepath.zip differ diff --git a/test/data/symlink.zip b/test/data/symlink.zip new file mode 100644 index 00000000..e74ee19a Binary files /dev/null and b/test/data/symlink.zip differ diff --git a/test/entry_test.rb b/test/entry_test.rb index b49783d3..eaa9c0d9 100644 --- a/test/entry_test.rb +++ b/test/entry_test.rb @@ -151,4 +151,40 @@ def test_store_file_without_compression assert_match(/mimetypeapplication\/epub\+zip/, first_100_bytes) end + + def test_entry_name_with_absolute_path_does_not_extract + path = '/tmp/file.txt' + File.delete(path) if File.exist?(path) + + Zip::File.open('test/data/absolutepath.zip') do |zip_file| + zip_file.each do |entry| + entry.extract + end + end + + refute File.exist?(path) + end + + def test_entry_name_with_absolute_path_extract_when_given_different_path + path = '/tmp/CVE-2018-1000544' + FileUtils.rm_rf(path) if Dir.exist?(path) + + Zip::File.open('test/data/absolutepath.zip') do |zip_file| + zip_file.each do |entry| + entry.extract("#{path}/#{entry.name}") + end + end + + assert File.exist?("#{path}/tmp/file.txt") + end + + def test_entry_name_with_relative_symlink + assert_raises Errno::ENOENT do + Zip::File.open('test/data/symlink.zip') do |zip_file| + zip_file.each do |entry| + entry.extract + end + end + end + end end