Skip to content

Commit 103ca42

Browse files
authored
Merge pull request #9493 from thesmartshadow/fix/cwe59-symlink-extract
Prevent extraction from escaping destination_dir via pre-existing symlinks
2 parents 868317b + 6415f5e commit 103ca42

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

lib/rubygems/package.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,11 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
453453
directories << mkdir
454454
end
455455

456+
real_mkdir = File.realpath(mkdir)
457+
unless real_mkdir == destination_dir || normalize_path(real_mkdir).start_with?(normalize_path(destination_dir + "/"))
458+
raise Gem::Package::PathError.new(real_mkdir, destination_dir)
459+
end
460+
456461
if entry.file?
457462
File.open(destination, "wb") do |out|
458463
copy_stream(tar.io, out, entry.size)

test/rubygems/test_gem_package.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,32 @@ def test_extract_tar_gz_symlink_directory
615615
File.read(extracted)
616616
end
617617

618+
def test_extract_tar_gz_rejects_preexisting_symlink_escape
619+
omit "Symlinks not supported or not enabled" unless symlink_supported?
620+
621+
package = Gem::Package.new @gem
622+
623+
tgz_io = util_tar_gz do |tar|
624+
tar.add_file "lib/owned.txt", 0o644 do |io|
625+
io.write "poc-content"
626+
end
627+
end
628+
629+
escape_dir = File.join(@tempdir, "escape")
630+
FileUtils.mkdir_p escape_dir
631+
632+
FileUtils.rm_rf File.join(@destination, "lib")
633+
File.symlink escape_dir, File.join(@destination, "lib")
634+
635+
escaped = File.join(escape_dir, "owned.txt")
636+
637+
assert_raise Gem::Package::PathError do
638+
package.extract_tar_gz tgz_io, @destination
639+
end
640+
641+
refute File.exist?(escaped), "must not write outside extraction root via symlink"
642+
end
643+
618644
def test_extract_symlink_into_symlink_dir
619645
omit "Symlinks not supported or not enabled" unless symlink_supported?
620646
package = Gem::Package.new @gem

0 commit comments

Comments
 (0)