Skip to content

Commit 205802c

Browse files
committed
Avoid using Dir.chdir
1 parent 261bc34 commit 205802c

2 files changed

Lines changed: 32 additions & 19 deletions

File tree

lib/ro_crate/model/directory.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,8 @@ def full_entry_path(relative_path)
7474
end
7575

7676
def list_all_files(source_directory, include_hidden: false)
77-
args = ['**/*']
78-
args << ::File::FNM_DOTMATCH if include_hidden
79-
Dir.chdir(source_directory) { Dir.glob(*args) }.reject do |path|
77+
flags = include_hidden ? ::File::FNM_DOTMATCH : 0
78+
Dir.glob('**/*', flags: flags, base: source_directory).reject do |path|
8079
path == '.' || path == '..' || path.end_with?('/.')
8180
end
8281
end

lib/ro_crate/reader.rb

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
require 'zip/version'
2+
13
module ROCrate
24
##
35
# A class to handle reading of RO-Crates from Zip files or directories.
46
class Reader
7+
LEGACY_EXTRACT = Zip::VERSION.start_with?('2.').freeze
8+
59
##
610
# Reads an RO-Crate from a directory or zip file.
711
#
@@ -43,14 +47,20 @@ def self.unzip_to(source, target)
4347
# @param source [#read] An IO-like object containing a Zip file.
4448
# @param target [String, ::File, Pathname] The target directory where the file should be unzipped.
4549
def self.unzip_io_to(source, target)
46-
Dir.chdir(target) do
47-
Zip::InputStream.open(source) do |input|
48-
while (entry = input.get_next_entry)
49-
unless ::File.exist?(entry.name) || entry.name_is_directory?
50-
FileUtils::mkdir_p(::File.dirname(entry.name))
51-
::File.binwrite(entry.name, input.read)
52-
end
53-
end
50+
target = Pathname(target)
51+
Zip::InputStream.open(source) do |input|
52+
while (entry = input.get_next_entry)
53+
next if entry.name_is_directory?
54+
55+
dest = target.join(entry.name)
56+
57+
# Guard against zip slip attacks, even though Rubyzip should block them.
58+
raise "Unsafe path in zip entry: #{entry.name}" unless dest.to_s.start_with?(::File.realpath(target) + ::File::SEPARATOR)
59+
60+
next if dest.exist?
61+
62+
FileUtils.mkdir_p(dest.dirname)
63+
::File.binwrite(dest, input.read)
5464
end
5565
end
5666
end
@@ -61,14 +71,18 @@ def self.unzip_io_to(source, target)
6171
# @param source [String, ::File, Pathname] The location of the zip file.
6272
# @param target [String, ::File, Pathname] The target directory where the file should be unzipped.
6373
def self.unzip_file_to(source, target)
64-
Dir.chdir(target) do
65-
Zip::File.open(source) do |zipfile|
66-
zipfile.each do |entry|
67-
unless ::File.exist?(entry.name)
68-
FileUtils::mkdir_p(::File.dirname(entry.name))
69-
zipfile.extract(entry, entry.name)
70-
end
71-
end
74+
target = Pathname(target)
75+
Zip::File.open(source) do |zipfile|
76+
zipfile.each do |entry|
77+
dest = target.join(entry.name)
78+
79+
# Guard against zip slip attacks, even though Rubyzip should block them.
80+
raise "Unsafe path in zip entry: #{entry.name}" unless dest.to_s.start_with?(::File.realpath(target) + ::File::SEPARATOR)
81+
82+
next if dest.exist?
83+
84+
FileUtils.mkdir_p(dest.dirname)
85+
LEGACY_EXTRACT ? entry.extract(dest) : entry.extract(entry.name, destination_directory: target)
7286
end
7387
end
7488
end

0 commit comments

Comments
 (0)