aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--examples/simple/README.md32
-rw-r--r--mitmproxy/addons/cut.py43
-rw-r--r--mitmproxy/addons/export.py14
-rw-r--r--mitmproxy/contentviews/base.py5
-rw-r--r--mitmproxy/tools/console/consoleaddons.py17
-rw-r--r--mitmproxy/tools/console/statusbar.py3
-rw-r--r--setup.py20
-rw-r--r--test/mitmproxy/addons/test_cut.py19
-rw-r--r--test/mitmproxy/addons/test_export.py15
-rw-r--r--test/mitmproxy/contentviews/test_base.py18
11 files changed, 125 insertions, 62 deletions
diff --git a/.gitignore b/.gitignore
index f88a2917..9fade1c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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):
diff --git a/setup.py b/setup.py
index c3c0ec09..315c15db 100644
--- a/setup.py
+++ b/setup.py
@@ -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)