diff options
-rw-r--r-- | docs/install.rst | 24 | ||||
-rw-r--r-- | mitmproxy/contrib/tnetstring.py (renamed from mitmproxy/tnetstring.py) | 280 | ||||
-rw-r--r-- | mitmproxy/flow/io.py | 2 | ||||
-rw-r--r-- | mitmproxy/platform/pf.py | 1 | ||||
-rw-r--r-- | mitmproxy/proxy/config.py | 2 | ||||
-rw-r--r-- | mitmproxy/web/static/app.js | 11 | ||||
-rw-r--r-- | netlib/http/http2/framereader.py | 3 | ||||
-rw-r--r-- | test/mitmproxy/test_contrib_tnetstring.py | 141 | ||||
-rw-r--r-- | test/mitmproxy/test_flow.py | 3 | ||||
-rw-r--r-- | test/mitmproxy/test_protocol_http1.py | 8 | ||||
-rw-r--r-- | test/mitmproxy/test_protocol_http2.py | 41 | ||||
-rw-r--r-- | tox.ini | 2 | ||||
-rw-r--r-- | web/src/js/ducks/settings.js | 2 |
13 files changed, 342 insertions, 178 deletions
diff --git a/docs/install.rst b/docs/install.rst index 566430f5..6d82f81f 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -11,8 +11,10 @@ Installation On Ubuntu Ubuntu comes with Python but we need to install pip, python-dev and several libraries. This was tested on a fully patched installation of Ubuntu 14.04. ->>> sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev ->>> sudo pip install mitmproxy +.. code:: bash + + sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev + sudo pip install mitmproxy # or pip install --user mitmproxy Once installation is complete you can run :ref:`mitmproxy` or :ref:`mitmdump` from a terminal. @@ -27,6 +29,20 @@ get set up to contribute to the project, install the dependencies as you would f mitmproxy installation (see :ref:`install-ubuntu`). Then see the Hacking_ section of the README on GitHub. +.. _install-fedora: + +Installation On Fedora +---------------------- + +Fedora comes with Python but we need to install pip, python-dev and several libraries. +This was tested on a fully patched installation of Fedora 23. + +.. code:: bash + + sudo dnf install -y python-pip python-devel libffi-devel openssl-devel libxml2-devel libxslt-devel libpng-devel libjpeg-devel + sudo pip install mitmproxy # or pip install --user mitmproxy + +Once installation is complete you can run :ref:`mitmproxy` or :ref:`mitmdump` from a terminal. .. _install-arch: @@ -87,7 +103,9 @@ Installation On Windows First, install the latest version of Python 2.7 from the `Python website`_. If you already have an older version of Python 2.7 installed, make sure to install pip_ -(pip is included in Python 2.7.9+ by default). +(pip is included in Python 2.7.9+ by default). If pip aborts with an error, make sure you are using the current version of pip. + +>>> python -m pip install --upgrade pip Next, add Python and the Python Scripts directory to your **PATH** variable. You can do this easily by running the following in powershell: diff --git a/mitmproxy/tnetstring.py b/mitmproxy/contrib/tnetstring.py index f40e8ad8..9bf20b09 100644 --- a/mitmproxy/tnetstring.py +++ b/mitmproxy/contrib/tnetstring.py @@ -79,9 +79,8 @@ __version__ = "%d.%d.%d%s" % ( __ver_major__, __ver_minor__, __ver_patch__, __ver_sub__) -def dumps(value, encoding=None): - """dumps(object,encoding=None) -> string - +def dumps(value): + """ This function dumps a python object as a tnetstring. """ # This uses a deque to collect output fragments in reverse order, @@ -91,22 +90,21 @@ def dumps(value, encoding=None): # consider the _gdumps() function instead; it's a standard top-down # generator that's simpler to understand but much less efficient. q = deque() - _rdumpq(q, 0, value, encoding) - return "".join(q) - + _rdumpq(q, 0, value) + return b''.join(q) -def dump(value, file, encoding=None): - """dump(object,file,encoding=None) - This function dumps a python object as a tnetstring and writes it to - the given file. +def dump(value, file_handle): """ - file.write(dumps(value, encoding)) - file.flush() + This function dumps a python object as a tnetstring and + writes it to the given file. + """ + file_handle.write(dumps(value)) -def _rdumpq(q, size, value, encoding=None): - """Dump value as a tnetstring, to a deque instance, last chunks first. +def _rdumpq(q, size, value): + """ + Dump value as a tnetstring, to a deque instance, last chunks first. This function generates the tnetstring representation of the given value, pushing chunks of the output onto the given deque instance. It pushes @@ -122,79 +120,70 @@ def _rdumpq(q, size, value, encoding=None): """ write = q.appendleft if value is None: - write("0:~") + write(b'0:~') return size + 3 - if value is True: - write("4:true!") + elif value is True: + write(b'4:true!') return size + 7 - if value is False: - write("5:false!") + elif value is False: + write(b'5:false!') return size + 8 - if isinstance(value, six.integer_types): - data = str(value) + elif isinstance(value, six.integer_types): + data = str(value).encode() ldata = len(data) - span = str(ldata) - write("#") + span = str(ldata).encode() + write(b'#') write(data) - write(":") + write(b':') write(span) return size + 2 + len(span) + ldata - if isinstance(value, (float,)): + elif isinstance(value, float): # Use repr() for float rather than str(). # It round-trips more accurately. # Probably unnecessary in later python versions that # use David Gay's ftoa routines. - data = repr(value) + data = repr(value).encode() ldata = len(data) - span = str(ldata) - write("^") + span = str(ldata).encode() + write(b'^') write(data) - write(":") + write(b':') write(span) return size + 2 + len(span) + ldata - if isinstance(value, str): + elif isinstance(value, bytes): lvalue = len(value) - span = str(lvalue) - write(",") + span = str(lvalue).encode() + write(b',') write(value) - write(":") + write(b':') write(span) return size + 2 + len(span) + lvalue - if isinstance(value, (list, tuple,)): - write("]") + elif isinstance(value, (list, tuple)): + write(b']') init_size = size = size + 1 for item in reversed(value): - size = _rdumpq(q, size, item, encoding) - span = str(size - init_size) - write(":") + size = _rdumpq(q, size, item) + span = str(size - init_size).encode() + write(b':') write(span) return size + 1 + len(span) - if isinstance(value, dict): - write("}") + elif isinstance(value, dict): + write(b'}') init_size = size = size + 1 - for (k, v) in six.iteritems(value): - size = _rdumpq(q, size, v, encoding) - size = _rdumpq(q, size, k, encoding) - span = str(size - init_size) - write(":") + for (k, v) in value.items(): + size = _rdumpq(q, size, v) + size = _rdumpq(q, size, k) + span = str(size - init_size).encode() + write(b':') write(span) return size + 1 + len(span) - if isinstance(value, unicode): - if encoding is None: - raise ValueError("must specify encoding to dump unicode strings") - value = value.encode(encoding) - lvalue = len(value) - span = str(lvalue) - write(",") - write(value) - write(":") - write(span) - return size + 2 + len(span) + lvalue - raise ValueError("unserializable object") + else: + raise ValueError("unserializable object: {} ({})".format(value, type(value))) -def _gdumps(value, encoding): - """Generate fragments of value dumped as a tnetstring. +def _gdumps(value): + """ + Generate fragments of value dumped as a tnetstring. This is the naive dumping algorithm, implemented as a generator so that it's easy to pass to "".join() without building a new list. @@ -203,72 +192,63 @@ def _gdumps(value, encoding): measurably faster as it doesn't have to build intermediate strins. """ if value is None: - yield "0:~" + yield b'0:~' elif value is True: - yield "4:true!" + yield b'4:true!' elif value is False: - yield "5:false!" + yield b'5:false!' elif isinstance(value, six.integer_types): - data = str(value) - yield str(len(data)) - yield ":" + data = str(value).encode() + yield str(len(data)).encode() + yield b':' yield data - yield "#" - elif isinstance(value, (float,)): - data = repr(value) - yield str(len(data)) - yield ":" + yield b'#' + elif isinstance(value, float): + data = repr(value).encode() + yield str(len(data)).encode() + yield b':' yield data - yield "^" - elif isinstance(value, (str,)): - yield str(len(value)) - yield ":" + yield b'^' + elif isinstance(value, bytes): + yield str(len(value)).encode() + yield b':' yield value - yield "," - elif isinstance(value, (list, tuple,)): + yield b',' + elif isinstance(value, (list, tuple)): sub = [] for item in value: sub.extend(_gdumps(item)) - sub = "".join(sub) - yield str(len(sub)) - yield ":" + sub = b''.join(sub) + yield str(len(sub)).encode() + yield b':' yield sub - yield "]" + yield b']' elif isinstance(value, (dict,)): sub = [] - for (k, v) in six.iteritems(value): + for (k, v) in value.items(): sub.extend(_gdumps(k)) sub.extend(_gdumps(v)) - sub = "".join(sub) - yield str(len(sub)) - yield ":" + sub = b''.join(sub) + yield str(len(sub)).encode() + yield b':' yield sub - yield "}" - elif isinstance(value, (unicode,)): - if encoding is None: - raise ValueError("must specify encoding to dump unicode strings") - value = value.encode(encoding) - yield str(len(value)) - yield ":" - yield value - yield "," + yield b'}' else: raise ValueError("unserializable object") -def loads(string, encoding=None): - """loads(string,encoding=None) -> object - +def loads(string): + """ This function parses a tnetstring into a python object. """ # No point duplicating effort here. In the C-extension version, # loads() is measurably faster then pop() since it can avoid # the overhead of building a second string. - return pop(string, encoding)[0] + return pop(string)[0] -def load(file, encoding=None): - """load(file,encoding=None) -> object +def load(file_handle): + """load(file) -> object This function reads a tnetstring from a file and parses it into a python object. The file must support the read() method, and this @@ -276,70 +256,68 @@ def load(file, encoding=None): """ # Read the length prefix one char at a time. # Note that the netstring spec explicitly forbids padding zeros. - c = file.read(1) + c = file_handle.read(1) if not c.isdigit(): raise ValueError("not a tnetstring: missing or invalid length prefix") - datalen = ord(c) - ord("0") - c = file.read(1) + datalen = ord(c) - ord('0') + c = file_handle.read(1) if datalen != 0: while c.isdigit(): - datalen = (10 * datalen) + (ord(c) - ord("0")) + datalen = (10 * datalen) + (ord(c) - ord('0')) if datalen > 999999999: errmsg = "not a tnetstring: absurdly large length prefix" raise ValueError(errmsg) - c = file.read(1) - if c != ":": + c = file_handle.read(1) + if c != b':': raise ValueError("not a tnetstring: missing or invalid length prefix") # Now we can read and parse the payload. # This repeats the dispatch logic of pop() so we can avoid # re-constructing the outermost tnetstring. - data = file.read(datalen) + data = file_handle.read(datalen) if len(data) != datalen: raise ValueError("not a tnetstring: length prefix too big") - type = file.read(1) - if type == ",": - if encoding is not None: - return data.decode(encoding) + tns_type = file_handle.read(1) + if tns_type == b',': return data - if type == "#": + if tns_type == b'#': try: return int(data) except ValueError: raise ValueError("not a tnetstring: invalid integer literal") - if type == "^": + if tns_type == b'^': try: return float(data) except ValueError: raise ValueError("not a tnetstring: invalid float literal") - if type == "!": - if data == "true": + if tns_type == b'!': + if data == b'true': return True - elif data == "false": + elif data == b'false': return False else: raise ValueError("not a tnetstring: invalid boolean literal") - if type == "~": + if tns_type == b'~': if data: raise ValueError("not a tnetstring: invalid null literal") return None - if type == "]": + if tns_type == b']': l = [] while data: - (item, data) = pop(data, encoding) + item, data = pop(data) l.append(item) return l - if type == "}": + if tns_type == b'}': d = {} while data: - (key, data) = pop(data, encoding) - (val, data) = pop(data, encoding) + key, data = pop(data) + val, data = pop(data) d[key] = val return d raise ValueError("unknown type tag") -def pop(string, encoding=None): - """pop(string,encoding=None) -> (object, remain) +def pop(string): + """pop(string,encoding='utf_8') -> (object, remain) This function parses a tnetstring into a python object. It returns a tuple giving the parsed object and a string @@ -347,53 +325,51 @@ def pop(string, encoding=None): """ # Parse out data length, type and remaining string. try: - (dlen, rest) = string.split(":", 1) + dlen, rest = string.split(b':', 1) dlen = int(dlen) except ValueError: - raise ValueError("not a tnetstring: missing or invalid length prefix") + raise ValueError("not a tnetstring: missing or invalid length prefix: {}".format(string)) try: - (data, type, remain) = (rest[:dlen], rest[dlen], rest[dlen + 1:]) + data, tns_type, remain = rest[:dlen], rest[dlen:dlen + 1], rest[dlen + 1:] except IndexError: # This fires if len(rest) < dlen, meaning we don't need # to further validate that data is the right length. - raise ValueError("not a tnetstring: invalid length prefix") + raise ValueError("not a tnetstring: invalid length prefix: {}".format(dlen)) # Parse the data based on the type tag. - if type == ",": - if encoding is not None: - return (data.decode(encoding), remain) - return (data, remain) - if type == "#": + if tns_type == b',': + return data, remain + if tns_type == b'#': try: - return (int(data), remain) + return int(data), remain except ValueError: - raise ValueError("not a tnetstring: invalid integer literal") - if type == "^": + raise ValueError("not a tnetstring: invalid integer literal: {}".format(data)) + if tns_type == b'^': try: - return (float(data), remain) + return float(data), remain except ValueError: - raise ValueError("not a tnetstring: invalid float literal") - if type == "!": - if data == "true": - return (True, remain) - elif data == "false": - return (False, remain) + raise ValueError("not a tnetstring: invalid float literal: {}".format(data)) + if tns_type == b'!': + if data == b'true': + return True, remain + elif data == b'false': + return False, remain else: - raise ValueError("not a tnetstring: invalid boolean literal") - if type == "~": + raise ValueError("not a tnetstring: invalid boolean literal: {}".format(data)) + if tns_type == b'~': if data: raise ValueError("not a tnetstring: invalid null literal") - return (None, remain) - if type == "]": + return None, remain + if tns_type == b']': l = [] while data: - (item, data) = pop(data, encoding) + item, data = pop(data) l.append(item) return (l, remain) - if type == "}": + if tns_type == b'}': d = {} while data: - (key, data) = pop(data, encoding) - (val, data) = pop(data, encoding) + key, data = pop(data) + val, data = pop(data) d[key] = val - return (d, remain) - raise ValueError("unknown type tag") + return d, remain + raise ValueError("unknown type tag: {}".format(tns_type)) diff --git a/mitmproxy/flow/io.py b/mitmproxy/flow/io.py index cd3d9986..671ddf43 100644 --- a/mitmproxy/flow/io.py +++ b/mitmproxy/flow/io.py @@ -4,7 +4,7 @@ import os from mitmproxy import exceptions from mitmproxy import models -from mitmproxy import tnetstring +from mitmproxy.contrib import tnetstring from mitmproxy.flow import io_compat diff --git a/mitmproxy/platform/pf.py b/mitmproxy/platform/pf.py index 97a4c192..c0397d78 100644 --- a/mitmproxy/platform/pf.py +++ b/mitmproxy/platform/pf.py @@ -8,6 +8,7 @@ def lookup(address, port, s): Returns an (address, port) tuple, or None. """ + s = s.decode() spec = "%s:%s" % (address, port) for i in s.split("\n"): if "ESTABLISHED:ESTABLISHED" in i and spec in i: diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py index 9246fe04..32d881b0 100644 --- a/mitmproxy/proxy/config.py +++ b/mitmproxy/proxy/config.py @@ -199,7 +199,7 @@ def process_proxy_options(parser, options): password_manager = authentication.PassManHtpasswd( options.auth_htpasswd) except ValueError as v: - return parser.error(v.message) + return parser.error(v) authenticator = authentication.BasicProxyAuth(password_manager, "mitmproxy") else: authenticator = authentication.NullProxyAuth(None) diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js index 06353edf..799ad03e 100644 --- a/mitmproxy/web/static/app.js +++ b/mitmproxy/web/static/app.js @@ -116,12 +116,21 @@ "use strict";function calcVScroll(t){if(!t)return{start:0,end:0,paddingTop:0,paddingBottom:0};var e=t.itemCount,o=t.rowHeight,r=t.viewportTop,a=t.viewportHeight,i=t.itemHeights,l=r+a,n=0,c=0,d=0,p=0;if(i)for(var h=0,s=0;e>h;h++){var m=i[h]||o;r>=s&&h%2===0&&(d=s,n=h),l>=s?c=h+1:p+=m,s+=m}else n=-2&Math.max(0,Math.floor(r/o)-1),c=Math.min(e,n+Math.ceil(a/o)+2),d=Math.min(n,e)*o,p=Math.max(0,e-c)*o;return{start:n,end:c,paddingTop:d,paddingBottom:p}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.calcVScroll=calcVScroll; },{}],40:[function(require,module,exports){ +<<<<<<< HEAD +"use strict";function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function Connection(e,t){"/"===e[0]&&(e=location.origin.replace("http","ws")+e);var n=new WebSocket(e);return n.onopen=function(){t(webSocketActions.connected()),t(settingsActions.fetchSettings()),t(flowActions.fetchFlows()).then(function(){console.log("flows are loaded now"),_actions.ConnectionActions.open()}),t(eventLogActions.fetchLogEntries())},n.onmessage=function(e){var n=JSON.parse(e.data);switch(_dispatcher.AppDispatcher.dispatchServerAction(n),n.type){case eventLogActions.UPDATE_LOG:return t(eventLogActions.updateLogEntries(n));case flowActions.UPDATE_FLOWS:return t(flowActions.updateFlows(n));case settingsActions.UPDATE_SETTINGS:return t(settingsActions.handleWsMsg(n));default:console.warn("unknown message",n)}},n.onerror=function(){_actions.ConnectionActions.error(),t(eventLogActions.addLogEntry("WebSocket connection error."))},n.onclose=function(){_actions.ConnectionActions.close(),t(eventLogActions.addLogEntry("WebSocket connection closed.")),t(webSocketActions.disconnected())},n}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=Connection;var _actions=require("./actions.js"),_dispatcher=require("./dispatcher.js"),_websocket=require("./ducks/websocket"),webSocketActions=_interopRequireWildcard(_websocket),_eventLog=require("./ducks/eventLog"),eventLogActions=_interopRequireWildcard(_eventLog),_flows=require("./ducks/flows"),flowActions=_interopRequireWildcard(_flows),_settings=require("./ducks/settings"),settingsActions=_interopRequireWildcard(_settings); + +},{"./actions.js":1,"./dispatcher.js":41,"./ducks/eventLog":42,"./ducks/flows":43,"./ducks/settings":45,"./ducks/websocket":49}],41:[function(require,module,exports){ +"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.AppDispatcher=void 0;var _flux=require("flux"),_flux2=_interopRequireDefault(_flux),PayloadSources={VIEW:"view",SERVER:"server"},AppDispatcher=exports.AppDispatcher=new _flux2["default"].Dispatcher;AppDispatcher.dispatchViewAction=function(e){e.source=PayloadSources.VIEW,this.dispatch(e)},AppDispatcher.dispatchServerAction=function(e){e.source=PayloadSources.SERVER,this.dispatch(e)}; + +},{"flux":"flux"}],42:[function(require,module,exports){ +======= "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.AppDispatcher=void 0;var _flux=require("flux"),_flux2=_interopRequireDefault(_flux),PayloadSources={VIEW:"view",SERVER:"server"},AppDispatcher=exports.AppDispatcher=new _flux2["default"].Dispatcher;AppDispatcher.dispatchViewAction=function(e){e.source=PayloadSources.VIEW,this.dispatch(e)},AppDispatcher.dispatchServerAction=function(e){e.source=PayloadSources.SERVER,this.dispatch(e)}; },{"flux":"flux"}],41:[function(require,module,exports){ "use strict";function reduce(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){default:return e}}function init(){return function(e){e((0,_websocket.connect)()),e({type:INIT})}}function destruct(){return function(e){e((0,_websocket.disconnect)()),e({type:DESTRUCT})}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.INIT=void 0,exports.reduce=reduce,exports.init=init,exports.destruct=destruct;var _websocket=require("./websocket"),INIT=exports.INIT="APP_INIT",defaultState={}; },{"./websocket":49}],42:[function(require,module,exports){ +>>>>>>> jason/websocket "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _defineProperty(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1],r=function(){switch(t.type){case TOGGLE_FILTER:var r=_extends({},e.filter,_defineProperty({},t.filter,!e.filter[t.filter]));return{v:_extends({},e,{filter:r,filteredEvents:(0,_view.updateViewFilter)(e.events,function(e){return r[e.level]})})};case TOGGLE_VISIBILITY:return{v:_extends({},e,{visible:!e.visible})};case UPDATE_LOG:var i=reduceList(e.events,t);return{v:_extends({},e,{events:i,filteredEvents:(0,_view.updateViewList)(e.filteredEvents,e.events,i,t,function(t){return e.filter[t.level]})})};default:return{v:e}}}();return"object"===("undefined"==typeof r?"undefined":_typeof(r))?r.v:void 0}function toggleEventLogFilter(e){return{type:TOGGLE_FILTER,filter:e}}function toggleEventLogVisibility(){return{type:TOGGLE_VISIBILITY}}function addLogEntry(e){var t=arguments.length<=1||void 0===arguments[1]?"web":arguments[1];return addItem({message:e,level:t,id:"log-"+id++})}Object.defineProperty(exports,"__esModule",{value:!0}),exports.fetchLogEntries=exports.updateLogEntries=exports.UPDATE_LOG=void 0;var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},_extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e};exports["default"]=reducer,exports.toggleEventLogFilter=toggleEventLogFilter,exports.toggleEventLogVisibility=toggleEventLogVisibility,exports.addLogEntry=addLogEntry;var _list=require("./utils/list"),_list2=_interopRequireDefault(_list),_view=require("./utils/view"),TOGGLE_FILTER="TOGGLE_EVENTLOG_FILTER",TOGGLE_VISIBILITY="TOGGLE_EVENTLOG_VISIBILITY",UPDATE_LOG=exports.UPDATE_LOG="UPDATE_EVENTLOG",_makeList=(0,_list2["default"])(UPDATE_LOG,"/events"),reduceList=_makeList.reduceList,updateList=_makeList.updateList,fetchList=_makeList.fetchList,addItem=_makeList.addItem,defaultState={visible:!1,filter:{debug:!1,info:!0,web:!0},events:reduceList(),filteredEvents:[]},id=0;exports.updateLogEntries=updateList,exports.fetchLogEntries=fetchList; },{"./utils/list":47,"./utils/view":48}],43:[function(require,module,exports){ @@ -131,7 +140,7 @@ "use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _redux=require("redux"),_eventLog=require("./eventLog"),_eventLog2=_interopRequireDefault(_eventLog),_websocket=require("./websocket"),_websocket2=_interopRequireDefault(_websocket),_flows=require("./flows"),_flows2=_interopRequireDefault(_flows),_settings=require("./settings"),_settings2=_interopRequireDefault(_settings),_ui=require("./ui"),_ui2=_interopRequireDefault(_ui),rootReducer=(0,_redux.combineReducers)({eventLog:_eventLog2["default"],websocket:_websocket2["default"],flows:_flows2["default"],settings:_settings2["default"],ui:_ui2["default"]});exports["default"]=rootReducer; },{"./eventLog":42,"./flows":43,"./settings":45,"./ui":46,"./websocket":49,"redux":"redux"}],45:[function(require,module,exports){ -"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}function reducer(){var t=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],e=arguments[1];switch(e.type){case REQUEST_SETTINGS:return _extends({},t,{isFetching:!0});case RECEIVE_SETTINGS:var n={settings:e.settings,isFetching:!1,actionsDuringFetch:[]},r=!0,s=!1,i=void 0;try{for(var u,E=t.actionsDuringFetch[Symbol.iterator]();!(r=(u=E.next()).done);r=!0)e=u.value,n=reducer(n,e)}catch(S){s=!0,i=S}finally{try{!r&&E["return"]&&E["return"]()}finally{if(s)throw i}}return n;case UPDATE_SETTINGS:return t.isFetching?_extends({},t,{actionsDuringFetch:[].concat(_toConsumableArray(t.actionsDuringFetch),[e])}):_extends({},t,{settings:_extends({},t.settings,e.settings)});default:return t}}function updateSettings(t){return"update"===t.cmd?{type:UPDATE_SETTINGS,settings:t.data}:void console.error("unknown settings update",t)}function fetchSettings(){return function(t){return t({type:REQUEST_SETTINGS}),(0,_utils.fetchApi)("/settings").then(function(t){return t.json()}).then(function(e){return t({type:RECEIVE_SETTINGS,settings:e.data})})}}function updateSettings(t){return _utils.fetchApi.put("/settings",t),{type:SET_INTERCEPT}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UPDATE_SETTINGS=exports.RECEIVE_SETTINGS=exports.REQUEST_SETTINGS=void 0;var _extends=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};exports["default"]=reducer,exports.updateSettings=updateSettings,exports.fetchSettings=fetchSettings,exports.updateSettings=updateSettings;var _utils=require("../utils"),REQUEST_SETTINGS=exports.REQUEST_SETTINGS="REQUEST_SETTINGS",RECEIVE_SETTINGS=exports.RECEIVE_SETTINGS="RECEIVE_SETTINGS",UPDATE_SETTINGS=exports.UPDATE_SETTINGS="UPDATE_SETTINGS",defaultState={settings:{},isFetching:!1,actionsDuringFetch:[]}; +"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}function reducer(){var t=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],e=arguments[1];switch(e.type){case REQUEST_SETTINGS:return _extends({},t,{isFetching:!0});case RECEIVE_SETTINGS:var n={settings:e.settings,isFetching:!1,actionsDuringFetch:[]},r=!0,s=!1,i=void 0;try{for(var E,u=t.actionsDuringFetch[Symbol.iterator]();!(r=(E=u.next()).done);r=!0)e=E.value,n=reducer(n,e)}catch(a){s=!0,i=a}finally{try{!r&&u["return"]&&u["return"]()}finally{if(s)throw i}}return n;case UPDATE_SETTINGS:return t.isFetching?_extends({},t,{actionsDuringFetch:[].concat(_toConsumableArray(t.actionsDuringFetch),[e])}):_extends({},t,{settings:_extends({},t.settings,e.settings)});default:return t}}function handleWsMsg(t){return"update"===t.cmd?{type:UPDATE_SETTINGS,settings:t.data}:void console.error("unknown settings update",t)}function fetchSettings(){return function(t){return t({type:REQUEST_SETTINGS}),(0,_utils.fetchApi)("/settings").then(function(t){return t.json()}).then(function(e){return t({type:RECEIVE_SETTINGS,settings:e.data})})}}function updateSettings(t){return _utils.fetchApi.put("/settings",t),{type:SET_INTERCEPT}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UPDATE_SETTINGS=exports.RECEIVE_SETTINGS=exports.REQUEST_SETTINGS=void 0;var _extends=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};exports["default"]=reducer,exports.handleWsMsg=handleWsMsg,exports.fetchSettings=fetchSettings,exports.updateSettings=updateSettings;var _utils=require("../utils"),REQUEST_SETTINGS=exports.REQUEST_SETTINGS="REQUEST_SETTINGS",RECEIVE_SETTINGS=exports.RECEIVE_SETTINGS="RECEIVE_SETTINGS",UPDATE_SETTINGS=exports.UPDATE_SETTINGS="UPDATE_SETTINGS",defaultState={settings:{},isFetching:!1,actionsDuringFetch:[]}; },{"../utils":52}],46:[function(require,module,exports){ "use strict";function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){case SET_ACTIVE_MENU:return _extends({},e,{activeMenu:t.activeMenu});case _flows.SELECT_FLOW:var r=t.flowId&&!t.currentSelection,n=!t.flowId&&t.currentSelection;return r?_extends({},e,{activeMenu:"Flow"}):n&&"Flow"===e.activeMenu?_extends({},e,{activeMenu:"Start"}):e;default:return e}}function setActiveMenu(e){return{type:SET_ACTIVE_MENU,activeMenu:e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.SET_ACTIVE_MENU=void 0;var _extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e};exports["default"]=reducer,exports.setActiveMenu=setActiveMenu;var _flows=require("./flows"),SET_ACTIVE_MENU=exports.SET_ACTIVE_MENU="SET_ACTIVE_MENU",defaultState={activeMenu:"Start"}; diff --git a/netlib/http/http2/framereader.py b/netlib/http/http2/framereader.py index d45be646..eb9b069a 100644 --- a/netlib/http/http2/framereader.py +++ b/netlib/http/http2/framereader.py @@ -1,6 +1,7 @@ import codecs import hyperframe +from ...exceptions import HttpException def http2_read_raw_frame(rfile): @@ -8,7 +9,7 @@ def http2_read_raw_frame(rfile): length = int(codecs.encode(header[:3], 'hex_codec'), 16) if length == 4740180: - raise ValueError("Length field looks more like HTTP/1.1: %s" % rfile.peek(20)) + raise HttpException("Length field looks more like HTTP/1.1:\n{}".format(rfile.read(-1))) body = rfile.safe_read(length) return [header, body] diff --git a/test/mitmproxy/test_contrib_tnetstring.py b/test/mitmproxy/test_contrib_tnetstring.py new file mode 100644 index 00000000..17654ad9 --- /dev/null +++ b/test/mitmproxy/test_contrib_tnetstring.py @@ -0,0 +1,141 @@ +import unittest +import random +import math +import io +import struct + +from mitmproxy.contrib import tnetstring + +MAXINT = 2 ** (struct.Struct('i').size * 8 - 1) - 1 + +FORMAT_EXAMPLES = { + b'0:}': {}, + b'0:]': [], + b'51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00\x00,]}': + {b'hello': [12345678901, b'this', True, None, b'\x00\x00\x00\x00']}, + b'5:12345#': 12345, + b'12:this is cool,': b'this is cool', + b'0:,': b'', + b'0:~': None, + b'4:true!': True, + b'5:false!': False, + b'10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'24:5:12345#5:67890#5:xxxxx,]': [12345, 67890, b'xxxxx'], + b'18:3:0.1^3:0.2^3:0.3^]': [0.1, 0.2, 0.3], + b'243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]': [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[b'hello-there']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] # noqa +} + + +def get_random_object(random=random, depth=0): + """Generate a random serializable object.""" + # The probability of generating a scalar value increases as the depth increase. + # This ensures that we bottom out eventually. + if random.randint(depth, 10) <= 4: + what = random.randint(0, 1) + if what == 0: + n = random.randint(0, 10) + l = [] + for _ in range(n): + l.append(get_random_object(random, depth + 1)) + return l + if what == 1: + n = random.randint(0, 10) + d = {} + for _ in range(n): + n = random.randint(0, 100) + k = bytes([random.randint(32, 126) for _ in range(n)]) + d[k] = get_random_object(random, depth + 1) + return d + else: + what = random.randint(0, 4) + if what == 0: + return None + if what == 1: + return True + if what == 2: + return False + if what == 3: + if random.randint(0, 1) == 0: + return random.randint(0, MAXINT) + else: + return -1 * random.randint(0, MAXINT) + n = random.randint(0, 100) + return bytes([random.randint(32, 126) for _ in range(n)]) + + +class Test_Format(unittest.TestCase): + + def test_roundtrip_format_examples(self): + for data, expect in FORMAT_EXAMPLES.items(): + self.assertEqual(expect, tnetstring.loads(data)) + self.assertEqual( + expect, tnetstring.loads(tnetstring.dumps(expect))) + self.assertEqual((expect, b''), tnetstring.pop(data)) + + def test_roundtrip_format_random(self): + for _ in range(500): + v = get_random_object() + self.assertEqual(v, tnetstring.loads(tnetstring.dumps(v))) + self.assertEqual((v, b""), tnetstring.pop(tnetstring.dumps(v))) + + def test_unicode_handling(self): + with self.assertRaises(ValueError): + tnetstring.dumps(u"hello") + self.assertEqual(tnetstring.dumps(u"hello".encode()), b"5:hello,") + self.assertEqual(type(tnetstring.loads(b"5:hello,")), bytes) + + def test_roundtrip_format_unicode(self): + for _ in range(500): + v = get_random_object() + self.assertEqual(v, tnetstring.loads(tnetstring.dumps(v))) + self.assertEqual((v, b''), tnetstring.pop(tnetstring.dumps(v))) + + def test_roundtrip_big_integer(self): + i1 = math.factorial(30000) + s = tnetstring.dumps(i1) + i2 = tnetstring.loads(s) + self.assertEqual(i1, i2) + + +class Test_FileLoading(unittest.TestCase): + + def test_roundtrip_file_examples(self): + for data, expect in FORMAT_EXAMPLES.items(): + s = io.BytesIO() + s.write(data) + s.write(b'OK') + s.seek(0) + self.assertEqual(expect, tnetstring.load(s)) + self.assertEqual(b'OK', s.read()) + s = io.BytesIO() + tnetstring.dump(expect, s) + s.write(b'OK') + s.seek(0) + self.assertEqual(expect, tnetstring.load(s)) + self.assertEqual(b'OK', s.read()) + + def test_roundtrip_file_random(self): + for _ in range(500): + v = get_random_object() + s = io.BytesIO() + tnetstring.dump(v, s) + s.write(b'OK') + s.seek(0) + self.assertEqual(v, tnetstring.load(s)) + self.assertEqual(b'OK', s.read()) + + def test_error_on_absurd_lengths(self): + s = io.BytesIO() + s.write(b'1000000000:pwned!,') + s.seek(0) + with self.assertRaises(ValueError): + tnetstring.load(s) + self.assertEqual(s.read(1), b':') + + +def suite(): + loader = unittest.TestLoader() + suite = unittest.TestSuite() + suite.addTest(loader.loadTestsFromTestCase(Test_Format)) + suite.addTest(loader.loadTestsFromTestCase(Test_FileLoading)) + return suite diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index af8256c4..9eaab9aa 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -5,7 +5,8 @@ import mock import netlib.utils from netlib.http import Headers -from mitmproxy import filt, controller, tnetstring, flow +from mitmproxy import filt, controller, flow +from mitmproxy.contrib import tnetstring from mitmproxy.exceptions import FlowReadException, ScriptException from mitmproxy.models import Error from mitmproxy.models import Flow diff --git a/test/mitmproxy/test_protocol_http1.py b/test/mitmproxy/test_protocol_http1.py index e0a57b4e..cf7bd598 100644 --- a/test/mitmproxy/test_protocol_http1.py +++ b/test/mitmproxy/test_protocol_http1.py @@ -18,14 +18,14 @@ class TestInvalidRequests(tservers.HTTPProxyTest): p = self.pathoc() r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port)) assert r.status_code == 400 - assert "Invalid HTTP request form" in r.content + assert b"Invalid HTTP request form" in r.content def test_relative_request(self): p = self.pathoc_raw() p.connect() r = p.request("get:/p/200") assert r.status_code == 400 - assert "Invalid HTTP request form" in r.content + assert b"Invalid HTTP request form" in r.content class TestExpectHeader(tservers.HTTPProxyTest): @@ -43,8 +43,8 @@ class TestExpectHeader(tservers.HTTPProxyTest): ) client.wfile.flush() - assert client.rfile.readline() == "HTTP/1.1 100 Continue\r\n" - assert client.rfile.readline() == "\r\n" + assert client.rfile.readline() == b"HTTP/1.1 100 Continue\r\n" + assert client.rfile.readline() == b"\r\n" client.wfile.write(b"0123456789abcdef\r\n") client.wfile.flush() diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py index 23072260..932c8df2 100644 --- a/test/mitmproxy/test_protocol_http2.py +++ b/test/mitmproxy/test_protocol_http2.py @@ -13,6 +13,7 @@ from mitmproxy.cmdline import APP_HOST, APP_PORT import netlib from ..netlib import tservers as netlib_tservers +from netlib.exceptions import HttpException from netlib.http.http2 import framereader from . import tservers @@ -50,6 +51,9 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase): try: raw = b''.join(framereader.http2_read_raw_frame(self.rfile)) events = h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False except: break self.wfile.write(h2_conn.data_to_send()) @@ -60,9 +64,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase): if not self.server.handle_server_event(event, h2_conn, self.rfile, self.wfile): done = True break - except Exception as e: - print(repr(e)) - print(traceback.format_exc()) + except: done = True break @@ -200,9 +202,12 @@ class TestSimple(_Http2TestBase, _Http2ServerBase): done = False while not done: try: - events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile))) - except: - break + raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + events = h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False + client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() @@ -270,9 +275,12 @@ class TestWithBodies(_Http2TestBase, _Http2ServerBase): done = False while not done: try: - events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile))) - except: - break + raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + events = h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False + client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() @@ -364,6 +372,9 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase): try: raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False except: break client.wfile.write(h2_conn.data_to_send()) @@ -412,9 +423,12 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase): responses = 0 while not done: try: - events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile))) - except: - break + raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + events = h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False + client.wfile.write(h2_conn.data_to_send()) client.wfile.flush() @@ -481,6 +495,9 @@ class TestConnectionLost(_Http2TestBase, _Http2ServerBase): try: raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) h2_conn.receive_data(raw) + except HttpException: + print(traceback.format_exc()) + assert False except: break try: @@ -7,7 +7,7 @@ deps = codecov>=2.0.5 passenv = CI TRAVIS_BUILD_ID TRAVIS TRAVIS_BRANCH TRAVIS_JOB_NUMBER TRAVIS_PULL_REQUEST TRAVIS_JOB_ID TRAVIS_REPO_SLUG TRAVIS_COMMIT setenv = - PY3TESTS = test/netlib test/pathod/ test/mitmproxy/script test/mitmproxy/test_contentview.py test/mitmproxy/test_custom_contentview.py test/mitmproxy/test_app.py test/mitmproxy/test_controller.py test/mitmproxy/test_fuzzing.py test/mitmproxy/test_script.py test/mitmproxy/test_web_app.py test/mitmproxy/test_utils.py test/mitmproxy/test_stateobject.py test/mitmproxy/test_cmdline.py + PY3TESTS = test/netlib test/pathod/ test/mitmproxy/script test/mitmproxy/test_contentview.py test/mitmproxy/test_custom_contentview.py test/mitmproxy/test_app.py test/mitmproxy/test_controller.py test/mitmproxy/test_fuzzing.py test/mitmproxy/test_script.py test/mitmproxy/test_web_app.py test/mitmproxy/test_utils.py test/mitmproxy/test_stateobject.py test/mitmproxy/test_cmdline.py test/mitmproxy/test_contrib_tnetstring.py test/mitmproxy/test_proxy.py test/mitmproxy/test_protocol_http1.py test/mitmproxy/test_platform_pf.py [testenv:py27] commands = diff --git a/web/src/js/ducks/settings.js b/web/src/js/ducks/settings.js index 3e6c8366..bef7c0ff 100644 --- a/web/src/js/ducks/settings.js +++ b/web/src/js/ducks/settings.js @@ -47,7 +47,7 @@ export default function reducer(state = defaultState, action) { } } -export function updateSettings(event) { +export function handleWsMsg(event) { /* This action creator takes all WebSocket events */ if (event.cmd === 'update') { return { |