From f63a5e7d1616449ef0505180de421ba36f560cac Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 27 Nov 2022 14:17:37 -0800 Subject: [PATCH] Improve matching of cookie assertions. The current implementation makes assumptions about the order and case sensitivity of cookie attributes. Introduce methods to parse those fields and compare them semantically. Update the existing tests to take advantage of these new assertions. --- actionpack/test/abstract_unit.rb | 89 +++++++++++++ .../test/controller/integration_test.rb | 4 +- .../request_forgery_protection_test.rb | 7 +- actionpack/test/dispatch/cookies_test.rb | 119 ++++++++---------- actionpack/test/dispatch/response_test.rb | 6 +- .../dispatch/session/cookie_store_test.rb | 25 ++-- 6 files changed, 169 insertions(+), 81 deletions(-) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 2ab035c76ae21..f3f6af9ab55a2 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -368,6 +368,95 @@ def jruby_skip(message = "") end end +module CookieAssertions + def parse_set_cookie_attributes(fields, attributes = {}) + if fields.is_a?(String) + fields = fields.split(";").map(&:strip) + end + + fields.each do |field| + key, value = field.split("=", 2) + + # Normalize the key to lowercase: + key.downcase! + + if value + attributes[key] = value + else + attributes[key] = true + end + end + + attributes + end + + # Parse the set-cookie header and return a hash of cookie names and values. + # + # Example: + # set_cookies = headers["set-cookie"] + # parse_set_cookies_headers(set_cookies) + def parse_set_cookies_headers(set_cookies) + if set_cookies.is_a?(String) + set_cookies = set_cookies.split("\n") + end + + cookies = {} + + set_cookies&.each do |cookie_string| + attributes = {} + + fields = cookie_string.split(";").map(&:strip) + + # The first one is the cookie name: + name, value = fields.shift.split("=", 2) + + attributes[:value] = value + + cookies[name] = parse_set_cookie_attributes(fields, attributes) + end + + cookies + end + + def assert_set_cookie_header(expected, header = @response.headers["Set-Cookie"]) + # In Rack v2, this is newline delimited. In Rack v3, this is an array. + # Normalize the comparison so that we can assert equality in both cases. + + if header.is_a?(String) + header = header.split("\n").sort + end + + if expected.is_a?(String) + expected = expected.split("\n").sort + end + + # While not strictly speaking correct, this is probably good enough for now: + header = parse_set_cookies_headers(header) + expected = parse_set_cookies_headers(expected) + + expected.each do |key, value| + assert_equal value, header[key] + end + end + + def assert_not_set_cookie_header(expected, header = @response.headers["Set-Cookie"]) + if header.is_a?(String) + header = header.split("\n").sort + end + + if expected.is_a?(String) + expected = expected.split("\n").sort + end + + # While not strictly speaking correct, this is probably good enough for now: + header = parse_set_cookies_headers(header) + + expected.each do |name| + assert_not_includes(header, name) + end + end +end + class DrivenByRackTest < ActionDispatch::SystemTestCase driven_by :rack_test end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 95384cfb41fc3..0d98a60c720f1 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -313,12 +313,14 @@ def test_post end end + include CookieAssertions + test "response cookies are added to the cookie jar for the next request" do with_test_route_set do cookies["cookie_1"] = "sugar" cookies["cookie_2"] = "oatmeal" get "/cookie_monster" - assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"] + assert_set_cookie_header "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"] assert_equal({ "cookie_1" => "", "cookie_2" => "oatmeal", "cookie_3" => "chocolate" }, cookies.to_hash) end end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 8152946f27312..6ce19221661c0 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -1241,9 +1241,14 @@ def test_csrf_token_cookie_has_same_site_lax assert_match "SameSite=Lax", @response.headers["Set-Cookie"] end + include CookieAssertions + def test_csrf_token_cookie_is_http_only get :cookie - assert_match "HttpOnly", @response.headers["Set-Cookie"] + + cookies = parse_set_cookies_headers(@response.headers["Set-Cookie"]) + csrf_token_cookie = cookies["csrf_token"] + assert csrf_token_cookie["httponly"] end def test_csrf_token_cookie_is_permanent diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index a248eb4d5babc..7ed108fc73c0d 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -81,6 +81,8 @@ def test_write_doesnt_set_a_nil_header end class CookiesTest < ActionController::TestCase + include CookieAssertions + class CustomSerializer def self.load(value) value.to_s + " and loaded" @@ -391,7 +393,7 @@ def test_setting_cookie_with_no_same_site_protection @request.env["action_dispatch.cookies_same_site_protection"] = proc { :none } get :authenticate - assert_cookie_header "user_name=david; path=/; SameSite=None" + assert_set_cookie_header "user_name=david; path=/; SameSite=None" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -401,7 +403,7 @@ def test_setting_cookie_with_same_site_protection_proc_normal_user_agent end get :authenticate - assert_cookie_header "user_name=david; path=/; SameSite=Strict" + assert_set_cookie_header "user_name=david; path=/; SameSite=Strict" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -412,7 +414,7 @@ def test_setting_cookie_with_same_site_protection_proc_special_user_agent request.user_agent = "spooky browser" get :authenticate - assert_cookie_header "user_name=david; path=/" + assert_set_cookie_header "user_name=david; path=/" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -429,7 +431,7 @@ def test_setting_cookie_with_same_site_strict @request.env["action_dispatch.cookies_same_site_protection"] = proc { :strict } get :authenticate - assert_cookie_header "user_name=david; path=/; SameSite=Strict" + assert_set_cookie_header "user_name=david; path=/; SameSite=Strict" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -437,7 +439,7 @@ def test_setting_cookie_with_same_site_nil @request.env["action_dispatch.cookies_same_site_protection"] = proc { nil } get :authenticate - assert_cookie_header "user_name=david; path=/" + assert_set_cookie_header "user_name=david; path=/" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -445,7 +447,7 @@ def test_setting_cookie_with_specific_same_site_strict @request.env["action_dispatch.cookies_same_site_protection"] = proc { :lax } get :set_same_site_strict - assert_cookie_header "user_name=david; path=/; SameSite=Strict" + assert_set_cookie_header "user_name=david; path=/; SameSite=Strict" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -453,13 +455,13 @@ def test_setting_cookie_with_specific_same_site_nil @request.env["action_dispatch.cookies_same_site_protection"] = proc { :lax } get :set_same_site_nil - assert_cookie_header "user_name=david; path=/" + assert_set_cookie_header "user_name=david; path=/" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie get :authenticate - assert_cookie_header "user_name=david; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -477,46 +479,46 @@ def test_setting_the_same_value_to_permanent_cookie def test_setting_with_escapable_characters get :set_with_with_escapable_characters - assert_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/; SameSite=Lax" + assert_set_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/; SameSite=Lax" assert_equal({ "that & guy" => "foo & bar => baz" }, @response.cookies) end def test_setting_cookie_for_fourteen_days get :authenticate_for_fourteen_days - assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie_for_fourteen_days_with_symbols get :authenticate_for_fourteen_days_with_symbols - assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie_with_http_only get :authenticate_with_http_only - assert_cookie_header "user_name=david; path=/; HttpOnly; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; HttpOnly; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie_with_secure @request.env["HTTPS"] = "on" get :authenticate_with_secure - assert_cookie_header "user_name=david; path=/; secure; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; secure; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie_with_secure_on_onion_address @request.host = "fake.onion" get :authenticate_with_secure - assert_cookie_header "user_name=david; path=/; secure; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; secure; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end def test_setting_cookie_with_secure_when_always_write_cookie_is_true old_cookie, @request.cookie_jar.always_write_cookie = @request.cookie_jar.always_write_cookie, true get :authenticate_with_secure - assert_cookie_header "user_name=david; path=/; secure; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; secure; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) ensure @request.cookie_jar.always_write_cookie = old_cookie @@ -524,14 +526,14 @@ def test_setting_cookie_with_secure_when_always_write_cookie_is_true def test_not_setting_cookie_with_secure get :authenticate_with_secure - assert_not_cookie_header "user_name=david; path=/; secure" + assert_not_set_cookie_header("user_name") assert_not_equal({ "user_name" => "david" }, @response.cookies) end def test_multiple_cookies get :set_multiple_cookies assert_equal 2, @response.cookies.size - assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax\nlogin=XJ-122; path=/; SameSite=Lax" + assert_set_cookie_header ["user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax", "login=XJ-122; path=/; SameSite=Lax"] assert_equal({ "login" => "XJ-122", "user_name" => "david" }, @response.cookies) end @@ -542,14 +544,14 @@ def test_setting_test_cookie def test_expiring_cookie request.cookies[:user_name] = "Joe" get :logout - assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" assert_equal({ "user_name" => nil }, @response.cookies) end def test_delete_cookie_with_path request.cookies[:user_name] = "Joe" get :delete_cookie_with_path - assert_cookie_header "user_name=; path=/beaten; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; path=/beaten; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" end def test_delete_unexisting_cookie @@ -880,7 +882,7 @@ def test_permanent_signed_cookie def test_delete_and_set_cookie request.cookies[:user_name] = "Joe" get :delete_and_set_cookie - assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT; SameSite=Lax" assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -1120,172 +1122,172 @@ def test_cookie_with_hash_value_not_modified_by_rotation def test_cookie_with_all_domain_option get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_a_non_standard_tld @request.host = "two.subdomains.nextangle.local" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_australian_style_tld @request.host = "nextangle.com.au" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_australian_style_tld_and_two_subdomains @request.host = "x.nextangle.com.au" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com.au; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_uk_style_tld @request.host = "nextangle.co.uk" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_uk_style_tld_and_two_subdomains @request.host = "x.nextangle.co.uk" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.co.uk; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_host_with_port @request.host = "nextangle.local:3000" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_localhost @request.host = "localhost" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_ipv4_address @request.host = "192.168.1.1" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_ipv6_address @request.host = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" get :set_cookie_with_domain assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_deleting_cookie_with_all_domain_option request.cookies[:user_name] = "Joe" get :delete_cookie_with_domain assert_response :success - assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" end def test_cookie_with_all_domain_option_and_tld_length get :set_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_a_non_standard_tld_and_tld_length @request.host = "two.subdomains.nextangle.local" get :set_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_a_non_standard_2_letter_tld @request.host = "admin.lvh.me" get :set_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.lvh.me; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.lvh.me; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_host_with_port_and_tld_length @request.host = "nextangle.local:3000" get :set_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.nextangle.local; path=/; SameSite=Lax" end def test_cookie_with_all_domain_option_using_longer_tld_length @request.host = "x.y.z.t.com" get :set_cookie_with_domain_and_longer_tld assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.y.z.t.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.y.z.t.com; path=/; SameSite=Lax" end def test_deleting_cookie_with_all_domain_option_and_tld_length request.cookies[:user_name] = "Joe" get :delete_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_one_of_these_domains @request.host = "example1.com" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=example1.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=example1.com; path=/; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_subdomain @request.host = "subdomain.example1.com" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=example1.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=example1.com; path=/; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_similar_tld @request.host = "example1.com.au" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_similar_domain @request.host = "myexample1.com" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_other_domain @request.host = "other-domain.com" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; path=/; SameSite=Lax" end def test_cookie_with_several_preset_domains_using_shared_domain @request.host = "example3.com" get :set_cookie_with_domains assert_response :success - assert_cookie_header "user_name=rizwanreza; domain=.example3.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=rizwanreza; domain=.example3.com; path=/; SameSite=Lax" end def test_cookie_with_domain_proc get :set_cookie_with_domain_proc assert_response :success - assert_cookie_header "user_name=braindeaf; domain=.sub.www.nextangle.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=braindeaf; domain=.sub.www.nextangle.com; path=/; SameSite=Lax" end def test_cookie_with_domain_proc_with_request get :set_cookie_with_domain_proc_with_request assert_response :success - assert_cookie_header "user_name=braindeaf; domain=.sub.www.nextangle.com; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=braindeaf; domain=.sub.www.nextangle.com; path=/; SameSite=Lax" end def test_deleting_cookie_with_several_preset_domains_using_one_of_these_domains @@ -1293,7 +1295,7 @@ def test_deleting_cookie_with_several_preset_domains_using_one_of_these_domains request.cookies[:user_name] = "Joe" get :delete_cookie_with_domains assert_response :success - assert_cookie_header "user_name=; domain=example2.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; domain=example2.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" end def test_deleting_cookie_with_several_preset_domains_using_other_domain @@ -1301,7 +1303,7 @@ def test_deleting_cookie_with_several_preset_domains_using_other_domain request.cookies[:user_name] = "Joe" get :delete_cookie_with_domains assert_response :success - assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax" end def test_cookies_hash_is_indifferent_access @@ -1327,7 +1329,7 @@ def test_setting_request_cookies_is_indifferent_access def test_cookies_retained_across_requests get :symbol_key - assert_cookie_header "user_name=david; path=/; SameSite=Lax" + assert_set_cookie_header "user_name=david; path=/; SameSite=Lax" assert_equal "david", cookies[:user_name] get :noop @@ -1446,7 +1448,7 @@ def test_encrypted_cookie_with_expires_set_relatively def test_vanilla_cookie_with_expires_set_relatively travel_to Time.utc(2017, 8, 15) do get :cookie_expires_in_two_hours - assert_cookie_header "user_name=assain; path=/; expires=Tue, 15 Aug 2017 02:00:00 GMT; SameSite=Lax" + assert_set_cookie_header "user_name=assain; path=/; expires=Tue, 15 Aug 2017 02:00:00 GMT; SameSite=Lax" end end @@ -1574,23 +1576,4 @@ def test_read_rails_5_2_stable_signed_cookies_if_use_metadata_config_is_true assert_equal "5-2-Stable Choco Chip Cookie", cookies.signed[:favorite] end - - private - def assert_cookie_header(expected) - header = @response.headers["Set-Cookie"] - if header.respond_to?(:to_str) - assert_equal expected.split("\n").sort, header.split("\n").sort - else - assert_equal expected.split("\n"), header - end - end - - def assert_not_cookie_header(expected) - header = @response.headers["Set-Cookie"] - if header.respond_to?(:to_str) - assert_not_equal expected.split("\n").sort, header.split("\n").sort - else - assert_not_equal expected.split("\n"), header - end - end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 4ba9dd78a1c59..4cf41144b3f99 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -227,10 +227,12 @@ def test_only_set_charset_still_defaults_to_text_html assert_equal "OK", @response.message end + include CookieAssertions + test "cookies" do @response.set_cookie("user_name", value: "david", path: "/") _status, headers, _body = @response.to_a - assert_equal "user_name=david; path=/", headers["Set-Cookie"] + assert_set_cookie_header "user_name=david; path=/", headers["Set-Cookie"] assert_equal({ "user_name" => "david" }, @response.cookies) end @@ -238,7 +240,7 @@ def test_only_set_charset_still_defaults_to_text_html @response.set_cookie("user_name", value: "david", path: "/") @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10, 5)) _status, headers, _body = @response.to_a - assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT", headers["Set-Cookie"] + assert_set_cookie_header "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 GMT", headers["Set-Cookie"] assert_equal({ "login" => "foo&bar", "user_name" => "david" }, @response.cookies) end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 95be113be1c36..86238d5557813 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -72,16 +72,23 @@ def renew_session_id end end - def parse_cookie_from_header - cookie_matches = headers["Set-Cookie"].match(/#{SessionKey}=([^;]+)/) - cookie_matches && cookie_matches[1] - end + include CookieAssertions + + def assert_session_cookie(attributes_string, contents) + cookies = parse_set_cookies_headers(headers["Set-Cookie"]) - def assert_session_cookie(cookie_string, contents) - assert_includes headers["Set-Cookie"], cookie_string + if session_cookie = cookies[SessionKey] + if attributes_string + expected_attributes = parse_set_cookie_attributes(attributes_string) - session_value = parse_cookie_from_header - session_data = Encryptor.decrypt_and_verify(Rack::Utils.unescape(session_value)) rescue nil + expected_attributes.each do |key, value| + assert_equal value, session_cookie[key], "expected #{key} to be #{value.inspect}, but was #{session_cookie[key].inspect}" + end + end + + session_value = session_cookie[:value] + session_data = Encryptor.decrypt_and_verify(Rack::Utils.unescape(session_value)) rescue nil + end assert_not_nil session_data, "session failed to decrypt" assert_equal session_data.slice(*contents.keys), contents @@ -92,7 +99,7 @@ def test_setting_session_value get "/set_session_value" assert_response :success - assert_session_cookie "path=/; HttpOnly", "foo" => "bar" + assert_session_cookie "path=/; HttpOnly", { "foo" => "bar" } end end