Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions lib/rubygems/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
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
Comment on lines +451 to +459

if entry.file?
File.open(destination, "wb") do |out|
copy_stream(tar.io, out, entry.size)
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


Comment thread
thesmartshadow marked this conversation as resolved.
Outdated
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