aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2016-05-12 11:03:57 -0600
committerMaximilian Hils <git@maximilianhils.com>2016-05-12 11:03:57 -0600
commitf1c922c652ec90f63cafed7f6b68d17f5229b58d (patch)
tree1091dc76bb1201dd334612a1d5bfa33f2918ad27
parent518cc78454f9656be37e3153e2b508e56aa7ebf7 (diff)
downloadmitmproxy-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.py18
-rw-r--r--netlib/utils.py28
-rw-r--r--test/netlib/test_utils.py15
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'