Skip to content

Commit f5042b8

Browse files
committed
Raise a security error when there are duplicate files in a package
This is a rudimentary fix for an issue where RubyGems would allow a mis-signed gem to be installed, as the tarball would contain multiple gem signatures. Nothing should give us a tarball with multiple entries, so we'll just disallow that.
1 parent 21872ae commit f5042b8

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

Diff for: lib/rubygems/package.rb

+4
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ def verify_files gem
603603
raise Gem::Package::FormatError.new \
604604
'package content (data.tar.gz) is missing', @gem
605605
end
606+
607+
if duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first) and duplicates.any?
608+
raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})"
609+
end
606610
end
607611

608612
##

Diff for: lib/rubygems/package/tar_writer.rb

+2
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ def add_file_signed name, mode, signer
196196
digest_name == signer.digest_name
197197
end
198198

199+
raise "no #{signer.digest_name} in #{digests.values.compact}" unless signature_digest
200+
199201
if signer.key then
200202
signature = signer.sign signature_digest.digest
201203

Diff for: test/rubygems/test_gem_package.rb

+33-1
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,32 @@ def test_verify_nonexistent
738738
assert_match %r%nonexistent.gem$%, e.message
739739
end
740740

741+
def test_verify_duplicate_file
742+
FileUtils.mkdir_p 'lib'
743+
FileUtils.touch 'lib/code.rb'
744+
745+
build = Gem::Package.new @gem
746+
build.spec = @spec
747+
build.setup_signer
748+
open @gem, 'wb' do |gem_io|
749+
Gem::Package::TarWriter.new gem_io do |gem|
750+
build.add_metadata gem
751+
build.add_contents gem
752+
753+
gem.add_file_simple 'a.sig', 0444, 0
754+
gem.add_file_simple 'a.sig', 0444, 0
755+
end
756+
end
757+
758+
package = Gem::Package.new @gem
759+
760+
e = assert_raises Gem::Security::Exception do
761+
package.verify
762+
end
763+
764+
assert_equal 'duplicate files in the package: ("a.sig")', e.message
765+
end
766+
741767
def test_verify_security_policy
742768
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
743769

@@ -795,7 +821,13 @@ def test_verify_security_policy_checksum_missing
795821

796822
# write bogus data.tar.gz to foil signature
797823
bogus_data = Gem.gzip 'hello'
798-
gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io|
824+
fake_signer = Class.new do
825+
def digest_name; 'SHA512'; end
826+
def digest_algorithm; Digest(:SHA512); end
827+
def key; 'key'; end
828+
def sign(*); 'fake_sig'; end
829+
end
830+
gem.add_file_signed 'data2.tar.gz', 0444, fake_signer.new do |io|
799831
io.write bogus_data
800832
end
801833

0 commit comments

Comments
 (0)