# frozen_string_literal: true require 'mechanize/test_case' require 'fileutils' class TestMechanizeCookieJar < Mechanize::TestCase def setup super @jar = Mechanize::CookieJar.new @jar.extend Minitest::Assertions def @jar.add(*args) capture_io { super } end def @jar.jar(*args) result = nil capture_io { result = super } result end def @jar.save_as(*args) result = nil capture_io { result = super } result end def @jar.clear!(*args) result = nil capture_io { result = super } result end end def cookie_values(options = {}) { :name => 'Foo', :value => 'Bar', :path => '/', :expires => Time.now + (10 * 86400), :for_domain => true, :domain => 'rubygems.org' }.merge(options) end def test_two_cookies_same_domain_and_name_different_paths url = URI 'http://rubygems.org/' cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:path => '/onetwo'))) assert_equal(1, @jar.cookies(url).length) assert_equal 2, @jar.cookies(URI('http://rubygems.org/onetwo')).length end def test_domain_case url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) @jar.add(url, Mechanize::Cookie.new( cookie_values(:domain => 'rubygems.Org', :name => 'aaron'))) assert_equal(2, @jar.cookies(url).length) url2 = URI 'http://rubygems.oRg/' assert_equal(2, @jar.cookies(url2).length) end def test_host_only url = URI.parse('http://rubygems.org/') @jar.add(url, Mechanize::Cookie.new( cookie_values(:domain => 'rubygems.org', :for_domain => false))) assert_equal(1, @jar.cookies(url).length) assert_equal(1, @jar.cookies(URI('http://rubygems.org/')).length) assert_equal(1, @jar.cookies(URI('https://rubygems.org/')).length) assert_equal(0, @jar.cookies(URI('http://www.rubygems.org/')).length) end def test_empty_value values = cookie_values(:value => "") url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) @jar.add url, Mechanize::Cookie.new(values.merge(:domain => 'rubygems.Org', :name => 'aaron')) assert_equal(2, @jar.cookies(url).length) url2 = URI 'http://rubygems.oRg/' assert_equal(2, @jar.cookies(url2).length) end def test_add_future_cookies url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) # Add the same cookie, and we should still only have one @jar.add(url, Mechanize::Cookie.new(cookie_values)) assert_equal(1, @jar.cookies(url).length) # Make sure we can get the cookie from different paths assert_equal(1, @jar.cookies(URI('http://rubygems.org/login')).length) # Make sure we can't get the cookie from different domains assert_equal(0, @jar.cookies(URI('http://google.com/')).length) end def test_add_multiple_cookies url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) # Add the same cookie, and we should still only have one @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(2, @jar.cookies(url).length) # Make sure we can get the cookie from different paths assert_equal(2, @jar.cookies(URI('http://rubygems.org/login')).length) # Make sure we can't get the cookie from different domains assert_equal(0, @jar.cookies(URI('http://google.com/')).length) end def test_add_rejects_cookies_that_do_not_contain_an_embedded_dot url = URI 'http://rubygems.org/' tld_cookie = Mechanize::Cookie.new(cookie_values(:domain => '.org')) @jar.add(url, tld_cookie) # single dot domain is now treated as no domain # single_dot_cookie = Mechanize::Cookie.new(cookie_values(:domain => '.')) # @jar.add(url, single_dot_cookie) assert_equal(0, @jar.cookies(url).length) end def test_fall_back_rules_for_local_domains url = URI 'http://www.example.local' tld_cookie = Mechanize::Cookie.new(cookie_values(:domain => '.local')) @jar.add(url, tld_cookie) assert_equal(0, @jar.cookies(url).length) sld_cookie = Mechanize::Cookie.new(cookie_values(:domain => '.example.local')) @jar.add(url, sld_cookie) assert_equal(1, @jar.cookies(url).length) end def test_add_makes_exception_for_localhost url = URI 'http://localhost' tld_cookie = Mechanize::Cookie.new(cookie_values(:domain => 'localhost')) @jar.add(url, tld_cookie) assert_equal(1, @jar.cookies(url).length) end def test_add_cookie_for_the_parent_domain url = URI 'http://x.foo.com' cookie = Mechanize::Cookie.new(cookie_values(:domain => '.foo.com')) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) end def test_add_does_not_reject_cookies_from_a_nested_subdomain url = URI 'http://y.x.foo.com' cookie = Mechanize::Cookie.new(cookie_values(:domain => '.foo.com')) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) end def test_cookie_without_leading_dot_does_not_cause_substring_match url = URI 'http://arubygems.org/' cookie = Mechanize::Cookie.new(cookie_values(:domain => 'rubygems.org')) @jar.add(url, cookie) assert_equal(0, @jar.cookies(url).length) end def test_cookie_without_leading_dot_matches_subdomains url = URI 'http://admin.rubygems.org/' cookie = Mechanize::Cookie.new(cookie_values(:domain => 'rubygems.org')) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) end def test_cookies_with_leading_dot_match_subdomains url = URI 'http://admin.rubygems.org/' @jar.add(url, Mechanize::Cookie.new(cookie_values(:domain => '.rubygems.org'))) assert_equal(1, @jar.cookies(url).length) end def test_cookies_with_leading_dot_match_parent_domains url = URI 'http://rubygems.org/' @jar.add(url, Mechanize::Cookie.new(cookie_values(:domain => '.rubygems.org'))) assert_equal(1, @jar.cookies(url).length) end def test_cookies_with_leading_dot_match_parent_domains_exactly url = URI 'http://arubygems.org/' @jar.add(url, Mechanize::Cookie.new(cookie_values(:domain => '.rubygems.org'))) assert_equal(0, @jar.cookies(url).length) end def test_cookie_for_ipv4_address_matches_the_exact_ipaddress url = URI 'http://192.168.0.1/' cookie = Mechanize::Cookie.new(cookie_values(:domain => '192.168.0.1')) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) end def test_cookie_for_ipv4_address_does_not_cause_subdomain_match url = URI 'http://192.168.0.1/' cookie = Mechanize::Cookie.new(cookie_values(:domain => '.0.1')) @jar.add(url, cookie) assert_equal(0, @jar.cookies(url).length) end def test_cookie_for_ipv6_address_matches_the_exact_ipaddress url = URI 'http://[fe80::0123:4567:89ab:cdef]/' cookie = Mechanize::Cookie.new(cookie_values(:domain => '[fe80::0123:4567:89ab:cdef]')) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) end def test_cookies_dot url = URI 'http://www.host.example/' @jar.add(url, Mechanize::Cookie.new(cookie_values(:domain => 'www.host.example'))) url = URI 'http://wwwxhost.example/' assert_equal(0, @jar.cookies(url).length) end def test_clear_bang url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(2, @jar.cookies(url).length) @jar.clear! assert_equal(0, @jar.cookies(url).length) end def test_save_cookies_yaml url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) s_cookie = Mechanize::Cookie.new(cookie_values(:name => 'Bar', :expires => nil)) @jar.add(url, cookie) @jar.add(url, s_cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(3, @jar.cookies(url).length) in_tmpdir do value = @jar.save_as("cookies.yml") assert_same @jar, value jar = Mechanize::CookieJar.new jar.load("cookies.yml") assert_equal(2, jar.cookies(url).length) end assert_equal(3, @jar.cookies(url).length) end def test_save_session_cookies_yaml url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) s_cookie = Mechanize::Cookie.new(cookie_values(:name => 'Bar', :expires => nil)) @jar.add(url, cookie) @jar.add(url, s_cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(3, @jar.cookies(url).length) in_tmpdir do @jar.save_as("cookies.yml", :format => :yaml, :session => true) jar = Mechanize::CookieJar.new jar.load("cookies.yml") assert_equal(3, jar.cookies(url).length) end assert_equal(3, @jar.cookies(url).length) end def test_save_cookies_cookiestxt url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) s_cookie = Mechanize::Cookie.new(cookie_values(:name => 'Bar', :expires => nil)) @jar.add(url, cookie) @jar.add(url, s_cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(3, @jar.cookies(url).length) in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt) assert_match(/\A# (?:Netscape )?HTTP Cookie File$/, File.read("cookies.txt")) jar = Mechanize::CookieJar.new jar.load("cookies.txt", :cookiestxt) assert_equal(2, jar.cookies(url).length) end in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt, :session => true) assert_match(/\A# (?:Netscape )?HTTP Cookie File$/, File.read("cookies.txt")) jar = Mechanize::CookieJar.new jar.load("cookies.txt", :cookiestxt) assert_equal(3, jar.cookies(url).length) end assert_equal(3, @jar.cookies(url).length) end def test_expire_cookies url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) # Add a second cookie @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(2, @jar.cookies(url).length) # Make sure we can get the cookie from different paths assert_equal(2, @jar.cookies(URI('http://rubygems.org/login')).length) # Expire the first cookie @jar.add(url, Mechanize::Cookie.new( cookie_values(:expires => Time.now - (10 * 86400)))) assert_equal(1, @jar.cookies(url).length) # Expire the second cookie @jar.add(url, Mechanize::Cookie.new( cookie_values( :name => 'Baz', :expires => Time.now - (10 * 86400)))) assert_equal(0, @jar.cookies(url).length) end def test_session_cookies values = cookie_values(:expires => nil) url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) # Add a second cookie @jar.add(url, Mechanize::Cookie.new(values.merge(:name => 'Baz'))) assert_equal(2, @jar.cookies(url).length) # Make sure we can get the cookie from different paths assert_equal(2, @jar.cookies(URI('http://rubygems.org/login')).length) # Expire the first cookie @jar.add(url, Mechanize::Cookie.new(values.merge(:expires => Time.now - (10 * 86400)))) assert_equal(1, @jar.cookies(url).length) # Expire the second cookie @jar.add(url, Mechanize::Cookie.new( values.merge(:name => 'Baz', :expires => Time.now - (10 * 86400)))) assert_equal(0, @jar.cookies(url).length) # When given a URI with a blank path, CookieJar#cookies should return # cookies with the path '/': url = URI 'http://rubygems.org' assert_equal '', url.path assert_equal(0, @jar.cookies(url).length) # Now add a cookie with the path set to '/': @jar.add(url, Mechanize::Cookie.new(values.merge( :name => 'has_root_path', :path => '/'))) assert_equal(1, @jar.cookies(url).length) end def test_paths values = cookie_values(:path => "/login", :expires => nil) url = URI 'http://rubygems.org/login' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length) # Add a second cookie @jar.add(url, Mechanize::Cookie.new(values.merge( :name => 'Baz' ))) assert_equal(2, @jar.cookies(url).length) # Make sure we don't get the cookie in a different path assert_equal(0, @jar.cookies(URI('http://rubygems.org/hello')).length) assert_equal(0, @jar.cookies(URI('http://rubygems.org/')).length) # Expire the first cookie @jar.add(url, Mechanize::Cookie.new(values.merge( :expires => Time.now - (10 * 86400)))) assert_equal(1, @jar.cookies(url).length) # Expire the second cookie @jar.add(url, Mechanize::Cookie.new(values.merge( :name => 'Baz', :expires => Time.now - (10 * 86400)))) assert_equal(0, @jar.cookies(url).length) end def test_save_and_read_cookiestxt url = URI 'http://rubygems.org/' # Add one cookie with an expiration date in the future cookie = Mechanize::Cookie.new(cookie_values) @jar.add(url, cookie) @jar.add(url, Mechanize::Cookie.new(cookie_values(:name => 'Baz'))) assert_equal(2, @jar.cookies(url).length) in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt) @jar.clear! @jar.load("cookies.txt", :cookiestxt) end assert_equal(2, @jar.cookies(url).length) end def test_save_and_read_cookiestxt_with_session_cookies url = URI 'http://rubygems.org/' @jar.add(url, Mechanize::Cookie.new(cookie_values(:expires => nil))) in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt) @jar.clear! @jar.load("cookies.txt", :cookiestxt) end assert_equal(0, @jar.cookies(url).length) end def test_prevent_command_injection_when_saving skip if windows? url = URI 'http://rubygems.org/' path = '| ruby -rfileutils -e \'FileUtils.touch("vul.txt")\'' @jar.add(url, Mechanize::Cookie.new(cookie_values)) in_tmpdir do @jar.save_as(path, :cookiestxt) assert_equal(false, File.exist?('vul.txt')) end end def test_prevent_command_injection_when_loading skip if windows? url = URI 'http://rubygems.org/' path = '| ruby -rfileutils -e \'FileUtils.touch("vul.txt")\'' @jar.add(url, Mechanize::Cookie.new(cookie_values)) in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt) @jar.clear! assert_raises Errno::ENOENT do @jar.load(path, :cookiestxt) end assert_equal(false, File.exist?('vul.txt')) end end def test_save_and_read_expired_cookies url = URI 'http://rubygems.org/' @jar.jar['rubygems.org'] = {} @jar.add url, Mechanize::Cookie.new(cookie_values) # HACK no assertion end def test_ssl_cookies # thanks to michal "ocher" ochman for reporting the bug responsible for this test. values = cookie_values(:expires => nil) values_ssl = values.merge(:name => 'Baz', :domain => "#{values[:domain]}:443") url = URI 'https://rubygems.org/login' cookie = Mechanize::Cookie.new(values) @jar.add(url, cookie) assert_equal(1, @jar.cookies(url).length, "did not handle SSL cookie") cookie = Mechanize::Cookie.new(values_ssl) @jar.add(url, cookie) assert_equal(2, @jar.cookies(url).length, "did not handle SSL cookie with :443") end def test_secure_cookie nurl = URI 'http://rubygems.org/login' surl = URI 'https://rubygems.org/login' ncookie = Mechanize::Cookie.new(cookie_values(:name => 'Foo1')) scookie = Mechanize::Cookie.new(cookie_values(:name => 'Foo2', :secure => true)) @jar.add(nurl, ncookie) @jar.add(nurl, scookie) @jar.add(surl, ncookie) @jar.add(surl, scookie) assert_equal('Foo1', @jar.cookies(nurl).map { |c| c.name }.sort.join(' ') ) assert_equal('Foo1 Foo2', @jar.cookies(surl).map { |c| c.name }.sort.join(' ') ) end def test_save_cookies_cookiestxt_subdomain top_url = URI 'http://rubygems.org/' subdomain_url = URI 'http://admin.rubygems.org/' # cookie1 is for *.rubygems.org; cookie2 is only for rubygems.org, no subdomains cookie1 = Mechanize::Cookie.new(cookie_values) cookie2 = Mechanize::Cookie.new(cookie_values(:name => 'Boo', :for_domain => false)) @jar.add(top_url, cookie1) @jar.add(top_url, cookie2) assert_equal(2, @jar.cookies(top_url).length) assert_equal(1, @jar.cookies(subdomain_url).length) in_tmpdir do @jar.save_as("cookies.txt", :cookiestxt) jar = Mechanize::CookieJar.new jar.load("cookies.txt", :cookiestxt) # HACK test the format assert_equal(2, jar.cookies(top_url).length) assert_equal(1, jar.cookies(subdomain_url).length) # Check that we actually wrote the file correctly (not just that we were # able to read what we wrote): # # * Cookies that only match exactly the domain specified must not have a # leading dot, and must have FALSE as the second field. # * Cookies that match subdomains may have a leading dot, and must have # TRUE as the second field. cookies_txt = File.readlines("cookies.txt") assert_equal(1, cookies_txt.grep( /^rubygems\.org\tFALSE/ ).length) assert_equal(1, cookies_txt.grep( /^\.rubygems\.org\tTRUE/ ).length) end assert_equal(2, @jar.cookies(top_url).length) assert_equal(1, @jar.cookies(subdomain_url).length) end end