Merge pull request #11101 from chrisroberts/e-checksums

Extend checksum support for box validation and remote files
This commit is contained in:
Chris Roberts 2019-10-08 13:16:33 -07:00 committed by GitHub
commit 0d2751686b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 236 additions and 113 deletions

View File

@ -527,22 +527,11 @@ module Vagrant
end end
def validate_checksum(checksum_type, checksum, path) def validate_checksum(checksum_type, checksum, path)
checksum_klass = case checksum_type.to_sym @logger.info("Validating checksum with #{checksum_type}")
when :md5
Digest::MD5
when :sha1
Digest::SHA1
when :sha256
Digest::SHA2
else
raise Errors::BoxChecksumInvalidType,
type: checksum_type.to_s
end
@logger.info("Validating checksum with #{checksum_klass}")
@logger.info("Expected checksum: #{checksum}") @logger.info("Expected checksum: #{checksum}")
actual = FileChecksum.new(path, checksum_klass).checksum actual = FileChecksum.new(path, checksum_type).checksum
@logger.info("Actual checksum: #{actual}")
if actual.casecmp(checksum) != 0 if actual.casecmp(checksum) != 0
raise Errors::BoxChecksumMismatch, raise Errors::BoxChecksumMismatch,
actual: actual, actual: actual,

View File

@ -1,12 +1,14 @@
require "uri" require "uri"
require "log4r" require "log4r"
require "digest"
require "digest/md5" require "digest/md5"
require "digest/sha1" require "digest/sha1"
require "vagrant/util/busy" require "vagrant/util/busy"
require "vagrant/util/platform" require "vagrant/util/platform"
require "vagrant/util/subprocess" require "vagrant/util/subprocess"
require "vagrant/util/curl_helper" require "vagrant/util/curl_helper"
require "vagrant/util/file_checksum"
module Vagrant module Vagrant
module Util module Util
@ -20,12 +22,6 @@ module Vagrant
# Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0) # Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0)
USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION}) #{ENV['VAGRANT_USER_AGENT_PROVISIONAL_STRING']}".freeze USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION}) #{ENV['VAGRANT_USER_AGENT_PROVISIONAL_STRING']}".freeze
# Supported file checksum
CHECKSUM_MAP = {
:md5 => Digest::MD5,
:sha1 => Digest::SHA1
}.freeze
# Hosts that do not require notification on redirect # Hosts that do not require notification on redirect
SILENCED_HOSTS = [ SILENCED_HOSTS = [
"vagrantcloud.com".freeze, "vagrantcloud.com".freeze,
@ -68,8 +64,11 @@ module Vagrant
@location_trusted = options[:location_trusted] @location_trusted = options[:location_trusted]
@checksums = { @checksums = {
:md5 => options[:md5], :md5 => options[:md5],
:sha1 => options[:sha1] :sha1 => options[:sha1],
} :sha256 => options[:sha256],
:sha384 => options[:sha384],
:sha512 => options[:sha512]
}.compact
end end
# This executes the actual download, downloading the source file # This executes the actual download, downloading the source file
@ -165,36 +164,23 @@ module Vagrant
# @option checksums [String] :sha1 Compare SHA1 checksum # @option checksums [String] :sha1 Compare SHA1 checksum
# @return [Boolean] # @return [Boolean]
def validate_download!(source, path, checksums) def validate_download!(source, path, checksums)
CHECKSUM_MAP.each do |type, klass| checksums.each do |type, expected|
if checksums[type] actual = FileChecksum.new(path, type).checksum
result = checksum_file(klass, path) @logger.debug("Validating checksum (#{type}) for #{source}. " \
@logger.debug("Validating checksum (#{type}) for #{source}. " \ "expected: #{expected} actual: #{actual}")
"expected: #{checksums[type]} actual: #{result}") if actual.casecmp(expected) != 0
if checksums[type] != result raise Errors::DownloaderChecksumError.new(
raise Errors::DownloaderChecksumError.new( source: source,
source: source, path: path,
path: path, type: type,
type: type, expected_checksum: expected,
expected_checksum: checksums[type], actual_checksum: actual
actual_checksum: result )
)
end
end end
end end
true true
end end
# Generate checksum on given file
#
# @param digest_class [Class] Digest class to use for generating checksum
# @param path [String, Pathname] Path to file
# @return [String] hexdigest result
def checksum_file(digest_class, path)
digester = digest_class.new
digester.file(path)
digester.hexdigest
end
def execute_curl(options, subprocess_options, &data_proc) def execute_curl(options, subprocess_options, &data_proc)
options = options.dup options = options.dup
options << subprocess_options options << subprocess_options

View File

@ -10,13 +10,27 @@ end
class FileChecksum class FileChecksum
BUFFER_SIZE = 1024 * 8 BUFFER_SIZE = 1024 * 8
# Supported file checksum
CHECKSUM_MAP = {
:md5 => Digest::MD5,
:sha1 => Digest::SHA1,
:sha256 => Digest::SHA256,
:sha384 => Digest::SHA384,
:sha512 => Digest::SHA512
}.freeze
# Initializes an object to calculate the checksum of a file. The given # Initializes an object to calculate the checksum of a file. The given
# ``digest_klass`` should implement the ``DigestClass`` interface. Note # ``digest_klass`` should implement the ``DigestClass`` interface. Note
# that the built-in Ruby digest classes duck type this properly: # that the built-in Ruby digest classes duck type this properly:
# Digest::MD5, Digest::SHA1, etc. # Digest::MD5, Digest::SHA1, etc.
def initialize(path, digest_klass) def initialize(path, digest_klass)
@digest_klass = digest_klass if digest_klass.is_a?(Class)
@path = path @digest_klass = digest_klass
else
@digest_klass = load_digest(digest_klass)
end
@path = path
end end
# This calculates the checksum of the file and returns it as a # This calculates the checksum of the file and returns it as a
@ -40,6 +54,17 @@ class FileChecksum
end end
end end
return digest.hexdigest digest.hexdigest
end
private
def load_digest(type)
digest = CHECKSUM_MAP[type.to_s.to_sym]
if digest.nil?
raise Errors::BoxChecksumInvalidType,
type: type.to_s
end
digest
end end
end end

View File

@ -19,6 +19,12 @@ module VagrantPlugins
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u options[:username] = u
end end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end end
# Parse the options # Parse the options
@ -52,16 +58,17 @@ module VagrantPlugins
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url) account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token) box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token) cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name, access_token) provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
begin begin
success = provider.create_provider success = provider.create_provider
@env.ui.success(I18n.t("cloud_command.provider.create_success", provider:provider_name, org: org, box_name: box_name, version: version)) @env.ui.success(I18n.t("cloud_command.provider.create_success", provider: provider_name, org: org, box_name: box_name, version: version))
success = success.delete_if{|_, v|v.nil?} success = success.compact
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env) VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0 return 0
rescue VagrantCloud::ClientError => e rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.create_fail", provider:provider_name, org: org, box_name: box_name, version: version)) @env.ui.error(I18n.t("cloud_command.errors.provider.create_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e) @env.ui.error(e)
return 1 return 1
end end

View File

@ -19,6 +19,12 @@ module VagrantPlugins
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u options[:username] = u
end end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end end
# Parse the options # Parse the options
@ -52,7 +58,8 @@ module VagrantPlugins
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url) account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token) box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token) cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name, access_token) provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
begin begin
success = provider.update success = provider.update

View File

@ -43,6 +43,12 @@ module VagrantPlugins
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u options[:username] = u
end end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end end
# Parse the options # Parse the options
@ -97,7 +103,8 @@ module VagrantPlugins
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url) account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, options[:short_description], options[:description], access_token) box = VagrantCloud::Box.new(account, box_name, nil, options[:short_description], options[:description], access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, options[:version_description], access_token) cloud_version = VagrantCloud::Version.new(box, version, nil, options[:version_description], access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, options[:url], org, box_name, access_token) provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, options[:url], org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
ui = Vagrant::UI::Prefixed.new(@env.ui, "cloud") ui = Vagrant::UI::Prefixed.new(@env.ui, "cloud")

View File

@ -7,6 +7,9 @@ module VagrantPlugins
attr_accessor :path attr_accessor :path
attr_accessor :md5 attr_accessor :md5
attr_accessor :sha1 attr_accessor :sha1
attr_accessor :sha256
attr_accessor :sha384
attr_accessor :sha512
attr_accessor :env attr_accessor :env
attr_accessor :upload_path attr_accessor :upload_path
attr_accessor :args attr_accessor :args
@ -26,6 +29,9 @@ module VagrantPlugins
@path = UNSET_VALUE @path = UNSET_VALUE
@md5 = UNSET_VALUE @md5 = UNSET_VALUE
@sha1 = UNSET_VALUE @sha1 = UNSET_VALUE
@sha256 = UNSET_VALUE
@sha384 = UNSET_VALUE
@sha512 = UNSET_VALUE
@env = UNSET_VALUE @env = UNSET_VALUE
@upload_path = UNSET_VALUE @upload_path = UNSET_VALUE
@privileged = UNSET_VALUE @privileged = UNSET_VALUE
@ -45,6 +51,9 @@ module VagrantPlugins
@path = nil if @path == UNSET_VALUE @path = nil if @path == UNSET_VALUE
@md5 = nil if @md5 == UNSET_VALUE @md5 = nil if @md5 == UNSET_VALUE
@sha1 = nil if @sha1 == UNSET_VALUE @sha1 = nil if @sha1 == UNSET_VALUE
@sha256 = nil if @sha256 == UNSET_VALUE
@sha384 = nil if @sha384 == UNSET_VALUE
@sha512 = nil if @sha512 == UNSET_VALUE
@env = {} if @env == UNSET_VALUE @env = {} if @env == UNSET_VALUE
@upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
@privileged = true if @privileged == UNSET_VALUE @privileged = true if @privileged == UNSET_VALUE

View File

@ -253,7 +253,10 @@ module VagrantPlugins
config.path, config.path,
download_path, download_path,
md5: config.md5, md5: config.md5,
sha1: config.sha1 sha1: config.sha1,
sha256: config.sha256,
sha384: config.sha384,
sha512: config.sha512
).download! ).download!
ext = File.extname(config.path) ext = File.extname(config.path)
script = download_path.read script = download_path.read

View File

@ -48,7 +48,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Create do
it "creates a provider" do it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
@ -59,7 +59,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Create do
it "displays an error if encoutering a problem with the request" do it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
allow(provider).to receive(:create_provider). allow(provider).to receive(:create_provider).
@ -73,7 +73,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Create do
it "creates a provider" do it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token). with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)

View File

@ -48,7 +48,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Update do
it "updates a provider" do it "updates a provider" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)
@ -59,7 +59,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Update do
it "displays an error if encoutering a problem with the request" do it "displays an error if encoutering a problem with the request" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
allow(provider).to receive(:update). allow(provider).to receive(:update).
@ -73,7 +73,7 @@ describe VagrantPlugins::CloudCommand::ProviderCommand::Command::Update do
it "creates a provider" do it "creates a provider" do
allow(VagrantCloud::Provider).to receive(:new). allow(VagrantCloud::Provider).to receive(:new).
with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token). with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token, nil, nil, nil).
and_return(provider) and_return(provider)
expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results)

View File

@ -175,8 +175,16 @@ describe "Vagrant::Shell::Provisioner" do
end end
context "with remote script" do context "with remote script" do
let(:filechecksum) { double("filechecksum", checksum: checksum_value) }
let(:checksum_value) { double("checksum_value") }
before do
allow(FileChecksum).to receive(:new).and_return(filechecksum)
allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true)
end
context "that does not have matching sha1 checksum" do context "that does not have matching sha1 checksum" do
let(:checksum_value) { "INVALID_VALUE" }
let(:config) { let(:config) {
double( double(
:config, :config,
@ -188,18 +196,97 @@ describe "Vagrant::Shell::Provisioner" do
:binary => false, :binary => false,
:md5 => nil, :md5 => nil,
:sha1 => 'EXPECTED_VALUE', :sha1 => 'EXPECTED_VALUE',
:sha256 => nil,
:sha384 => nil,
:sha512 => nil,
:reset => false, :reset => false,
:reboot => false :reboot => false
) )
} }
let(:digest){ double("digest") } it "should raise an exception" do
before do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config)
allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true)
allow(digest).to receive(:file).and_return(digest) expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError)
expect(Digest::SHA1).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return('INVALID_VALUE')
end end
end
context "that does not have matching sha256 checksum" do
let(:checksum_value) { "INVALID_VALUE" }
let(:config) {
double(
:config,
:args => "doesn't matter",
:env => {},
:upload_path => "arbitrary",
:remote? => true,
:path => "http://example.com/script.sh",
:binary => false,
:md5 => nil,
:sha1 => nil,
:sha256 => 'EXPECTED_VALUE',
:sha384 => nil,
:sha512 => nil,
:reset => false,
:reboot => false
)
}
it "should raise an exception" do
vsp = VagrantPlugins::Shell::Provisioner.new(machine, config)
expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError)
end
end
context "that does not have matching sha384 checksum" do
let(:checksum_value) { "INVALID_VALUE" }
let(:config) {
double(
:config,
:args => "doesn't matter",
:env => {},
:upload_path => "arbitrary",
:remote? => true,
:path => "http://example.com/script.sh",
:binary => false,
:md5 => nil,
:sha1 => nil,
:sha256 => nil,
:sha384 => 'EXPECTED_VALUE',
:sha512 => nil,
:reset => false,
:reboot => false
)
}
it "should raise an exception" do
vsp = VagrantPlugins::Shell::Provisioner.new(machine, config)
expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError)
end
end
context "that does not have matching sha512 checksum" do
let(:checksum_value) { "INVALID_VALUE" }
let(:config) {
double(
:config,
:args => "doesn't matter",
:env => {},
:upload_path => "arbitrary",
:remote? => true,
:path => "http://example.com/script.sh",
:binary => false,
:md5 => nil,
:sha1 => nil,
:sha256 => nil,
:sha384 => nil,
:sha512 => 'EXPECTED_VALUE',
:reset => false,
:reboot => false
)
}
it "should raise an exception" do it "should raise an exception" do
vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) vsp = VagrantPlugins::Shell::Provisioner.new(machine, config)
@ -209,6 +296,7 @@ describe "Vagrant::Shell::Provisioner" do
end end
context "that does not have matching md5 checksum" do context "that does not have matching md5 checksum" do
let(:checksum_value) { "INVALID_VALUE" }
let(:config) { let(:config) {
double( double(
:config, :config,
@ -220,19 +308,14 @@ describe "Vagrant::Shell::Provisioner" do
:binary => false, :binary => false,
:md5 => 'EXPECTED_VALUE', :md5 => 'EXPECTED_VALUE',
:sha1 => nil, :sha1 => nil,
:sha256 => nil,
:sha384 => nil,
:sha512 => nil,
:reset => false, :reset => false,
:reboot => false :reboot => false
) )
} }
let(:digest){ double("digest") }
before do
allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true)
allow(digest).to receive(:file).and_return(digest)
expect(Digest::MD5).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return('INVALID_VALUE')
end
it "should raise an exception" do it "should raise an exception" do
vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) vsp = VagrantPlugins::Shell::Provisioner.new(machine, config)

View File

@ -175,23 +175,19 @@ describe Vagrant::Util::Downloader do
context "with checksum" do context "with checksum" do
let(:checksum_expected_value){ 'MD5_CHECKSUM_VALUE' } let(:checksum_expected_value){ 'MD5_CHECKSUM_VALUE' }
let(:checksum_invalid_value){ 'INVALID_VALUE' } let(:checksum_invalid_value){ 'INVALID_VALUE' }
let(:digest){ double("digest") } let(:filechecksum) { double("filechecksum", checksum: checksum_value) }
let(:checksum_value) { double("checksum_value") }
before do before { allow(FileChecksum).to receive(:new).with(any_args).and_return(filechecksum) }
allow(digest).to receive(:file).and_return(digest)
end
[Digest::MD5, Digest::SHA1].each do |klass| [Digest::MD5, Digest::SHA1, Digest::SHA256, Digest::SHA384, Digest::SHA512].each do |klass|
short_name = klass.to_s.split("::").last.downcase short_name = klass.to_s.split("::").last.downcase
context "using #{short_name} digest" do context "using #{short_name} digest" do
subject { described_class.new(source, destination, short_name.to_sym => checksum_expected_value) } subject { described_class.new(source, destination, short_name.to_sym => checksum_expected_value) }
context "that matches expected value" do context "that matches expected value" do
before do let(:checksum_value) { checksum_expected_value }
expect(klass).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return(checksum_expected_value)
end
it "should not raise an exception" do it "should not raise an exception" do
expect(subject.download!).to be(true) expect(subject.download!).to be(true)
@ -199,10 +195,7 @@ describe Vagrant::Util::Downloader do
end end
context "that does not match expected value" do context "that does not match expected value" do
before do let(:checksum_value) { checksum_invalid_value }
expect(klass).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return(checksum_invalid_value)
end
it "should raise an exception" do it "should raise an exception" do
expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError)
@ -213,13 +206,9 @@ describe Vagrant::Util::Downloader do
context "using both md5 and sha1 digests" do context "using both md5 and sha1 digests" do
context "that both match expected values" do context "that both match expected values" do
subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } let(:checksum_value) { checksum_expected_value }
before do subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) }
expect(Digest::MD5).to receive(:new).and_return(digest)
expect(Digest::SHA1).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).exactly(2).times
end
it "should not raise an exception" do it "should not raise an exception" do
expect(subject.download!).to be(true) expect(subject.download!).to be(true)
@ -227,12 +216,14 @@ describe Vagrant::Util::Downloader do
end end
context "that only sha1 matches expected value" do context "that only sha1 matches expected value" do
subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_expected_value) } subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) }
let(:valid_checksum) { double("valid_checksum", checksum: checksum_expected_value) }
let(:invalid_checksum) { double("invalid_checksum", checksum: checksum_invalid_value) }
before do before do
allow(Digest::MD5).to receive(:new).and_return(digest) allow(FileChecksum).to receive(:new).with(anything, :sha1).and_return(valid_checksum)
allow(Digest::SHA1).to receive(:new).and_return(digest) allow(FileChecksum).to receive(:new).with(anything, :md5).and_return(invalid_checksum)
expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once)
end end
it "should raise an exception" do it "should raise an exception" do
@ -241,12 +232,14 @@ describe Vagrant::Util::Downloader do
end end
context "that only md5 matches expected value" do context "that only md5 matches expected value" do
subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_invalid_value) } subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) }
let(:valid_checksum) { double("valid_checksum", checksum: checksum_expected_value) }
let(:invalid_checksum) { double("invalid_checksum", checksum: checksum_invalid_value) }
before do before do
allow(Digest::MD5).to receive(:new).and_return(digest) allow(FileChecksum).to receive(:new).with(anything, :md5).and_return(valid_checksum)
allow(Digest::SHA1).to receive(:new).and_return(digest) allow(FileChecksum).to receive(:new).with(anything, :sha1).and_return(invalid_checksum)
expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once)
end end
it "should raise an exception" do it "should raise an exception" do
@ -255,14 +248,9 @@ describe Vagrant::Util::Downloader do
end end
context "that none match expected value" do context "that none match expected value" do
let(:checksum_value) { checksum_expected_value }
subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_invalid_value) } subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_invalid_value) }
before do
allow(Digest::MD5).to receive(:new).and_return(digest)
allow(Digest::SHA1).to receive(:new).and_return(digest)
expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once)
end
it "should raise an exception" do it "should raise an exception" do
expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError)
end end

View File

@ -20,4 +20,16 @@ describe FileChecksum do
instance = described_class.new(file, Digest::SHA1) instance = described_class.new(file, Digest::SHA1)
expect(instance.checksum).to eq("264b207c7913e461c43d0f63d2512f4017af4755") expect(instance.checksum).to eq("264b207c7913e461c43d0f63d2512f4017af4755")
end end
it "should support initialize with class or string" do
file = environment.workdir.join("file")
file.open("w+") { |f| f.write("HELLO!") }
%w(md5 sha1 sha256 sha384 sha512).each do |type|
klass = Digest.const_get(type.upcase)
t_i = described_class.new(file, type)
k_i = described_class.new(file, klass)
expect(t_i.checksum).to eq(k_i.checksum)
end
end
end end

View File

@ -34,7 +34,7 @@ Gem::Specification.new do |s|
s.add_dependency "winrm", "~> 2.1" s.add_dependency "winrm", "~> 2.1"
s.add_dependency "winrm-fs", "~> 1.0" s.add_dependency "winrm-fs", "~> 1.0"
s.add_dependency "winrm-elevated", "~> 1.1" s.add_dependency "winrm-elevated", "~> 1.1"
s.add_dependency "vagrant_cloud", "~> 2.0.2" s.add_dependency "vagrant_cloud", "~> 2.0.3"
# NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions # NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions
# of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5). # of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5).

View File

@ -91,7 +91,8 @@ you are not using a catalog).
included within the catalog entry. included within the catalog entry.
* `--checksum-type TYPE` - The type of checksum that `--checksum` is if it * `--checksum-type TYPE` - The type of checksum that `--checksum` is if it
is specified. Supported values are currently "md5", "sha1", and "sha256". is specified. Supported values are currently "md5", "sha1", "sha256",
"sha384", and "sha512".
* `--name VALUE` - Logical name for the box. This is the value that you * `--name VALUE` - Logical name for the box. This is the value that you
would put into `config.vm.box` in your Vagrantfile. When adding a box from would put into `config.vm.box` in your Vagrantfile. When adding a box from

View File

@ -83,6 +83,12 @@ The remainder of the available options are optional:
* `sha1` (string) - SHA1 checksum used to validate remotely downloaded shell files. * `sha1` (string) - SHA1 checksum used to validate remotely downloaded shell files.
* `sha256` (string) - SHA256 checksum used to validate remotely downloaded shell files.
* `sha384` (string) - SHA384 checksum used to validate remotely downloaded shell files.
* `sha512` (string) - SHA512 checksum used to validate remotely downloaded shell files.
* `sensitive` (boolean) - Marks the Hash values used in the `env` option as sensitive * `sensitive` (boolean) - Marks the Hash values used in the `env` option as sensitive
and hides them from output. By default this is "false". and hides them from output. By default this is "false".

View File

@ -46,7 +46,7 @@ when Vagrant must download the box. If this is specified, then
* `config.vm.box_download_checksum_type` (string) - The type of checksum specified * `config.vm.box_download_checksum_type` (string) - The type of checksum specified
by `config.vm.box_download_checksum` (if any). Supported values are by `config.vm.box_download_checksum` (if any). Supported values are
currently "md5", "sha1", and "sha256". currently "md5", "sha1", "sha256", "sha384", and "sha512".
* `config.vm.box_download_client_cert` (string) - Path to a client certificate to * `config.vm.box_download_client_cert` (string) - Path to a client certificate to
use when downloading the box, if it is necessary. By default, no client use when downloading the box, if it is necessary. By default, no client