diff --git a/cookiejar.gemspec b/cookiejar.gemspec index 1c58a00..7413964 100644 --- a/cookiejar.gemspec +++ b/cookiejar.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |s| s.rdoc_options = ['--title', 'CookieJar -- Client-side HTTP Cookies'] s.require_paths = ['lib'] + s.add_dependency 'public_suffix', '~> 4.0' s.add_development_dependency 'rake', '>= 10.0', '< 13' s.add_development_dependency 'rspec-collection_matchers', '~> 1.0' s.add_development_dependency 'rspec', '~> 3.0' diff --git a/lib/cookiejar/cookie_validation.rb b/lib/cookiejar/cookie_validation.rb index a64c607..c7372aa 100644 --- a/lib/cookiejar/cookie_validation.rb +++ b/lib/cookiejar/cookie_validation.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'cgi' require 'uri' +require 'public_suffix' module CookieJar # Represents a set of cookie validation errors @@ -107,7 +108,7 @@ def self.hostname_reach(hostname) host = to_domain hostname host = host.downcase match = BASE_HOSTNAME.match host - match[1] if match + match[1] if match && (PublicSuffix.valid?(match[1]) || match[1] == 'local') end # Compute the base of a path, for default cookie path assignment @@ -161,10 +162,11 @@ def self.compute_search_domains(request_uri) def self.compute_search_domains_for_host(host) host = effective_host host result = [host] - unless host =~ IPADDR + return result if host =~ IPADDR + loop do result << ".#{host}" - base = hostname_reach host - result << ".#{base}" if base + host = hostname_reach(host) + break unless host end result end diff --git a/spec/cookie_validation_spec.rb b/spec/cookie_validation_spec.rb index e4cae54..424ed0a 100644 --- a/spec/cookie_validation_spec.rb +++ b/spec/cookie_validation_spec.rb @@ -36,11 +36,6 @@ Cookie.from_set_cookie 'http://www.foo.com/', 'foo=bar;domain=bar.com' end).to raise_error InvalidCookieError end - it 'should fail for domains more than one level up' do - expect(lambda do - Cookie.from_set_cookie 'http://x.y.z.com/', 'foo=bar;domain=z.com' - end).to raise_error InvalidCookieError - end it 'should fail for setting subdomain cookies' do expect(lambda do Cookie.from_set_cookie 'http://foo.com/', 'foo=bar;domain=auth.foo.com' @@ -111,7 +106,7 @@ describe '#compute_search_domains' do it 'should handle subdomains' do expect(CookieValidation.compute_search_domains('http://www.auth.foo.com/')).to eq( - ['www.auth.foo.com', '.www.auth.foo.com', '.auth.foo.com']) + ['www.auth.foo.com', '.www.auth.foo.com', '.auth.foo.com', '.foo.com']) end it 'should handle root domains' do expect(CookieValidation.compute_search_domains('http://foo.com/')).to eq( @@ -129,6 +124,10 @@ expect(CookieValidation.compute_search_domains('http://zero/')).to eq( ['zero.local', '.zero.local', '.local']) end + it 'should handle multi-part tlds' do + expect(CookieValidation.compute_search_domains('http://foo.co.nz/')).to eq( + ['foo.co.nz', '.foo.co.nz']) + end end describe '#determine_cookie_domain' do it 'should add a dot to the front of domains' do @@ -190,9 +189,10 @@ it 'should handle matching a superdomain' do expect(CookieValidation.domains_match('.foo.com', 'auth.foo.com')).to eq '.foo.com' expect(CookieValidation.domains_match('.y.z.foo.com', 'x.y.z.foo.com')).to eq '.y.z.foo.com' + expect(CookieValidation.domains_match('.z.foo.com', 'x.y.z.foo.com')).to eq '.z.foo.com' end - it 'should not match superdomains, or illegal domains' do - expect(CookieValidation.domains_match('.z.foo.com', 'x.y.z.foo.com')).to be_nil + it 'should not match tlds, or illegal domains' do + expect(CookieValidation.domains_match('.com', 'x.y.z.foo.com')).to be_nil expect(CookieValidation.domains_match('foo.com', 'com')).to be_nil end it 'should not match domains with and without a dot suffix together' do @@ -211,6 +211,9 @@ it 'should return nil for a root domain' do expect(CookieValidation.hostname_reach('github.com')).to be_nil end + it 'should return nil for a two part tld' do + expect(CookieValidation.hostname_reach('co.nz')).to be_nil + end it "should return 'local' for a local domain" do ['foo.local', 'foo.local.'].each do |hostname| expect(CookieValidation.hostname_reach(hostname)).to eq 'local'