From 608435cabf03e759118f2314490dcee5539f6f66 Mon Sep 17 00:00:00 2001 From: Shadab Zafar Date: Thu, 7 Jul 2016 23:25:39 +0530 Subject: Delete stickycookies when told by the server Fixes #1096 --- mitmproxy/flow/modules.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/mitmproxy/flow/modules.py b/mitmproxy/flow/modules.py index cba96fbc..46da5b64 100644 --- a/mitmproxy/flow/modules.py +++ b/mitmproxy/flow/modules.py @@ -1,8 +1,10 @@ from __future__ import absolute_import, print_function, division import collections +import email.utils import hashlib import re +import time from six.moves import http_cookiejar from six.moves import urllib @@ -320,10 +322,33 @@ class StickyCookieState: for name, (value, attrs) in f.response.cookies.items(multi=True): # FIXME: We now know that Cookie.py screws up some cookies with # valid RFC 822/1123 datetime specifications for expiry. Sigh. - a = self.ckey(attrs, f) - if self.domain_match(f.request.host, a[0]): - b = attrs.with_insert(0, name, value) - self.jar[a][name] = b + dom_port_path = self.ckey(attrs, f) + + if self.domain_match(f.request.host, dom_port_path[0]): + + # See if 'expires' time is in the past + expired = False + if 'expires' in attrs: + e = email.utils.parsedate_tz(attrs["expires"]) + if e: + exp_ts = email.utils.mktime_tz(e) + now_ts = time.time() + expired = exp_ts < now_ts + + # or if Max-Age is 0 + expired = expired or (int(attrs.get('Max-Age', 1)) == 0) + + if expired: + # Remove the cookie from jar + self.jar[dom_port_path].pop(name, None) + + # If all cookies of a dom_port_path have been removed + # then remove it from the jar itself + if not self.jar[dom_port_path]: + self.jar.pop(dom_port_path, None) + else: + b = attrs.with_insert(0, name, value) + self.jar[dom_port_path][name] = b def handle_request(self, f): l = [] -- cgit v1.2.3 From 6a746deff57d7283ee8440148b87ea16a672739a Mon Sep 17 00:00:00 2001 From: Shadab Zafar Date: Thu, 7 Jul 2016 23:26:52 +0530 Subject: Add tests for deletion of stickycookies --- test/mitmproxy/test_flow.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index bf7622f6..c0d9155f 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -55,14 +55,16 @@ class TestStickyCookieState: assert s.domain_match("google.com", ".google.com") def test_response(self): - c = "SSID=mooo; domain=.google.com, FOO=bar; Domain=.google.com; Path=/; " \ + c = ( + "SSID=mooo; domain=.google.com, FOO=bar; Domain=.google.com; Path=/; " "Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; " + ) s, f = self._response(c, "host") assert not s.jar.keys() s, f = self._response(c, "www.google.com") - assert s.jar.keys() + assert list(s.jar.keys())[0] == ('.google.com', 80, '/') s, f = self._response("SSID=mooo", "www.google.com") assert list(s.jar.keys())[0] == ('www.google.com', 80, '/') @@ -101,6 +103,28 @@ class TestStickyCookieState: assert len(s.jar[googlekey]) == 1 assert list(s.jar[googlekey]["somecookie"].values())[0] == "newvalue" + def test_response_delete(self): + c = "duffer=zafar; Path=/", "www.google.com" + + # Test that a cookie is be deleted + # by setting the expire time in the past + s, f = self._response(*c) + f.response.headers["Set-Cookie"] = "duffer=; Expires=Thu, 01-Jan-1970 00:00:00 GMT" + s.handle_response(f) + assert not s.jar.keys() + + # or by setting Max-Age to 0 + s, f = self._response(*c) + f.response.headers["Set-Cookie"] = "duffer=; Max-Age=0" + s.handle_response(f) + assert not s.jar.keys() + + # or both + s, f = self._response(*c) + f.response.headers["Set-Cookie"] = "duffer=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0" + s.handle_response(f) + assert not s.jar.keys() + def test_request(self): s, f = self._response("SSID=mooo", b"www.google.com") assert "cookie" not in f.request.headers -- cgit v1.2.3 From c92992f03bba6553ec39fc42e6716beb942967e3 Mon Sep 17 00:00:00 2001 From: Shadab Zafar Date: Fri, 8 Jul 2016 14:16:29 +0530 Subject: Move cookie expiry detection to separate function --- mitmproxy/flow/modules.py | 17 +---------------- netlib/http/cookies.py | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/mitmproxy/flow/modules.py b/mitmproxy/flow/modules.py index 46da5b64..ab41da8d 100644 --- a/mitmproxy/flow/modules.py +++ b/mitmproxy/flow/modules.py @@ -1,10 +1,8 @@ from __future__ import absolute_import, print_function, division import collections -import email.utils import hashlib import re -import time from six.moves import http_cookiejar from six.moves import urllib @@ -325,20 +323,7 @@ class StickyCookieState: dom_port_path = self.ckey(attrs, f) if self.domain_match(f.request.host, dom_port_path[0]): - - # See if 'expires' time is in the past - expired = False - if 'expires' in attrs: - e = email.utils.parsedate_tz(attrs["expires"]) - if e: - exp_ts = email.utils.mktime_tz(e) - now_ts = time.time() - expired = exp_ts < now_ts - - # or if Max-Age is 0 - expired = expired or (int(attrs.get('Max-Age', 1)) == 0) - - if expired: + if cookies.is_expired(attrs): # Remove the cookie from jar self.jar[dom_port_path].pop(name, None) diff --git a/netlib/http/cookies.py b/netlib/http/cookies.py index 768a85df..90789365 100644 --- a/netlib/http/cookies.py +++ b/netlib/http/cookies.py @@ -1,7 +1,8 @@ import collections +import email.utils import re +import time -import email.utils from netlib import multidict """ @@ -260,3 +261,24 @@ def refresh_set_cookie_header(c, delta): if not ret: raise ValueError("Invalid Cookie") return ret + +def is_expired(cookie_attrs): + """ + Determines whether a cookie has expired. + + Returns: boolean + """ + expired = False + + # See if 'expires' time is in the past + if 'expires' in cookie_attrs: + e = email.utils.parsedate_tz(cookie_attrs["expires"]) + if e: + exp_ts = email.utils.mktime_tz(e) + now_ts = time.time() + expired = exp_ts < now_ts + + # or if Max-Age is 0 + expired = expired or (int(cookie_attrs.get('Max-Age', 1)) == 0) + + return expired -- cgit v1.2.3 From 39f51084003b93a2e9868f7a56acfc29c12ed79e Mon Sep 17 00:00:00 2001 From: Shadab Zafar Date: Sun, 10 Jul 2016 01:06:50 +0530 Subject: Test cookies.is_expired separately --- netlib/http/cookies.py | 13 +++++++++---- test/netlib/http/test_cookies.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/netlib/http/cookies.py b/netlib/http/cookies.py index 90789365..dd0af99c 100644 --- a/netlib/http/cookies.py +++ b/netlib/http/cookies.py @@ -262,23 +262,28 @@ def refresh_set_cookie_header(c, delta): raise ValueError("Invalid Cookie") return ret + def is_expired(cookie_attrs): """ Determines whether a cookie has expired. Returns: boolean """ - expired = False # See if 'expires' time is in the past + expires = False if 'expires' in cookie_attrs: e = email.utils.parsedate_tz(cookie_attrs["expires"]) if e: exp_ts = email.utils.mktime_tz(e) now_ts = time.time() - expired = exp_ts < now_ts + expires = exp_ts < now_ts # or if Max-Age is 0 - expired = expired or (int(cookie_attrs.get('Max-Age', 1)) == 0) + max_age = False + try: + max_age = int(cookie_attrs.get('Max-Age', 1)) == 0 + except ValueError: + pass - return expired + return expires or max_age diff --git a/test/netlib/http/test_cookies.py b/test/netlib/http/test_cookies.py index 83b85656..17e21b94 100644 --- a/test/netlib/http/test_cookies.py +++ b/test/netlib/http/test_cookies.py @@ -245,3 +245,24 @@ def test_refresh_cookie(): assert cookies.refresh_set_cookie_header(c, 0) c = "foo/bar=bla" assert cookies.refresh_set_cookie_header(c, 0) + + +def test_is_expired(): + CA = cookies.CookieAttrs + + # A cookie can be expired + # by setting the expire time in the past + assert cookies.is_expired(CA([("Expires", "Thu, 01-Jan-1970 00:00:00 GMT")])) + + # or by setting Max-Age to 0 + assert cookies.is_expired(CA([("Max-Age", "0")])) + + # or both + assert cookies.is_expired(CA([("Expires", "Thu, 01-Jan-1970 00:00:00 GMT"), ("Max-Age", "0")])) + + assert not cookies.is_expired(CA([("Expires", "Thu, 24-Aug-2063 00:00:00 GMT")])) + assert not cookies.is_expired(CA([("Max-Age", "1")])) + assert not cookies.is_expired(CA([("Expires", "Thu, 15-Jul-2068 00:00:00 GMT"), ("Max-Age", "1")])) + + assert not cookies.is_expired(CA([("Max-Age", "nan")])) + assert not cookies.is_expired(CA([("Expires", "false")])) -- cgit v1.2.3 From 7eade1ef7c24b98567c1657973290aa5377b2719 Mon Sep 17 00:00:00 2001 From: Shadab Zafar Date: Sun, 10 Jul 2016 01:08:02 +0530 Subject: Remove redundant tests --- test/mitmproxy/test_flow.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index c0d9155f..74b3f599 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -113,18 +113,6 @@ class TestStickyCookieState: s.handle_response(f) assert not s.jar.keys() - # or by setting Max-Age to 0 - s, f = self._response(*c) - f.response.headers["Set-Cookie"] = "duffer=; Max-Age=0" - s.handle_response(f) - assert not s.jar.keys() - - # or both - s, f = self._response(*c) - f.response.headers["Set-Cookie"] = "duffer=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0" - s.handle_response(f) - assert not s.jar.keys() - def test_request(self): s, f = self._response("SSID=mooo", b"www.google.com") assert "cookie" not in f.request.headers -- cgit v1.2.3