From 3e04650cd97a01f9f70f399e218b4a61fc5a48ca Mon Sep 17 00:00:00 2001 From: mgrobelin Date: Tue, 21 Aug 2018 13:54:09 +0200 Subject: [PATCH 1/3] Replace usage of Ruby's stdlib URI by Addressable::URI "Addressable is a replacement for the URI implementation that is part of Ruby's standard library. It more closely conforms to RFC 3986, RFC 3987, and RFC 6570 (level 4), additionally providing support for IRIs and URI templates.", see https://github.com/sporkmonger/addressable Fixes https://github.com/inspec/train/issues/110 Signed-off-by: Markus Grobelin --- lib/train.rb | 32 ++++++++++++++++---------------- test/unit/train_test.rb | 38 +++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/lib/train.rb b/lib/train.rb index 3b6b52b8..74a0a75c 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -7,7 +7,7 @@ require_relative "train/plugins" require_relative "train/errors" require_relative "train/platforms" -require "uri" +require "addressable/uri" module Train # Create a new transport instance, with the plugin indicated by the @@ -101,7 +101,7 @@ def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metric creds[:path] ||= uri.path creds[:password] ||= if opts[:www_form_encoded_password] && !uri.password.nil? - URI.decode_www_form_component(uri.password) + Addressable::URI.unencode_component(uri.password) else uri.password end @@ -121,24 +121,24 @@ def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metric # Parse a URI. Supports empty URI's with paths, e.g. `mock://` # # @param string [string] URI string, e.g. `schema://domain.com` - # @return [URI::Generic] parsed URI object + # @return [Addressable::URI] parsed URI object def self.parse_uri(string) - URI.parse(string) - rescue URI::InvalidURIError => e + u = Addressable::URI.parse(string) # A use-case we want to catch is parsing empty URIs with a schema # e.g. mock://. To do this, we match it manually and fake the hostname - case string - when %r{^([a-z]+)://$} - string += "dummy" - when /^([a-z]+):$/ - string += "//dummy" - else - raise Train::UserError, e + if u.scheme && (u.host.nil? || u.host.empty?) && u.path.empty? + case string + when %r{^([a-z]+)://$} + string += "dummy" + when /^([a-z]+):$/ + string += "//dummy" + end + u = Addressable::URI.parse(string) + u.host = nil end - - uri = URI.parse(string) - uri.host = nil - uri + u + rescue Addressable::URI::InvalidURIError => e + raise Train::UserError, e end private_class_method :parse_uri diff --git a/test/unit/train_test.rb b/test/unit/train_test.rb index 9df0ad7a..80ee0a4b 100644 --- a/test/unit/train_test.rb +++ b/test/unit/train_test.rb @@ -155,7 +155,7 @@ _(res[:target]).must_equal org[:target] end - it "supports IPv6 URIs" do + it "supports IPv6 URIs (with brackets)" do org = { target: "mock://[abc::def]:123" } res = Train.target_config(org) _(res[:backend]).must_equal "mock" @@ -167,6 +167,18 @@ _(res[:target]).must_equal org[:target] end + it "supports IPv6 URIs (without brackets)" do + org = { target: "mock://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:123" } + res = Train.target_config(org) + _(res[:backend]).must_equal "mock" + _(res[:host]).must_equal "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210" + _(res[:user]).must_be_nil + _(res[:password]).must_be_nil + _(res[:port]).must_equal 123 + _(res[:path]).must_be_nil + _(res[:target]).must_equal org[:target] + end + it "supports empty URIs with schema://" do org = { target: "mock://" } res = Train.target_config(org) @@ -193,16 +205,16 @@ it "supports www-form encoded passwords when the option is set" do raw_password = '+!@#$%^&*()_-\';:"\\|/?.>,<][}{=`~' - encoded_password = URI.encode_www_form_component(raw_password) - orig = { target: "mock://username:#{encoded_password}@1.2.3.4:100", - www_form_encoded_password: true } - result = Train.target_config(orig) - _(result[:backend]).must_equal "mock" - _(result[:host]).must_equal "1.2.3.4" - _(result[:user]).must_equal "username" - _(result[:password]).must_equal raw_password - _(result[:port]).must_equal 100 - _(result[:target]).must_equal orig[:target] + encoded_password = Addressable::URI.normalize_component(raw_password, Addressable::URI::CharacterClasses::UNRESERVED) + org = { target: "mock://username:#{encoded_password}@1.2.3.4:100", + www_form_encoded_password: true } + res = Train.target_config(org) + _(res[:backend]).must_equal "mock" + _(res[:host]).must_equal "1.2.3.4" + _(res[:user]).must_equal "username" + _(res[:password]).must_equal raw_password + _(res[:port]).must_equal 100 + _(res[:target]).must_equal org[:target] end it "ignores www-form-encoded password value when there is no password" do @@ -217,8 +229,8 @@ _(res[:target]).must_equal org[:target] end - it "it raises UserError on invalid URIs" do - org = { target: "mock world" } + it "it raises UserError on invalid URIs (invalid scheme)" do + org = { target: "123://invalid_scheme.example.com/" } _ { Train.target_config(org) }.must_raise Train::UserError end end From 45452e083e5bb3f0756406ccee72d3b2ef0227b2 Mon Sep 17 00:00:00 2001 From: mgrobelin Date: Fri, 24 Aug 2018 09:51:40 +0200 Subject: [PATCH 2/3] Added addressable gem for better URI handling Signed-off-by: Markus Grobelin --- train-core.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/train-core.gemspec b/train-core.gemspec index de46263d..360b01e6 100644 --- a/train-core.gemspec +++ b/train-core.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] + spec.add_dependency "addressable", "~> 2.5" spec.add_dependency "inifile", "~> 3.0" spec.add_dependency "json", ">= 1.8", "< 3.0" spec.add_dependency "mixlib-shellout", ">= 2.0", "< 4.0" From e1aa37bb305d76d4b6544dd2f6d3b8c50472ac0d Mon Sep 17 00:00:00 2001 From: mgrobelin Date: Tue, 19 Mar 2019 14:43:14 +0100 Subject: [PATCH 3/3] fixed linting and removed addressable from Gemfile see https://github.com/inspec/train/pull/339#pullrequestreview-215067182 Signed-off-by: Markus Grobelin --- lib/train.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/train.rb b/lib/train.rb index 74a0a75c..50340c13 100644 --- a/lib/train.rb +++ b/lib/train.rb @@ -128,10 +128,10 @@ def self.parse_uri(string) # e.g. mock://. To do this, we match it manually and fake the hostname if u.scheme && (u.host.nil? || u.host.empty?) && u.path.empty? case string - when %r{^([a-z]+)://$} - string += "dummy" - when /^([a-z]+):$/ - string += "//dummy" + when %r{^([a-z]+)://$} + string += "dummy" + when /^([a-z]+):$/ + string += "//dummy" end u = Addressable::URI.parse(string) u.host = nil