Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions lib/rubygems/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -448,13 +448,18 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
File.dirname destination
end

unless directories.include?(mkdir)
FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
directories << mkdir
end
unless directories.include?(mkdir)
FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
directories << mkdir
end

real_mkdir = File.realpath(mkdir)
unless real_mkdir == destination_dir || normalize_path(real_mkdir).start_with?(normalize_path(destination_dir + "/"))
raise Gem::Package::PathError.new(real_mkdir, destination_dir)
end

if entry.file?
File.open(destination, "wb") do |out|
if entry.file?
File.open(destination, "wb") do |out|
copy_stream(tar.io, out, entry.size)
# Flush needs to happen before chmod because there could be data
# in the IO buffer that needs to be written, and that could be
Expand Down
27 changes: 27 additions & 0 deletions test/rubygems/test_gem_package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,33 @@ def test_extract_tar_gz_symlink_directory
File.read(extracted)
end


def test_extract_tar_gz_rejects_preexisting_symlink_escape
omit "Symlinks not supported or not enabled" unless symlink_supported?

package = Gem::Package.new @gem

tgz_io = util_tar_gz do |tar|
tar.add_file "lib/owned.txt", 0o644 do |io|
io.write "poc-content"
end
end

escape_dir = File.join(@tempdir, "escape")
FileUtils.mkdir_p escape_dir

FileUtils.rm_rf File.join(@destination, "lib")
File.symlink escape_dir, File.join(@destination, "lib")

escaped = File.join(escape_dir, "owned.txt")

assert_raise Gem::Package::PathError do
package.extract_tar_gz tgz_io, @destination
end

refute File.exist?(escaped), "must not write outside extraction root via symlink"
end

def test_extract_symlink_into_symlink_dir
omit "Symlinks not supported or not enabled" unless symlink_supported?
package = Gem::Package.new @gem
Expand Down