diff options
author | Maximilian Hils <git@maximilianhils.com> | 2016-05-12 11:03:57 -0600 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2016-05-12 11:03:57 -0600 |
commit | f1c922c652ec90f63cafed7f6b68d17f5229b58d (patch) | |
tree | 1091dc76bb1201dd334612a1d5bfa33f2918ad27 | |
parent | 518cc78454f9656be37e3153e2b508e56aa7ebf7 (diff) | |
download | mitmproxy-f1c922c652ec90f63cafed7f6b68d17f5229b58d.tar.gz mitmproxy-f1c922c652ec90f63cafed7f6b68d17f5229b58d.tar.bz2 mitmproxy-f1c922c652ec90f63cafed7f6b68d17f5229b58d.zip |
Sanitize Print (#1135)
* sanitize strings with shell control characters
* netlib: add utilities to safe-print bytes
* escaped str: add TODO for multi-byte chars
-rw-r--r-- | mitmproxy/dump.py | 18 | ||||
-rw-r--r-- | netlib/utils.py | 28 | ||||
-rw-r--r-- | test/netlib/test_utils.py | 15 |
3 files changed, 52 insertions, 9 deletions
diff --git a/mitmproxy/dump.py b/mitmproxy/dump.py index 0e0ccc62..aae397cd 100644 --- a/mitmproxy/dump.py +++ b/mitmproxy/dump.py @@ -5,7 +5,7 @@ import click import itertools from netlib import tcp -import netlib.utils +from netlib.utils import bytes_to_escaped_str, pretty_size from . import flow, filt, contentviews from .exceptions import ContentViewException, FlowReadException, ScriptException @@ -175,8 +175,8 @@ class DumpMaster(flow.FlowMaster): if self.o.flow_detail >= 2: headers = "\r\n".join( "{}: {}".format( - click.style(k, fg="blue", bold=True), - click.style(v, fg="blue")) + click.style(bytes_to_escaped_str(k), fg="blue", bold=True), + click.style(bytes_to_escaped_str(v), fg="blue")) for k, v in message.headers.fields ) self.echo(headers, indent=4) @@ -238,7 +238,7 @@ class DumpMaster(flow.FlowMaster): stickycookie = "" if flow.client_conn: - client = click.style(flow.client_conn.address.host, bold=True) + client = click.style(bytes_to_escaped_str(flow.client_conn.address.host), bold=True) else: client = click.style("[replay]", fg="yellow", bold=True) @@ -247,12 +247,12 @@ class DumpMaster(flow.FlowMaster): GET="green", DELETE="red" ).get(method.upper(), "magenta") - method = click.style(method, fg=method_color, bold=True) + method = click.style(bytes_to_escaped_str(method), fg=method_color, bold=True) if self.showhost: url = flow.request.pretty_url else: url = flow.request.url - url = click.style(url, bold=True) + url = click.style(bytes_to_escaped_str(url), bold=True) httpversion = "" if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"): @@ -282,12 +282,12 @@ class DumpMaster(flow.FlowMaster): elif 400 <= code < 600: code_color = "red" code = click.style(str(code), fg=code_color, bold=True, blink=(code == 418)) - reason = click.style(flow.response.reason, fg=code_color, bold=True) + reason = click.style(bytes_to_escaped_str(flow.response.reason), fg=code_color, bold=True) if flow.response.content is None: size = "(content missing)" else: - size = netlib.utils.pretty_size(len(flow.response.content)) + size = pretty_size(len(flow.response.content)) size = click.style(size, bold=True) arrows = click.style("<<", bold=True) @@ -349,4 +349,4 @@ class DumpMaster(flow.FlowMaster): if self.o.rfile and not self.o.keepserving: self.unload_scripts() # make sure to trigger script unload events. return - super(DumpMaster, self).run()
\ No newline at end of file + super(DumpMaster, self).run() diff --git a/netlib/utils.py b/netlib/utils.py index ebd192f3..be2701a0 100644 --- a/netlib/utils.py +++ b/netlib/utils.py @@ -431,3 +431,31 @@ def safe_subn(pattern, repl, target, *args, **kwargs): need a better solution that is aware of the actual content ecoding. """ return re.subn(str(pattern), str(repl), target, *args, **kwargs) + + +def bytes_to_escaped_str(data): + """ + Take bytes and return a safe string that can be displayed to the user. + """ + # TODO: We may want to support multi-byte characters without escaping them. + # One way to do would be calling .decode("utf8", "backslashreplace") first + # and then escaping UTF8 control chars (see clean_bin). + + if not isinstance(data, bytes): + raise ValueError("data must be bytes") + return repr(data).lstrip("b")[1:-1] + + +def escaped_str_to_bytes(data): + """ + Take an escaped string and return the unescaped bytes equivalent. + """ + if not isinstance(data, str): + raise ValueError("data must be str") + + if six.PY2: + return data.decode("string-escape") + + # This one is difficult - we use an undocumented Python API here + # as per http://stackoverflow.com/a/23151714/934719 + return codecs.escape_decode(data)[0] diff --git a/test/netlib/test_utils.py b/test/netlib/test_utils.py index be2a59fc..1d8f7b0f 100644 --- a/test/netlib/test_utils.py +++ b/test/netlib/test_utils.py @@ -1,3 +1,4 @@ +# coding=utf-8 from netlib import utils, tutils from netlib.http import Headers @@ -170,3 +171,17 @@ class TestSerializable: def test_safe_subn(): assert utils.safe_subn("foo", u"bar", "\xc2foo") + + +def test_bytes_to_escaped_str(): + assert utils.bytes_to_escaped_str(b"foo") == "foo" + assert utils.bytes_to_escaped_str(b"\b") == r"\x08" + assert utils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)" + assert utils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc" + + +def test_escaped_str_to_bytes(): + assert utils.escaped_str_to_bytes("foo") == b"foo" + assert utils.escaped_str_to_bytes(r"\x08") == b"\b" + assert utils.escaped_str_to_bytes(r"&!?=\\)") == br"&!?=\)" + assert utils.escaped_str_to_bytes(r"ΓΌ") == b'\xc3\xbc' |