mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
55549b85f1
syntax error. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@39765 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
634 lines
14 KiB
Ruby
634 lines
14 KiB
Ruby
# coding: UTF-8
|
|
|
|
require 'rubygems/package/tar_test_case'
|
|
require 'rubygems/simple_gem'
|
|
|
|
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
|
|
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'
|
|
|
|
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
|
|
|
|
reader.each_entry do |entry|
|
|
case entry.full_name
|
|
when 'checksums.yaml.gz'
|
|
Zlib::GzipReader.wrap entry do |io|
|
|
checksums = io.read
|
|
end
|
|
end
|
|
end
|
|
|
|
s = StringIO.new
|
|
|
|
package.gzip_to s do |io|
|
|
io.write spec.to_yaml
|
|
end
|
|
|
|
metadata_sha1 = Digest::SHA1.hexdigest s.string
|
|
metadata_sha512 = Digest::SHA512.hexdigest s.string
|
|
|
|
data_digests = nil
|
|
util_tar do |tar|
|
|
data_digests = package.add_contents tar
|
|
end
|
|
|
|
expected = {
|
|
'SHA1' => {
|
|
'metadata.gz' => metadata_sha1,
|
|
'data.tar.gz' => data_digests['SHA1'].hexdigest,
|
|
},
|
|
'SHA512' => {
|
|
'metadata.gz' => metadata_sha512,
|
|
'data.tar.gz' => data_digests['SHA512'].hexdigest,
|
|
}
|
|
}
|
|
|
|
assert_equal expected, YAML.load(checksums)
|
|
end
|
|
|
|
def test_add_files
|
|
spec = Gem::Specification.new
|
|
spec.files = %w[lib/code.rb lib/empty]
|
|
|
|
FileUtils.mkdir_p 'lib/empty'
|
|
|
|
open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
|
|
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_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'
|
|
|
|
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_exists 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
|
|
private_key_path = File.join Gem.user_home, 'gem-private_key.pem'
|
|
Gem::Security.write PRIVATE_KEY, private_key_path
|
|
|
|
public_cert_path = File.join Gem.user_home, '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'
|
|
|
|
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_exists 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_raises Gem::InvalidSpecificationException do
|
|
package.build
|
|
end
|
|
|
|
assert_equal 'missing value for attribute summary', e.message
|
|
end
|
|
|
|
def test_build_signed
|
|
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'
|
|
|
|
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_exists 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_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_exists 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 do end
|
|
|
|
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
|
|
|
|
open 'empty.gem', 'wb' do |io|
|
|
io.write gem.string
|
|
end
|
|
|
|
package = Gem::Package.new 'empty.gem'
|
|
|
|
package.extract_files @destination
|
|
|
|
assert_path_exists @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_raises 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_install_location
|
|
package = Gem::Package.new @gem
|
|
|
|
file = 'file.rb'
|
|
file.taint
|
|
|
|
destination = package.install_location file, @destination
|
|
|
|
assert_equal File.join(@destination, 'file.rb'), destination
|
|
refute destination.tainted?
|
|
end
|
|
|
|
def test_install_location_absolute
|
|
package = Gem::Package.new @gem
|
|
|
|
e = assert_raises 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_extra_slash
|
|
package = Gem::Package.new @gem
|
|
|
|
file = 'foo//file.rb'
|
|
file.taint
|
|
|
|
destination = @destination.sub '/', '//'
|
|
|
|
destination = package.install_location file, destination
|
|
|
|
assert_equal File.join(@destination, 'foo', 'file.rb'), destination
|
|
refute destination.tainted?
|
|
end
|
|
|
|
def test_install_location_relative
|
|
package = Gem::Package.new @gem
|
|
|
|
e = assert_raises 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_load_spec
|
|
entry = StringIO.new Gem.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.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
|
|
|
|
open 'mismatch.gem', 'wb' do |io|
|
|
io.write gem.string
|
|
end
|
|
|
|
package = Gem::Package.new 'mismatch.gem'
|
|
|
|
e = assert_raises 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.gzip @spec.to_yaml
|
|
|
|
tar.add_file 'metadata.gz', 0444 do |io|
|
|
io.write metadata_gz
|
|
end
|
|
|
|
digest = OpenSSL::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
|
|
|
|
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
|
|
Tempfile.open 'corrupt' do |io|
|
|
data = Gem.gzip 'a' * 10
|
|
io.write tar_file_header('metadata.gz', "\000x", 0644, data.length)
|
|
io.write data
|
|
io.rewind
|
|
|
|
package = Gem::Package.new io.path
|
|
|
|
e = assert_raises Gem::Package::FormatError do
|
|
package.verify
|
|
end
|
|
|
|
assert_equal "tar is corrupt, name contains null byte in #{io.path}",
|
|
e.message
|
|
end
|
|
end
|
|
|
|
def test_verify_empty
|
|
FileUtils.touch 'empty.gem'
|
|
|
|
package = Gem::Package.new 'empty.gem'
|
|
|
|
e = assert_raises 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_raises Gem::Package::FormatError do
|
|
package.verify
|
|
end
|
|
|
|
assert_match /\s-\snonexistent\.gem\z/, e.message
|
|
end
|
|
|
|
def test_verify_security_policy
|
|
package = Gem::Package.new @gem
|
|
package.security_policy = Gem::Security::HighSecurity
|
|
|
|
e = assert_raises 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
|
|
@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
|
|
@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'
|
|
|
|
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.gzip 'hello'
|
|
gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length 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_raises 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
|
|
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_raises Gem::Package::FormatError do
|
|
package.verify
|
|
end
|
|
|
|
assert_equal 'package content (data.tar.gz) is missing in bad.gem',
|
|
e.message
|
|
end
|
|
|
|
def test_spec
|
|
package = Gem::Package.new @gem
|
|
|
|
assert_equal @spec, package.spec
|
|
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
|
|
|