Skip to content

Commit 666ef79

Browse files
committed
Implement a safe mkdir for package that verifies were inside the destination dir for all new directories ccreated
1 parent a67d9f6 commit 666ef79

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

Diff for: lib/rubygems/package.rb

+28-5
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
378378
File.dirname destination
379379
end
380380

381-
FileUtils.mkdir_p mkdir, mkdir_options
381+
mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name
382382

383383
File.open destination, 'wb' do |out|
384384
out.write entry.read
@@ -416,13 +416,10 @@ def install_location filename, destination_dir # :nodoc:
416416
raise Gem::Package::PathError.new(filename, destination_dir) if
417417
filename.start_with? '/'
418418

419-
destination_dir = File.realpath destination_dir if
420-
File.respond_to? :realpath
419+
destination_dir = realpath destination_dir
421420
destination_dir = File.expand_path destination_dir
422421

423422
destination = File.join destination_dir, filename
424-
destination = File.realpath destination if
425-
File.respond_to? :realpath
426423
destination = File.expand_path destination
427424

428425
raise Gem::Package::PathError.new(destination, destination_dir) unless
@@ -432,6 +429,22 @@ def install_location filename, destination_dir # :nodoc:
432429
destination
433430
end
434431

432+
def mkdir_p_safe mkdir, mkdir_options, destination_dir, file_name
433+
destination_dir = realpath File.expand_path(destination_dir)
434+
parts = mkdir.split(File::SEPARATOR)
435+
parts.reduce do |path, basename|
436+
path = realpath path unless path == ""
437+
path = File.expand_path(path + File::SEPARATOR + basename)
438+
lstat = File.lstat path rescue nil
439+
if !lstat || !lstat.directory?
440+
unless path.start_with? destination_dir and (FileUtils.mkdir path, mkdir_options rescue false)
441+
raise Gem::Package::PathError.new(file_name, destination_dir)
442+
end
443+
end
444+
path
445+
end
446+
end
447+
435448
##
436449
# Loads a Gem::Specification from the TarEntry +entry+
437450

@@ -622,6 +635,16 @@ def verify_gz entry # :nodoc:
622635
raise Gem::Package::FormatError.new(e.message, entry.full_name)
623636
end
624637

638+
if File.respond_to? :realpath
639+
def realpath file
640+
File.realpath file
641+
end
642+
else
643+
def realpath file
644+
file
645+
end
646+
end
647+
625648
end
626649

627650
require 'rubygems/package/digest_io'

Diff for: test/rubygems/test_gem_package.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def test_extract_symlink_parent
476476
package.extract_tar_gz tgz_io, destination_subdir
477477
end
478478

479-
assert_equal("installing into parent path ../outside.txt of " +
479+
assert_equal("installing into parent path lib/link/outside.txt of " +
480480
"#{destination_subdir} is not allowed", e.message)
481481
end
482482

0 commit comments

Comments
 (0)