diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | examples/simple/README.md | 32 | ||||
| -rw-r--r-- | mitmproxy/addons/cut.py | 43 | ||||
| -rw-r--r-- | mitmproxy/addons/export.py | 14 | ||||
| -rw-r--r-- | mitmproxy/contentviews/base.py | 5 | ||||
| -rw-r--r-- | mitmproxy/tools/console/consoleaddons.py | 17 | ||||
| -rw-r--r-- | mitmproxy/tools/console/statusbar.py | 3 | ||||
| -rw-r--r-- | setup.py | 20 | ||||
| -rw-r--r-- | test/mitmproxy/addons/test_cut.py | 19 | ||||
| -rw-r--r-- | test/mitmproxy/addons/test_export.py | 15 | ||||
| -rw-r--r-- | test/mitmproxy/contentviews/test_base.py | 18 |
11 files changed, 125 insertions, 62 deletions
@@ -11,6 +11,7 @@ MANIFEST .cache/ .tox*/ build/ +dist/ mitmproxy/contrib/kaitaistruct/*.ksy # UI diff --git a/examples/simple/README.md b/examples/simple/README.md index 5a7782db..d140a84c 100644 --- a/examples/simple/README.md +++ b/examples/simple/README.md @@ -1,18 +1,18 @@ ## Simple Examples -| Filename | Description | -|:-----------------------------|:---------------------------------------------------------------------------| -| add_header.py | Simple script that just adds a header to every request. | -| custom_contentview.py | Add a custom content view to the mitmproxy UI. | -| filter_flows.py | This script demonstrates how to use mitmproxy's filter pattern in scripts. | -| io_read_dumpfile.py | Read a dumpfile generated by mitmproxy. | -| io_write_dumpfile.py | Only write selected flows into a mitmproxy dumpfile. | -| log_events.py | Use mitmproxy's logging API. | -| modify_body_inject_iframe.py | Inject configurable iframe into pages. | -| modify_form.py | Modify HTTP form submissions. | -| modify_querystring.py | Modify HTTP query strings. | -| redirect_requests.py | Redirect a request to a different server. | -| script_arguments.py | Add arguments to a script. | -| send_reply_from_proxy.py | Send a HTTP response directly from the proxy. | -| upsidedownternet.py | Turn all images upside down. | -| wsgi_flask_app.py | Embed a WSGI app into mitmproxy. | +| Filename | Description | +| :----------------------------- | :--------------------------------------------------------------------------- | +| add_header.py | Simple script that just adds a header to every request. | +| custom_contentview.py | Add a custom content view to the mitmproxy UI. | +| custom_option.py | Add arguments to a script. | +| filter_flows.py | This script demonstrates how to use mitmproxy's filter pattern in scripts. | +| io_read_dumpfile.py | Read a dumpfile generated by mitmproxy. | +| io_write_dumpfile.py | Only write selected flows into a mitmproxy dumpfile. | +| log_events.py | Use mitmproxy's logging API. | +| modify_body_inject_iframe.py | Inject configurable iframe into pages. | +| modify_form.py | Modify HTTP form submissions. | +| modify_querystring.py | Modify HTTP query strings. | +| redirect_requests.py | Redirect a request to a different server. | +| send_reply_from_proxy.py | Send a HTTP response directly from the proxy. | +| upsidedownternet.py | Turn all images upside down. | +| wsgi_flask_app.py | Embed a WSGI app into mitmproxy. | diff --git a/mitmproxy/addons/cut.py b/mitmproxy/addons/cut.py index d684b8c7..c31465c7 100644 --- a/mitmproxy/addons/cut.py +++ b/mitmproxy/addons/cut.py @@ -88,26 +88,29 @@ class Cut: if path.startswith("+"): append = True path = mitmproxy.types.Path(path[1:]) - if len(cuts) == 1 and len(flows) == 1: - with open(path, "ab" if append else "wb") as fp: - if fp.tell() > 0: - # We're appending to a file that already exists and has content - fp.write(b"\n") - v = extract(cuts[0], flows[0]) - if isinstance(v, bytes): - fp.write(v) - else: - fp.write(v.encode("utf8")) - ctx.log.alert("Saved single cut.") - else: - with open(path, "a" if append else "w", newline='', encoding="utf8") as fp: - writer = csv.writer(fp) - for f in flows: - vals = [extract(c, f) for c in cuts] - writer.writerow( - [strutils.always_str(x) or "" for x in vals] # type: ignore - ) - ctx.log.alert("Saved %s cuts over %d flows as CSV." % (len(cuts), len(flows))) + try: + if len(cuts) == 1 and len(flows) == 1: + with open(path, "ab" if append else "wb") as fp: + if fp.tell() > 0: + # We're appending to a file that already exists and has content + fp.write(b"\n") + v = extract(cuts[0], flows[0]) + if isinstance(v, bytes): + fp.write(v) + else: + fp.write(v.encode("utf8")) + ctx.log.alert("Saved single cut.") + else: + with open(path, "a" if append else "w", newline='', encoding="utf8") as fp: + writer = csv.writer(fp) + for f in flows: + vals = [extract(c, f) for c in cuts] + writer.writerow( + [strutils.always_str(x) or "" for x in vals] # type: ignore + ) + ctx.log.alert("Saved %s cuts over %d flows as CSV." % (len(cuts), len(flows))) + except IOError as e: + ctx.log.error(str(e)) @command.command("cut.clip") def clip( diff --git a/mitmproxy/addons/export.py b/mitmproxy/addons/export.py index 0169f5b1..173296e4 100644 --- a/mitmproxy/addons/export.py +++ b/mitmproxy/addons/export.py @@ -1,5 +1,6 @@ import typing +from mitmproxy import ctx from mitmproxy import command from mitmproxy import flow from mitmproxy import exceptions @@ -58,11 +59,14 @@ class Export(): raise exceptions.CommandError("No such export format: %s" % fmt) func = formats[fmt] # type: typing.Any v = func(f) - with open(path, "wb") as fp: - if isinstance(v, bytes): - fp.write(v) - else: - fp.write(v.encode("utf-8")) + try: + with open(path, "wb") as fp: + if isinstance(v, bytes): + fp.write(v) + else: + fp.write(v.encode("utf-8")) + except IOError as e: + ctx.log.error(str(e)) @command.command("export.clip") def clip(self, fmt: str, f: flow.Flow) -> None: diff --git a/mitmproxy/contentviews/base.py b/mitmproxy/contentviews/base.py index bdab1e99..dbaa6ccc 100644 --- a/mitmproxy/contentviews/base.py +++ b/mitmproxy/contentviews/base.py @@ -49,8 +49,9 @@ def format_dict( ] entries, where key is padded to a uniform width. """ - max_key_len = max(len(k) for k in d.keys()) - max_key_len = min(max_key_len, KEY_MAX) + + max_key_len = max((len(k) for k in d.keys()), default=0) + max_key_len = min((max_key_len, KEY_MAX), default=0) for key, value in d.items(): if isinstance(key, bytes): key += b":" diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py index b10d27e4..5907fe95 100644 --- a/mitmproxy/tools/console/consoleaddons.py +++ b/mitmproxy/tools/console/consoleaddons.py @@ -465,13 +465,16 @@ class ConsoleAddon: Save data to file as a CSV. """ rows = self._grideditor().value - with open(path, "w", newline='', encoding="utf8") as fp: - writer = csv.writer(fp) - for row in rows: - writer.writerow( - [strutils.always_str(x) or "" for x in row] # type: ignore - ) - ctx.log.alert("Saved %s rows as CSV." % (len(rows))) + try: + with open(path, "w", newline='', encoding="utf8") as fp: + writer = csv.writer(fp) + for row in rows: + writer.writerow( + [strutils.always_str(x) or "" for x in row] # type: ignore + ) + ctx.log.alert("Saved %s rows as CSV." % (len(rows))) + except IOError as e: + ctx.log.error(str(e)) @command.command("console.grideditor.editor") def grideditor_editor(self) -> None: diff --git a/mitmproxy/tools/console/statusbar.py b/mitmproxy/tools/console/statusbar.py index 09cfd58a..ef32b195 100644 --- a/mitmproxy/tools/console/statusbar.py +++ b/mitmproxy/tools/console/statusbar.py @@ -140,9 +140,10 @@ class StatusBar(urwid.WidgetWrap): signals.flowlist_change.connect(self.sig_update) master.options.changed.connect(self.sig_update) master.view.focus.sig_change.connect(self.sig_update) + master.view.sig_view_add.connect(self.sig_update) self.redraw() - def sig_update(self, sender, updated=None): + def sig_update(self, sender, flow=None, updated=None): self.redraw() def keypress(self, *args, **kwargs): @@ -62,25 +62,25 @@ setup( # It is not considered best practice to use install_requires to pin dependencies to specific versions. install_requires=[ "blinker>=1.4, <1.5", - "brotlipy>=0.5.1, <0.8", + "brotlipy>=0.7.0,<0.8", "certifi>=2015.11.20.1", # no semver here - this should always be on the last release! "click>=6.2, <7", "cryptography>=2.1.4,<2.2", 'h11>=0.7.0,<0.8', - "h2>=3.0, <4", - "hyperframe>=5.0, <6", + "h2>=3.0.1,<4", + "hyperframe>=5.1.0,<6", "kaitaistruct>=0.7, <0.8", "ldap3>=2.4,<2.5", "passlib>=1.6.5, <1.8", "pyasn1>=0.3.1,<0.5", - "pyOpenSSL>=17.2,<17.6", + "pyOpenSSL>=17.5,<17.6", "pyparsing>=2.1.3, <2.3", "pyperclip>=1.5.22, <1.7", "requests>=2.9.1, <3", "ruamel.yaml>=0.13.2, <0.16", "sortedcontainers>=1.5.4, <1.6", "tornado>=4.3, <4.6", - "urwid>=1.3.1, <2.1", + "urwid>=2.0.1,<2.1", "wsproto>=0.11.0,<0.12.0", ], extras_require={ @@ -91,11 +91,11 @@ setup( "flake8>=3.5, <3.6", "Flask>=0.10.1, <0.13", "mypy>=0.560,<0.561", - "pytest-cov>=2.2.1, <3", - "pytest-faulthandler>=1.3.0, <2", - "pytest-timeout>=1.0.0, <2", - "pytest-xdist>=1.14, <2", - "pytest>=3.1, <4", + "pytest-cov>=2.5.1,<3", + "pytest-faulthandler>=1.3.1,<2", + "pytest-timeout>=1.2.1,<2", + "pytest-xdist>=1.22,<2", + "pytest>=3.3,<4", "rstcheck>=2.2, <4.0", "sphinx_rtd_theme>=0.1.9, <0.3", "sphinx-autobuild>=0.5.2, <0.8", diff --git a/test/mitmproxy/addons/test_cut.py b/test/mitmproxy/addons/test_cut.py index c444b8ee..266f9de7 100644 --- a/test/mitmproxy/addons/test_cut.py +++ b/test/mitmproxy/addons/test_cut.py @@ -112,6 +112,25 @@ def test_cut_save(tmpdir): assert qr(f).splitlines() == [b"GET,content", b"GET,content"] +@pytest.mark.parametrize("exception, log_message", [ + (PermissionError, "Permission denied"), + (IsADirectoryError, "Is a directory"), + (FileNotFoundError, "No such file or directory") +]) +def test_cut_save_open(exception, log_message, tmpdir): + f = str(tmpdir.join("path")) + v = view.View() + c = cut.Cut() + with taddons.context() as tctx: + tctx.master.addons.add(v, c) + v.add([tflow.tflow(resp=True)]) + + with mock.patch("mitmproxy.addons.cut.open") as m: + m.side_effect = exception(log_message) + tctx.command(c.save, "@all", "request.method", f) + assert tctx.master.has_log(log_message, level="error") + + def test_cut(): c = cut.Cut() with taddons.context(): diff --git a/test/mitmproxy/addons/test_export.py b/test/mitmproxy/addons/test_export.py index 233c62d5..4ceb0429 100644 --- a/test/mitmproxy/addons/test_export.py +++ b/test/mitmproxy/addons/test_export.py @@ -94,6 +94,21 @@ def test_export(tmpdir): os.unlink(f) +@pytest.mark.parametrize("exception, log_message", [ + (PermissionError, "Permission denied"), + (IsADirectoryError, "Is a directory"), + (FileNotFoundError, "No such file or directory") +]) +def test_export_open(exception, log_message, tmpdir): + f = str(tmpdir.join("path")) + e = export.Export() + with taddons.context() as tctx: + with mock.patch("mitmproxy.addons.export.open") as m: + m.side_effect = exception(log_message) + e.file("raw", tflow.tflow(resp=True), f) + assert tctx.master.has_log(log_message, level="error") + + def test_clip(tmpdir): e = export.Export() with taddons.context(): diff --git a/test/mitmproxy/contentviews/test_base.py b/test/mitmproxy/contentviews/test_base.py index 777ab4dd..c94d8be2 100644 --- a/test/mitmproxy/contentviews/test_base.py +++ b/test/mitmproxy/contentviews/test_base.py @@ -1 +1,17 @@ -# TODO: write tests +import pytest +from mitmproxy.contentviews import base + + +def test_format_dict(): + d = {"one": "two", "three": "four"} + f_d = base.format_dict(d) + assert next(f_d) + + d = {"adsfa": ""} + f_d = base.format_dict(d) + assert next(f_d) + + d = {} + f_d = base.format_dict(d) + with pytest.raises(StopIteration): + next(f_d) |
