From a055978d7402eafe90fd6c26ee68a8e7282594b5 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Thu, 12 Jan 2017 10:22:30 -0800 Subject: [PATCH] Scrub credentials from box URLs --- lib/vagrant/action/builtin/box_add.rb | 20 ++++++++----- lib/vagrant/util.rb | 1 + lib/vagrant/util/credential_scrubber.rb | 29 +++++++++++++++++++ .../vagrant/action/builtin/box_add_test.rb | 27 +++++++++++++++++ 4 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 lib/vagrant/util/credential_scrubber.rb diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb index ff796d82d..44dc0654a 100644 --- a/lib/vagrant/action/builtin/box_add.rb +++ b/lib/vagrant/action/builtin/box_add.rb @@ -168,12 +168,15 @@ module Vagrant url = url[0] end + display_original_url = Util::CredentialScrubber.scrub(Array(original_url).first) + display_url = Util::CredentialScrubber.scrub(url) + env[:ui].output(I18n.t( "vagrant.box_loading_metadata", - name: Array(original_url).first)) + name: display_original_url)) if original_url != url env[:ui].detail(I18n.t( - "vagrant.box_expanding_url", url: url)) + "vagrant.box_expanding_url", url: display_url)) end metadata = nil @@ -189,8 +192,8 @@ module Vagrant raise if !expanded raise Errors::BoxAddShortNotFound, error: e.extra_data[:message], - name: original_url, - url: url + name: display_original_url, + url: display_url ensure metadata_path.delete if metadata_path && metadata_path.file? end @@ -208,12 +211,12 @@ module Vagrant raise Errors::BoxAddNoMatchingProvider, name: metadata.name, requested: provider, - url: url + url: display_url else raise Errors::BoxAddNoMatchingVersion, constraints: version || ">= 0", name: metadata.name, - url: url, + url: display_url, versions: metadata.versions.join(", ") end end @@ -265,7 +268,7 @@ module Vagrant raise "Bad box authentication hook, did not generate proper results." end provider_url = authed_urls[0] - end + end box_add( [[provider_url, metadata_provider.url]], @@ -429,6 +432,7 @@ module Vagrant if opts[:ui] show_url = opts[:show_url] show_url ||= url + display_url = Util::CredentialScrubber.scrub(show_url) translation = "vagrant.box_downloading" @@ -439,7 +443,7 @@ module Vagrant env[:ui].detail(I18n.t( translation, - url: show_url)) + url: display_url)) if File.file?(d.destination) env[:ui].info(I18n.t("vagrant.actions.box.download.resuming")) end diff --git a/lib/vagrant/util.rb b/lib/vagrant/util.rb index 07f3b1802..9f86db5a1 100644 --- a/lib/vagrant/util.rb +++ b/lib/vagrant/util.rb @@ -2,6 +2,7 @@ module Vagrant module Util autoload :Busy, 'vagrant/util/busy' autoload :Counter, 'vagrant/util/counter' + autoload :CredentialScrubber, 'vagrant/util/credential_scrubber' autoload :Env, 'vagrant/util/env' autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access' autoload :Platform, 'vagrant/util/platform' diff --git a/lib/vagrant/util/credential_scrubber.rb b/lib/vagrant/util/credential_scrubber.rb new file mode 100644 index 000000000..f9498ddf4 --- /dev/null +++ b/lib/vagrant/util/credential_scrubber.rb @@ -0,0 +1,29 @@ +module Vagrant + module Util + # Utility class to remove credential information from strings + class CredentialScrubber + # String used to replace credential information + REPLACEMENT_TEXT = "*****".freeze + + # Attempt to remove detected credentials from string + # + # @param [String] string + # @return [String] + def self.scrub(string) + string = url_scrubber(string) + end + + # Detect URLs and remove any embedded credentials + # + # @param [String] string + # @return [String] + def self.url_scrubber(string) + string.gsub(%r{(ftp|https?)://[^\s]+@[^\s]+}) do |address| + uri = URI.parse(address) + uri.user = uri.password = REPLACEMENT_TEXT + uri.to_s + end + end + end + end +end diff --git a/test/unit/vagrant/action/builtin/box_add_test.rb b/test/unit/vagrant/action/builtin/box_add_test.rb index c1409a558..a1f966d4b 100644 --- a/test/unit/vagrant/action/builtin/box_add_test.rb +++ b/test/unit/vagrant/action/builtin/box_add_test.rb @@ -279,6 +279,33 @@ describe Vagrant::Action::Builtin::BoxAdd, :skip_windows do subject.call(env) end + + context "with URL containing credentials" do + let(:username){ "box-username" } + let(:password){ "box-password" } + + it "scrubs credentials in output" do + box_path = iso_env.box2_file(:virtualbox) + with_web_server(box_path) do |port| + env[:box_name] = "foo" + env[:box_url] = "http://#{username}:#{password}@127.0.0.1:#{port}/#{box_path.basename}" + + expect(box_collection).to receive(:add).with { |path, name, version, **opts| + expect(checksum(path)).to eq(checksum(box_path)) + expect(name).to eq("foo") + expect(version).to eq("0") + expect(opts[:metadata_url]).to be_nil + true + }.and_return(box) + + allow(env[:ui]).to receive(:detail) + expect(env[:ui]).to receive(:detail).with(%r{.*http://(?!#{username}).+?:(?!#{password}).+?@127\.0\.0\.1:#{port}/#{box_path.basename}.*}) + expect(app).to receive(:call).with(env) + + subject.call(env) + end + end + end end context "with box metadata" do