1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/rubygems/test_gem_package.rb

1180 lines
29 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require_relative 'package/tar_test_case'
require 'digest'
class TestGemPackage < Gem::Package::TarTestCase
def setup
super
@spec = quick_gem 'a' do |s|
s.description = 'π'
s.files = %w[lib/code.rb]
end
util_build_gem @spec
@gem = @spec.cache_file
@destination = File.join @tempdir, 'extract'
FileUtils.mkdir_p @destination
end
def test_class_new_old_format
pend "jruby can't require the simple_gem file" if Gem.java_platform?
require_relative "simple_gem"
File.open 'old_format.gem', 'wb' do |io|
io.write SIMPLE_GEM
end
package = Gem::Package.new 'old_format.gem'
assert package.spec
end
def test_add_checksums
gem_io = StringIO.new
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.date = Time.at 0
spec.rubygems_version = Gem::Version.new '0'
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build_time = 1 # 0 uses current time
package.setup_signer
Gem::Package::TarWriter.new gem_io do |gem|
package.add_metadata gem
package.add_contents gem
package.add_checksums gem
end
gem_io.rewind
reader = Gem::Package::TarReader.new gem_io
checksums = nil
tar = nil
reader.each_entry do |entry|
case entry.full_name
when 'checksums.yaml.gz' then
Zlib::GzipReader.wrap entry do |io|
checksums = io.read
end
when 'data.tar.gz' then
tar = entry.read
end
end
s = StringIO.new
package.gzip_to s do |io|
io.write spec.to_yaml
end
metadata_sha256 = Digest::SHA256.hexdigest s.string
metadata_sha512 = Digest::SHA512.hexdigest s.string
expected = {
'SHA512' => {
'metadata.gz' => metadata_sha512,
'data.tar.gz' => Digest::SHA512.hexdigest(tar),
},
'SHA256' => {
'metadata.gz' => metadata_sha256,
'data.tar.gz' => Digest::SHA256.hexdigest(tar),
},
}
assert_equal expected, load_yaml(checksums)
end
def test_build_time_uses_source_date_epoch
epoch = ENV["SOURCE_DATE_EPOCH"]
ENV["SOURCE_DATE_EPOCH"] = "123456789"
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.date = Time.at 0
spec.rubygems_version = Gem::Version.new '0'
package = Gem::Package.new spec.file_name
assert_equal Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc, package.build_time
ensure
ENV["SOURCE_DATE_EPOCH"] = epoch
end
def test_build_time_without_source_date_epoch
epoch = ENV["SOURCE_DATE_EPOCH"]
ENV["SOURCE_DATE_EPOCH"] = nil
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.rubygems_version = Gem::Version.new '0'
package = Gem::Package.new spec.file_name
assert_kind_of Time, package.build_time
build_time = package.build_time.to_i
assert_equal Gem.source_date_epoch.to_i, build_time
ensure
ENV["SOURCE_DATE_EPOCH"] = epoch
end
def test_add_files
spec = Gem::Specification.new
spec.files = %w[lib/code.rb lib/empty]
FileUtils.mkdir_p 'lib/empty'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
File.open 'lib/extra.rb', 'w' do |io|
io.write '# lib/extra.rb'
end
package = Gem::Package.new 'bogus.gem'
package.spec = spec
tar = util_tar do |tar_io|
package.add_files tar_io
end
tar.rewind
files = []
Gem::Package::TarReader.new tar do |tar_io|
tar_io.each_entry do |entry|
files << entry.full_name
end
end
assert_equal %w[lib/code.rb], files
end
def test_add_files_symlink
spec = Gem::Specification.new
spec.files = %w[lib/code.rb lib/code_sym.rb lib/code_sym2.rb]
FileUtils.mkdir_p 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
# NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb
begin
File.symlink('code.rb', 'lib/code_sym.rb')
File.symlink('../lib/code.rb', 'lib/code_sym2.rb')
rescue Errno::EACCES => e
if win_platform?
pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
package = Gem::Package.new 'bogus.gem'
package.spec = spec
tar = util_tar do |tar_io|
package.add_files tar_io
end
tar.rewind
files, symlinks = [], []
Gem::Package::TarReader.new tar do |tar_io|
tar_io.each_entry do |entry|
if entry.symlink?
symlinks << { entry.full_name => entry.header.linkname }
else
files << entry.full_name
end
end
end
assert_equal %w[lib/code.rb], files
2021-04-14 23:47:18 -04:00
assert_equal [{'lib/code_sym.rb' => 'code.rb'}, {'lib/code_sym2.rb' => '../lib/code.rb'}], symlinks
end
def test_build
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.rubygems_version = :junk
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build
assert_equal Gem::VERSION, spec.rubygems_version
assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert_equal spec, reader.spec
assert_equal %w[metadata.gz data.tar.gz checksums.yaml.gz],
reader.files
assert_equal %w[lib/code.rb], reader.contents
end
def test_build_auto_signed
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
Gem::Security.write PRIVATE_KEY, private_key_path
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
FileUtils.cp PUBLIC_CERT_PATH, public_cert_path
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build
assert_equal Gem::VERSION, spec.rubygems_version
assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
assert_equal [PUBLIC_CERT.to_pem], reader.spec.cert_chain
assert_equal %w[metadata.gz metadata.gz.sig
data.tar.gz data.tar.gz.sig
checksums.yaml.gz checksums.yaml.gz.sig],
reader.files
assert_equal %w[lib/code.rb], reader.contents
end
def test_build_auto_signed_encrypted_key
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
FileUtils.cp ENCRYPTED_PRIVATE_KEY_PATH, private_key_path
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
Gem::Security.write PUBLIC_CERT, public_cert_path
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build
assert_equal Gem::VERSION, spec.rubygems_version
assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
assert_equal [PUBLIC_CERT.to_pem], reader.spec.cert_chain
assert_equal %w[metadata.gz metadata.gz.sig
data.tar.gz data.tar.gz.sig
checksums.yaml.gz checksums.yaml.gz.sig],
reader.files
assert_equal %w[lib/code.rb], reader.contents
end
def test_build_invalid
spec = Gem::Specification.new 'build', '1'
package = Gem::Package.new spec.file_name
package.spec = spec
e = assert_raise Gem::InvalidSpecificationException do
package.build
end
assert_equal 'missing value for attribute summary', e.message
end
def test_build_invalid_arguments
spec = Gem::Specification.new 'build', '1'
package = Gem::Package.new spec.file_name
package.spec = spec
e = assert_raise ArgumentError do
package.build true, true
end
assert_equal "skip_validation = true and strict_validation = true are incompatible", e.message
end
def test_build_signed
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.cert_chain = [PUBLIC_CERT.to_pem]
spec.signing_key = PRIVATE_KEY
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build
assert_equal Gem::VERSION, spec.rubygems_version
assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
assert_equal spec, reader.spec
assert_equal %w[metadata.gz metadata.gz.sig
data.tar.gz data.tar.gz.sig
checksums.yaml.gz checksums.yaml.gz.sig],
reader.files
assert_equal %w[lib/code.rb], reader.contents
end
def test_build_signed_encrypted_key
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
spec.files = ['lib/code.rb']
spec.cert_chain = [PUBLIC_CERT.to_pem]
spec.signing_key = ENCRYPTED_PRIVATE_KEY
FileUtils.mkdir 'lib'
File.open 'lib/code.rb', 'w' do |io|
io.write '# lib/code.rb'
end
package = Gem::Package.new spec.file_name
package.spec = spec
package.build
assert_equal Gem::VERSION, spec.rubygems_version
assert_path_exist spec.file_name
reader = Gem::Package.new spec.file_name
assert reader.verify
assert_equal spec, reader.spec
assert_equal %w[metadata.gz metadata.gz.sig
data.tar.gz data.tar.gz.sig
checksums.yaml.gz checksums.yaml.gz.sig],
reader.files
assert_equal %w[lib/code.rb], reader.contents
end
def test_raw_spec
data_tgz = util_tar_gz {}
gem = util_tar do |tar|
tar.add_file 'data.tar.gz', 0644 do |io|
io.write data_tgz.string
end
tar.add_file 'metadata.gz', 0644 do |io|
Zlib::GzipWriter.wrap io do |gzio|
gzio.write @spec.to_yaml
end
end
end
gem_path = "#{@destination}/test.gem"
File.open gem_path, "wb" do |io|
io.write gem.string
end
spec, metadata = Gem::Package.raw_spec(gem_path)
assert_equal @spec, spec
assert_match @spec.to_yaml, metadata.force_encoding("UTF-8")
end
def test_contents
package = Gem::Package.new @gem
assert_equal %w[lib/code.rb], package.contents
end
def test_extract_files
package = Gem::Package.new @gem
package.extract_files @destination
extracted = File.join @destination, 'lib/code.rb'
assert_path_exist extracted
mask = 0100666 & (~File.umask)
assert_equal mask.to_s(8), File.stat(extracted).mode.to_s(8) unless
win_platform?
end
def test_extract_files_empty
data_tgz = util_tar_gz {}
gem = util_tar do |tar|
tar.add_file 'data.tar.gz', 0644 do |io|
io.write data_tgz.string
end
tar.add_file 'metadata.gz', 0644 do |io|
Zlib::GzipWriter.wrap io do |gzio|
gzio.write @spec.to_yaml
end
end
end
File.open 'empty.gem', 'wb' do |io|
io.write gem.string
end
package = Gem::Package.new 'empty.gem'
package.extract_files @destination
assert_path_exist @destination
end
def test_extract_tar_gz_absolute
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.add_file '/absolute.rb', 0644 do |io|
io.write 'hi'
end
end
e = assert_raise Gem::Package::PathError do
package.extract_tar_gz tgz_io, @destination
end
assert_equal("installing into parent path /absolute.rb of " +
"#{@destination} is not allowed", e.message)
end
def test_extract_tar_gz_symlink_relative_path
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.add_file 'relative.rb', 0644 do |io|
io.write 'hi'
end
tar.mkdir 'lib', 0755
tar.add_symlink 'lib/foo.rb', '../relative.rb', 0644
end
begin
package.extract_tar_gz tgz_io, @destination
rescue Errno::EACCES => e
if win_platform?
pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
extracted = File.join @destination, 'lib/foo.rb'
assert_path_exist extracted
assert_equal '../relative.rb',
File.readlink(extracted)
assert_equal 'hi',
File.read(extracted)
end
def test_extract_symlink_parent
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.mkdir 'lib', 0755
tar.add_symlink 'lib/link', '../..', 0644
tar.add_file 'lib/link/outside.txt', 0644 do |io|
io.write 'hi'
end
end
# Extract into a subdirectory of @destination; if this test fails it writes
# a file outside destination_subdir, but we want the file to remain inside
# @destination so it will be cleaned up.
destination_subdir = File.join @destination, 'subdir'
FileUtils.mkdir_p destination_subdir
e = assert_raise(Gem::Package::PathError, Errno::EACCES) do
package.extract_tar_gz tgz_io, destination_subdir
end
if Gem::Package::PathError === e
assert_equal("installing into parent path lib/link/outside.txt of " +
"#{destination_subdir} is not allowed", e.message)
elsif win_platform?
pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
def test_extract_symlink_parent_doesnt_delete_user_dir
package = Gem::Package.new @gem
# Extract into a subdirectory of @destination; if this test fails it writes
# a file outside destination_subdir, but we want the file to remain inside
# @destination so it will be cleaned up.
destination_subdir = File.join @destination, 'subdir'
FileUtils.mkdir_p destination_subdir
destination_user_dir = File.join @destination, 'user'
destination_user_subdir = File.join destination_user_dir, 'dir'
FileUtils.mkdir_p destination_user_subdir
pend "TMPDIR seems too long to add it as symlink into tar" if destination_user_dir.size > 90
test/rubygems/test_gem_package.rb: Skip a test when TMPDIR is too long to suppress the following failure: https://rubyci.org/logs/rubyci.s3.amazonaws.com/solaris11-gcc/ruby-master/log/20200617T130007Z.fail.html.gz https://rubyci.org/logs/rubyci.s3.amazonaws.com/android29-x86_64/ruby-master/log/20200617T131443Z.fail.html.gz ``` 1) Failure: TestGemPackage#test_extract_symlink_parent_doesnt_delete_user_dir [/export/home/chkbuild/chkbuild-gcc/tmp/build/20200617T130007Z/ruby/test/rubygems/test_gem_package.rb:620]: --- expected +++ actual @@ -1 +1 @@ -"installing into parent path /export/home/chkbuild/chkbuild-gcc/tmp/build/20200617T130007Z/ruby/tmp/test_rubygems_15916/extract/user/dir of /export/home/chkbuild/chkbuild-gcc/tmp/build/20200617T130007Z/ruby/tmp/test_rubygems_15916/extract/subdir is not allowed" +"installing into parent path link/dir of /export/home/chkbuild/chkbuild-gcc/tmp/build/20200617T130007Z/ruby/tmp/test_rubygems_15916/extract/subdir is not allowed" ``` These CI environments use very long TMPDIR for some reason. The test case creates a directory in TMPDIR and attempts to add a symbolic link to the path into a tarball. However, tar format limits the maximum length up to 99, so the path is truncated. This truncation makes the path check of `Gem::Package#install_location` pass through, and then the check of `#mkdir_p_safe` raises an error. The error message is slightly different from the expected value, so the test fails. I'm unsure what to do, so I tentatively skip the test when TMPDIR is long. I'll create a ticket into rubygems bug tracker.
2020-06-17 10:28:51 -04:00
tgz_io = util_tar_gz do |tar|
tar.add_symlink 'link', destination_user_dir, 16877
tar.add_symlink 'link/dir', '.', 16877
end
e = assert_raise(Gem::Package::PathError, Errno::EACCES) do
package.extract_tar_gz tgz_io, destination_subdir
end
assert_path_exist destination_user_subdir
if Gem::Package::PathError === e
assert_equal("installing into parent path #{destination_user_subdir} of " +
"#{destination_subdir} is not allowed", e.message)
elsif win_platform?
pend "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
def test_extract_tar_gz_directory
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.mkdir 'lib', 0755
tar.add_file 'lib/foo.rb', 0644 do |io|
io.write 'hi'
end
tar.mkdir 'lib/foo', 0755
end
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, 'lib/foo.rb'
assert_path_exist extracted
extracted = File.join @destination, 'lib/foo'
assert_path_exist extracted
end
def test_extract_tar_gz_dot_slash
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.add_file './dot_slash.rb', 0644 do |io|
io.write 'hi'
end
end
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, 'dot_slash.rb'
assert_path_exist extracted
end
def test_extract_tar_gz_dot_file
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.add_file '.dot_file.rb', 0644 do |io|
io.write 'hi'
end
end
package.extract_tar_gz tgz_io, @destination
extracted = File.join @destination, '.dot_file.rb'
assert_path_exist extracted
end
if Gem.win_platform?
def test_extract_tar_gz_case_insensitive
package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar|
tar.add_file 'foo/file.rb', 0644 do |io|
io.write 'hi'
end
end
package.extract_tar_gz tgz_io, @destination.upcase
extracted = File.join @destination, 'foo/file.rb'
assert_path_exist extracted
end
end
def test_install_location
package = Gem::Package.new @gem
file = 'file.rb'.dup
file.taint if RUBY_VERSION < '2.7'
destination = package.install_location file, @destination
assert_equal File.join(@destination, 'file.rb'), destination
refute destination.tainted? if RUBY_VERSION < '2.7'
end
def test_install_location_absolute
package = Gem::Package.new @gem
e = assert_raise Gem::Package::PathError do
package.install_location '/absolute.rb', @destination
end
assert_equal("installing into parent path /absolute.rb of " +
"#{@destination} is not allowed", e.message)
end
def test_install_location_dots
package = Gem::Package.new @gem
file = 'file.rb'
destination = File.join @destination, 'foo', '..', 'bar'
FileUtils.mkdir_p File.join @destination, 'foo'
FileUtils.mkdir_p File.expand_path destination
destination = package.install_location file, destination
# this test only fails on ruby missing File.realpath
assert_equal File.join(@destination, 'bar', 'file.rb'), destination
end
def test_install_location_extra_slash
package = Gem::Package.new @gem
file = 'foo//file.rb'.dup
file.taint if RUBY_VERSION < '2.7'
destination = @destination.sub '/', '//'
destination = package.install_location file, destination
assert_equal File.join(@destination, 'foo', 'file.rb'), destination
refute destination.tainted? if RUBY_VERSION < '2.7'
end
def test_install_location_relative
package = Gem::Package.new @gem
e = assert_raise Gem::Package::PathError do
package.install_location '../relative.rb', @destination
end
parent = File.expand_path File.join @destination, "../relative.rb"
assert_equal("installing into parent path #{parent} of " +
"#{@destination} is not allowed", e.message)
end
def test_install_location_suffix
package = Gem::Package.new @gem
filename = "../#{File.basename(@destination)}suffix.rb"
e = assert_raise Gem::Package::PathError do
package.install_location filename, @destination
end
parent = File.expand_path File.join @destination, filename
assert_equal("installing into parent path #{parent} of " +
"#{@destination} is not allowed", e.message)
end
def test_load_spec
entry = StringIO.new Gem::Util.gzip @spec.to_yaml
def entry.full_name() 'metadata.gz' end
package = Gem::Package.new 'nonexistent.gem'
spec = package.load_spec entry
assert_equal @spec, spec
end
def test_verify
package = Gem::Package.new @gem
package.verify
assert_equal @spec, package.spec
assert_equal %w[checksums.yaml.gz data.tar.gz metadata.gz],
package.files.sort
end
def test_verify_checksum_bad
data_tgz = util_tar_gz do |tar|
tar.add_file 'lib/code.rb', 0444 do |io|
io.write '# lib/code.rb'
end
end
data_tgz = data_tgz.string
gem = util_tar do |tar|
metadata_gz = Gem::Util.gzip @spec.to_yaml
tar.add_file 'metadata.gz', 0444 do |io|
io.write metadata_gz
end
tar.add_file 'data.tar.gz', 0444 do |io|
io.write data_tgz
end
bogus_checksums = {
'SHA1' => {
'data.tar.gz' => 'bogus',
'metadata.gz' => 'bogus',
},
}
tar.add_file 'checksums.yaml.gz', 0444 do |io|
Zlib::GzipWriter.wrap io do |gz_io|
gz_io.write YAML.dump bogus_checksums
end
end
end
File.open 'mismatch.gem', 'wb' do |io|
io.write gem.string
end
package = Gem::Package.new 'mismatch.gem'
e = assert_raise Gem::Package::FormatError do
package.verify
end
assert_equal 'SHA1 checksum mismatch for data.tar.gz in mismatch.gem',
e.message
end
def test_verify_checksum_missing
data_tgz = util_tar_gz do |tar|
tar.add_file 'lib/code.rb', 0444 do |io|
io.write '# lib/code.rb'
end
end
data_tgz = data_tgz.string
gem = util_tar do |tar|
metadata_gz = Gem::Util.gzip @spec.to_yaml
tar.add_file 'metadata.gz', 0444 do |io|
io.write metadata_gz
end
digest = Digest::SHA1.new
digest << metadata_gz
checksums = {
'SHA1' => {
'metadata.gz' => digest.hexdigest,
},
}
tar.add_file 'checksums.yaml.gz', 0444 do |io|
Zlib::GzipWriter.wrap io do |gz_io|
gz_io.write YAML.dump checksums
end
end
tar.add_file 'data.tar.gz', 0444 do |io|
io.write data_tgz
end
end
File.open 'data_checksum_missing.gem', 'wb' do |io|
io.write gem.string
end
package = Gem::Package.new 'data_checksum_missing.gem'
assert package.verify
end
def test_verify_corrupt
pend "jruby strips the null byte and does not think it's corrupt" if Gem.java_platform?
tf = Tempfile.open 'corrupt' do |io|
data = Gem::Util.gzip 'a' * 10
io.write \
tar_file_header('metadata.gz', "\000x", 0644, data.length, Time.now)
io.write data
io.rewind
package = Gem::Package.new io.path
e = assert_raise Gem::Package::FormatError do
package.verify
end
assert_equal "tar is corrupt, name contains null byte in #{io.path}",
e.message
io
end
tf.close!
end
def test_verify_empty
FileUtils.touch 'empty.gem'
package = Gem::Package.new 'empty.gem'
e = assert_raise Gem::Package::FormatError do
package.verify
end
assert_equal 'package metadata is missing in empty.gem', e.message
end
def test_verify_nonexistent
package = Gem::Package.new 'nonexistent.gem'
e = assert_raise Gem::Package::FormatError do
package.verify
end
assert_match %r{^No such file or directory}, e.message
assert_match %r{nonexistent.gem$}, e.message
end
def test_verify_duplicate_file
FileUtils.mkdir_p 'lib'
FileUtils.touch 'lib/code.rb'
build = Gem::Package.new @gem
build.spec = @spec
build.setup_signer
2021-04-14 23:47:18 -04:00
File.open @gem, 'wb' do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
build.add_metadata gem
build.add_contents gem
gem.add_file_simple 'a.sig', 0444, 0
gem.add_file_simple 'a.sig', 0444, 0
end
end
package = Gem::Package.new @gem
e = assert_raise Gem::Security::Exception do
package.verify
end
assert_equal 'duplicate files in the package: ("a.sig")', e.message
end
def test_verify_security_policy
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
e = assert_raise Gem::Security::Exception do
package.verify
end
assert_equal 'unsigned gems are not allowed by the High Security policy',
e.message
refute package.instance_variable_get(:@spec), '@spec must not be loaded'
assert_empty package.instance_variable_get(:@files), '@files must empty'
end
def test_verify_security_policy_low_security
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
FileUtils.mkdir_p 'lib'
FileUtils.touch 'lib/code.rb'
build = Gem::Package.new @gem
build.spec = @spec
build.build
package = Gem::Package.new @gem
package.security_policy = Gem::Security::LowSecurity
assert package.verify
end
def test_verify_security_policy_checksum_missing
pend 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
build = Gem::Package.new @gem
build.spec = @spec
build.setup_signer
FileUtils.mkdir 'lib'
FileUtils.touch 'lib/code.rb'
File.open @gem, 'wb' do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
build.add_metadata gem
build.add_contents gem
# write bogus data.tar.gz to foil signature
bogus_data = Gem::Util.gzip 'hello'
fake_signer = Class.new do
def digest_name; 'SHA512'; end
def digest_algorithm; Digest(:SHA512).new; end
def key; 'key'; end
def sign(*); 'fake_sig'; end
end
gem.add_file_signed 'data2.tar.gz', 0444, fake_signer.new do |io|
io.write bogus_data
end
# pre rubygems 2.0 gems do not add checksums
end
end
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
e = assert_raise Gem::Security::Exception do
package.verify
end
assert_equal 'invalid signature', e.message
refute package.instance_variable_get(:@spec), '@spec must not be loaded'
assert_empty package.instance_variable_get(:@files), '@files must empty'
end
def test_verify_truncate
File.open 'bad.gem', 'wb' do |io|
io.write File.read(@gem, 1024) # don't care about newlines
end
package = Gem::Package.new 'bad.gem'
e = assert_raise Gem::Package::FormatError do
package.verify
end
assert_equal 'package content (data.tar.gz) is missing in bad.gem',
e.message
end
# end #verify tests
def test_verify_entry
entry = Object.new
def entry.full_name() raise ArgumentError, 'whatever' end
package = Gem::Package.new @gem
_, err = use_ui @ui do
e = nil
out_err = capture_output do
e = assert_raise ArgumentError do
package.verify_entry entry
end
end
assert_equal "whatever", e.message
assert_equal "full_name", e.backtrace_locations.first.label
out_err
end
assert_equal "Exception while verifying #{@gem}\n", err
valid_metadata = ["metadata", "metadata.gz"]
valid_metadata.each do |vm|
$spec_loaded = false
$good_name = vm
entry = Object.new
def entry.full_name() $good_name end
package = Gem::Package.new(@gem)
package.instance_variable_set(:@files, [])
def package.load_spec(entry) $spec_loaded = true end
package.verify_entry(entry)
assert $spec_loaded
end
invalid_metadata = ["metadataxgz", "foobar\nmetadata", "metadata\nfoobar"]
invalid_metadata.each do |vm|
$spec_loaded = false
$bad_name = vm
entry = Object.new
def entry.full_name() $bad_name end
package = Gem::Package.new(@gem)
package.instance_variable_set(:@files, [])
def package.load_spec(entry) $spec_loaded = true end
package.verify_entry(entry)
refute $spec_loaded
end
end
def test_spec
package = Gem::Package.new @gem
assert_equal @spec, package.spec
end
def test_gem_attr
package = Gem::Package.new(@gem)
assert_equal(@gem, package.gem.path)
end
def test_spec_from_io
# This functionality is used by rubygems.org to extract spec data from an
# uploaded gem before it is written to storage.
io = StringIO.new Gem.read_binary @gem
package = Gem::Package.new io
assert_equal @spec, package.spec
end
def test_spec_from_io_raises_gem_error_for_io_not_at_start
io = StringIO.new Gem.read_binary @gem
io.read(1)
assert_raise(Gem::Package::Error) do
Gem::Package.new io
end
end
def test_contents_from_io
io = StringIO.new Gem.read_binary @gem
package = Gem::Package.new io
assert_equal %w[lib/code.rb], package.contents
end
def util_tar
tar_io = StringIO.new
Gem::Package::TarWriter.new tar_io do |tar|
yield tar
end
tar_io.rewind
tar_io
end
def util_tar_gz(&block)
tar_io = util_tar(&block)
tgz_io = StringIO.new
# can't wrap TarWriter because it seeks
Zlib::GzipWriter.wrap tgz_io do |io|
io.write tar_io.string
end
StringIO.new tgz_io.string
end
end