aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml1
-rw-r--r--.travis.yml1
-rw-r--r--mitmproxy/addons/onboardingapp/static/images/favicon.icobin0 -> 5430 bytes
-rw-r--r--mitmproxy/addons/onboardingapp/static/images/mitmproxy-long.pngbin0 -> 123829 bytes
-rw-r--r--mitmproxy/addons/onboardingapp/static/mitmproxy.css5
-rw-r--r--mitmproxy/addons/onboardingapp/templates/index.html160
-rw-r--r--mitmproxy/addons/onboardingapp/templates/layout.html9
-rw-r--r--mitmproxy/addons/view.py2
-rw-r--r--mitmproxy/contentviews/base.py4
-rw-r--r--mitmproxy/options.py4
-rw-r--r--mitmproxy/test/tflow.py22
-rw-r--r--mitmproxy/test/tutils.py8
-rw-r--r--mitmproxy/tools/console/common.py106
-rw-r--r--mitmproxy/tools/console/defaultkeys.py6
-rw-r--r--mitmproxy/tools/console/flowdetailview.py96
-rw-r--r--mitmproxy/tools/console/flowview.py28
-rw-r--r--mitmproxy/tools/console/grideditor/col.py67
-rw-r--r--mitmproxy/tools/console/grideditor/col_text.py2
-rw-r--r--mitmproxy/tools/console/grideditor/col_viewany.py33
-rw-r--r--mitmproxy/tools/console/grideditor/editors.py25
-rw-r--r--mitmproxy/tools/console/help.py4
-rw-r--r--mitmproxy/tools/console/window.py45
-rw-r--r--mitmproxy/utils/arg_check.py4
-rw-r--r--mitmproxy/version.py4
-rw-r--r--release/.gitignore1
-rw-r--r--release/known_hosts.enc1
-rwxr-xr-xrelease/rtool.py6
-rw-r--r--setup.py2
-rw-r--r--test/mitmproxy/addons/test_cut.py15
-rw-r--r--test/mitmproxy/addons/test_view.py2
-rw-r--r--test/mitmproxy/net/http/test_response.py4
-rw-r--r--test/mitmproxy/test_version.py7
-rw-r--r--test/mitmproxy/tools/console/test_common.py28
-rw-r--r--test/mitmproxy/tools/console/test_master.py13
-rw-r--r--tox.ini2
-rw-r--r--web/src/js/filt/filt.js8
-rw-r--r--web/src/js/filt/filt.peg8
37 files changed, 424 insertions, 309 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 3ef985be..6891f1b3 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -79,6 +79,7 @@ deploy_script:
($Env:TOXENV -match "py35") -and
(($Env:APPVEYOR_REPO_BRANCH -In ("master", "pyinstaller")) -or ($Env:APPVEYOR_REPO_TAG -match "true"))
) {
+ tox -e rtool -- decrypt release\known_hosts.enc release\known_hosts
tox -e rtool -- upload-snapshot --bdist --wheel --installer
}
diff --git a/.travis.yml b/.travis.yml
index a29d0c75..b7504097 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -73,6 +73,7 @@ after_success:
- |
if [[ $BDIST == "1" && $TRAVIS_PULL_REQUEST == "false" && ($TRAVIS_BRANCH == "pyinstaller" || $TRAVIS_BRANCH == "master" || -n $TRAVIS_TAG) ]]
then
+ tox -e rtool -- decrypt release/known_hosts.enc release/known_hosts
tox -e rtool -- upload-snapshot --bdist
fi
diff --git a/mitmproxy/addons/onboardingapp/static/images/favicon.ico b/mitmproxy/addons/onboardingapp/static/images/favicon.ico
new file mode 100644
index 00000000..3c3b891c
--- /dev/null
+++ b/mitmproxy/addons/onboardingapp/static/images/favicon.ico
Binary files differ
diff --git a/mitmproxy/addons/onboardingapp/static/images/mitmproxy-long.png b/mitmproxy/addons/onboardingapp/static/images/mitmproxy-long.png
new file mode 100644
index 00000000..f9397d1e
--- /dev/null
+++ b/mitmproxy/addons/onboardingapp/static/images/mitmproxy-long.png
Binary files differ
diff --git a/mitmproxy/addons/onboardingapp/static/mitmproxy.css b/mitmproxy/addons/onboardingapp/static/mitmproxy.css
index b390976a..969bd62b 100644
--- a/mitmproxy/addons/onboardingapp/static/mitmproxy.css
+++ b/mitmproxy/addons/onboardingapp/static/mitmproxy.css
@@ -1,8 +1,6 @@
-
#certbank div {
text-align: center;
-
-
+ padding-top: 20px;
}
.fronttable {
@@ -40,7 +38,6 @@ section {
.masthead {
padding: 50px 0 60px;
text-align: center;
-
}
.header {
diff --git a/mitmproxy/addons/onboardingapp/templates/index.html b/mitmproxy/addons/onboardingapp/templates/index.html
index fc6213ea..38aa27ed 100644
--- a/mitmproxy/addons/onboardingapp/templates/index.html
+++ b/mitmproxy/addons/onboardingapp/templates/index.html
@@ -4,59 +4,135 @@
<script>
function changeTo(device) {
if (device == "apple") {
- var text = `<h3>Apple: How to install on macOS / OSX</h3>
- <ul>
- <li>Double-click the PEM file</li>
- <li>The "Keychain Access" applications opens</li>
- <li>Find the new certificate "mitmproxy" in the list</li>
- <li>Double-click the "mitmproxy" entry</li>
- <li>A dialog window openes up</li>
- <li>Change "Secure Socket Layer (SSL)" to "Always Trust"</li>
- <li>Close the dialog window (and enter your password if prompted)</li>
- <li>For iOS version 10.3 or up, you need to make sure mitmproxy is enabled in<br>
- Certificate Trust Settings, you can check it by going to<br>
- Settings > General > About > Certificate Trust Settings</li>
- <li>Done!</li>
- </ul>`;
+ var text = `<div class = "container">
+ <div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on macOS</h3>
+ <ul class="left">
+ <li>Double-click the PEM file</li>
+ <li>The "Keychain Access" applications opens</li>
+ <li>Find the new certificate "mitmproxy" in the list</li>
+ <li>Double-click the "mitmproxy" entry</li>
+ <li>A dialog window openes up</li>
+ <li>Change "Secure Socket Layer (SSL)" to "Always Trust"</li>
+ <li>Close the dialog window (and enter your password if prompted)</li>
+ <li>Done!</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on browsers</h3>
+ <ul>
+ <li>Safari on macOS uses the macOS keychain. So installing our CA in the system is enough.</li>
+ <li>Chrome on macOS uses the macOS keychain. So installing our CA in the system is enough.</li>
+ <li>Firefox on macOS has its own CA store and needs to be installed with Firefox-specific instructions that can be found <a href="https://wiki.mozilla.org/MozillaRootCertificate#Mozilla_Firefox">HERE</a> .</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on iOS v10.3</h3>
+ <ul>
+ <li>After certificate installation, open Settings</li>
+ <li>Navigate to General and then About</li>
+ <li>Select Certificate Trust Settings</li>
+ <li>Each root that has been installed via a profile will be listed below the heading Enable Full Trust For Root Certificates. Toggle mitmproxy on</li>
+ <li>Done!</li>
+ </div>
+ </div>
+ </div>`;
}
else if (device == "windows") {
- var text = `<h3>Windows: How to install on Windows</h3>
- <ul>
- <li>Double-click the P12 file</li>
- <li>Select Store Location for Current User and click Next</li>
- <li>Click Next</li>
- <li>Leave the Password column blank and click Next</li>
- <li>Select Place all certificates in the following store</li>
- <li>Click Browse and select Trusted Root Certification Authorities</li>
- <li>Click Next and then click Finish</li>
- <li>Click Yes if prompted for confirmation</li>
- <li>Done!</li>
- </ul>`;
+ var text = `<div class = "container">
+ <div class="row">
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Windows</h3>
+ <ul>
+ <li>Double-click the P12 file</li>
+ <li>Select Store Location for Current User and click Next</li>
+ <li>Click Next</li>
+ <li>Leave the Password column blank and click Next</li>
+ <li>Select Place all certificates in the following store</li>
+ <li>Click Browse and select Trusted Root Certification Authorities</li>
+ <li>Click Next and then click Finish</li>
+ <li>Click Yes if prompted for confirmation</li>
+ <li>Done!</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on browsers</h3>
+ <ul>
+ <li>Edge and IE use the Windows CA store. So installing our CA in the system is enough.</li>
+ <li>Chrome on Windows uses the Windows CA store. So installing our CA in the system is enough.</li>
+ <li>Firefox on Windows has its own CA store and needs to be installed with Firefox-specific instructions that can be found <a href="https://wiki.mozilla.org/MozillaRootCertificate#Mozilla_Firefox">HERE</a> .</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Windows (Automated)</h3>
+ <ul>
+ <li> >>> certutil.exe -importpfx Root mitmproxy-ca-cert.p12 </li>
+ <li> To know more click <a href="https://technet.microsoft.com/en-us/library/cc732443.aspx">HERE</a> </li>
+ </ul>
+ </div>
+ </div>
+ </div>`;
}
else if (device == "android") {
- var text = `<h3>Android: How to install on Android</h3>
- <ul>
- <li>Open your device's Settings app</li>
- <li>Under "Credential storage," tap Install from storage</li>
- <li>Under "Open from," tap where you saved the certificate</li>
- <li>Tap the file</li>
- <li>If prompted, enter the key store password and tap OK</li>
- <li>Type a name for the certificate</li>
- <li>Pick VPN and apps</li>
- <li>Tap OK</li>
- <li>Done!</li>
- </ul>`;
+ var text = `<div class = "container">
+ <div class="row">
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Android</h3>
+ <ul>
+ <li>Open your device's Settings app</li>
+ <li>Under "Credential storage," tap Install from storage</li>
+ <li>Under "Open from," tap where you saved the certificate</li>
+ <li>Tap the file</li>
+ <li>If prompted, enter the key store password and tap OK</li>
+ <li>Type a name for the certificate</li>
+ <li>Pick VPN and apps</li>
+ <li>Tap OK</li>
+ <li>Done!</li>
+ </ul>
+ </div>
+ </div>
+ </div>`;
}
else if (device == "asterisk") {
- var text = "";
+ var text = `<div class = "container">
+ <div class="row">
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Chrome on Debian/Ubuntu</h3>
+ <ul>
+ <li>Using Chrome, hit a page on your server via HTTPS and continue past the red warning page (assuming you haven't done this already)</li>
+ <li>Open up Chrome Settings > Show advanced settings > HTTPS/SSL > Manage Certificates</li>
+ <li>Click the Authorities tab and scroll down to find your certificate under the Organization Name that you gave to the certificate</li>
+ <li>Select it, click Edit (NOTE: in recent versions of Chrome, the button is now "Advanced" instead of "Edit"), check all the boxes and click OK. You may have to restart Chrome</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Chrome on Linux</h3>
+ <ul>
+ <li>Open Developer Tools > Security, and select View certificate</li>
+ <li>Click the Details tab > Export. Choose PKCS #7, single certificate as the file format</li>
+ <li>Then follow my original instructions to get to the Manage Certificates page. Click the Authorities tab > Import and choose the file to which you exported the certificate, and make sure to choose PKCS #7, single certificate as the file type</li>
+ <li>If prompted certification store, choose Trusted Root Certificate Authorities</li>
+ <li>Check all boxes and click OK. Restart Chrome</li>
+ </ul>
+ </div>
+ <div class="col-md-4">
+ <h3 class="text-center">How to install on Ubuntu (Manually)</h3>
+ <ul>
+ <li>Create a directory for extra CA certificates in /usr/share/ca-certificates: <div class="text-muted">$ sudo mkdir /usr/share/ca-certificates/extra<div></li>
+ <li>Copy the CA mitmproxy.crt file to this directory: <div class="text-muted">$ sudo cp mitmproxy.crt /usr/share/ca-certificates/extra/mitmproxy.crt<div></li>
+ <li>Let Ubuntu add the mitmproxy.crt file's path relative to /usr/share/ca-certificates to /etc/ca-certificates.conf: <div class="text-muted">$ sudo dpkg-reconfigure ca-certificates</div></li>
+ <li>In case of a .pem file on Ubuntu, it must first be converted to a .crt file: <div class="text-muted">$ openssl x509 -in foo.pem -inform PEM -out foo.crt</div></li>
+ </ul>
+ </div>
+ </div>
+ </div>`;
}
document.getElementById("dynamic").innerHTML = text;
}
</script>
-<center>
-<h2> Click to install your mitmproxy certificate: </h2>
-</center>
+<h2 class="text-center"> Click to install your mitmproxy certificate </h2>
<div id="certbank" class="row">
<div class="col-md-3">
<a onclick="changeTo('apple')" href="/cert/pem"><i class="fa fa-apple fa-5x"></i></a>
diff --git a/mitmproxy/addons/onboardingapp/templates/layout.html b/mitmproxy/addons/onboardingapp/templates/layout.html
index 8726a788..f6e1b286 100644
--- a/mitmproxy/addons/onboardingapp/templates/layout.html
+++ b/mitmproxy/addons/onboardingapp/templates/layout.html
@@ -12,20 +12,23 @@
<link href="/static/bootstrap.min.css" rel="stylesheet">
<link href="/static/mitmproxy.css" rel="stylesheet">
<link href="/static/fontawesome/css/font-awesome.min.css" rel="stylesheet">
+ <link rel="icon" href="/static/images/favicon.ico" type="image/x-icon"/>
</head>
<body>
<div class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
- <a class="navbar-brand" href="#">mitmproxy</a>
+ <a class="navbar-brand" href="#">
+ <img height="20px" src="static/images/mitmproxy-long.png"/>
+ </a>
</div>
</div>
</div>
<div class="container">
- {% block content %}
- {% end %}
+ {% block content %}
+ {% end %}
</div>
</body>
diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py
index 3a15fd3e..5fbefdb0 100644
--- a/mitmproxy/addons/view.py
+++ b/mitmproxy/addons/view.py
@@ -238,7 +238,7 @@ class View(collections.Sequence):
@command.command("view.order.options")
def order_options(self) -> typing.Sequence[str]:
"""
- Choices supported by the console_order option.
+ Choices supported by the view_order option.
"""
return list(sorted(self.orders.keys()))
diff --git a/mitmproxy/contentviews/base.py b/mitmproxy/contentviews/base.py
index 97740eea..bdab1e99 100644
--- a/mitmproxy/contentviews/base.py
+++ b/mitmproxy/contentviews/base.py
@@ -43,9 +43,11 @@ def format_dict(
) -> typing.Iterator[TViewLine]:
"""
Helper function that transforms the given dictionary into a list of
+ [
("key", key )
("value", value)
- tuples, where key is padded to a uniform width.
+ ]
+ 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)
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index ff7edf39..862380c5 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -44,8 +44,6 @@ class Options(optmanager.OptManager):
console_layout = None # type: str
console_layout_headers = None # type: bool
console_mouse = None # type: bool
- console_order = None # type: str
- console_order_reversed = None # type: bool
console_palette = None # type: str
console_palette_transparent = None # type: bool
default_contentview = None # type: str
@@ -98,6 +96,8 @@ class Options(optmanager.OptManager):
upstream_cert = None # type: bool
verbosity = None # type: str
view_filter = None # type: Optional[str]
+ view_order = None # type: str
+ view_order_reversed = None # type: bool
web_debug = None # type: bool
web_iface = None # type: str
web_open_browser = None # type: bool
diff --git a/mitmproxy/test/tflow.py b/mitmproxy/test/tflow.py
index 91747866..05d194d6 100644
--- a/mitmproxy/test/tflow.py
+++ b/mitmproxy/test/tflow.py
@@ -53,8 +53,8 @@ def twebsocketflow(client_conn=True, server_conn=True, messages=True, err=None,
sec_websocket_version="13",
sec_websocket_key="1234",
),
- timestamp_start=1,
- timestamp_end=2,
+ timestamp_start=946681200,
+ timestamp_end=946681201,
content=b''
)
resp = http.HTTPResponse(
@@ -66,8 +66,8 @@ def twebsocketflow(client_conn=True, server_conn=True, messages=True, err=None,
upgrade='websocket',
sec_websocket_accept=b'',
),
- timestamp_start=1,
- timestamp_end=2,
+ timestamp_start=946681202,
+ timestamp_end=946681203,
content=b'',
)
handshake_flow = http.HTTPFlow(client_conn, server_conn)
@@ -158,9 +158,9 @@ def tclient_conn():
clientcert=None,
mitmcert=None,
ssl_established=False,
- timestamp_start=1,
- timestamp_ssl_setup=2,
- timestamp_end=3,
+ timestamp_start=946681200,
+ timestamp_ssl_setup=946681201,
+ timestamp_end=946681206,
sni="address",
cipher_name="cipher",
alpn_proto_negotiated=b"http/1.1",
@@ -182,10 +182,10 @@ def tserver_conn():
source_address=("address", 22),
ip_address=("192.168.0.1", 22),
cert=None,
- timestamp_start=1,
- timestamp_tcp_setup=2,
- timestamp_ssl_setup=3,
- timestamp_end=4,
+ timestamp_start=946681202,
+ timestamp_tcp_setup=946681203,
+ timestamp_ssl_setup=946681204,
+ timestamp_end=946681205,
ssl_established=False,
sni="address",
alpn_proto_negotiated=None,
diff --git a/mitmproxy/test/tutils.py b/mitmproxy/test/tutils.py
index cd9f3b3f..d5b52bbe 100644
--- a/mitmproxy/test/tutils.py
+++ b/mitmproxy/test/tutils.py
@@ -31,8 +31,8 @@ def treq(**kwargs):
http_version=b"HTTP/1.1",
headers=http.Headers(((b"header", b"qvalue"), (b"content-length", b"7"))),
content=b"content",
- timestamp_start=1,
- timestamp_end=2,
+ timestamp_start=946681200,
+ timestamp_end=946681201,
)
default.update(kwargs)
return http.Request(**default)
@@ -49,8 +49,8 @@ def tresp(**kwargs):
reason=b"OK",
headers=http.Headers(((b"header-response", b"svalue"), (b"content-length", b"7"))),
content=b"message",
- timestamp_start=1,
- timestamp_end=2,
+ timestamp_start=946681202,
+ timestamp_end=946681203,
)
default.update(kwargs)
return http.Response(**default)
diff --git a/mitmproxy/tools/console/common.py b/mitmproxy/tools/console/common.py
index 47a30272..8a842799 100644
--- a/mitmproxy/tools/console/common.py
+++ b/mitmproxy/tools/console/common.py
@@ -1,9 +1,10 @@
import platform
+import typing
+from functools import lru_cache
import urwid
import urwid.util
-from functools import lru_cache
from mitmproxy.utils import human
# Detect Windows Subsystem for Linux
@@ -43,41 +44,48 @@ def highlight_key(str, key, textattr="text", keyattr="key"):
KEY_MAX = 30
-def format_keyvals(lst, key="key", val="text", indent=0):
+def format_keyvals(
+ entries: typing.List[typing.Tuple[str, typing.Union[None, str, urwid.Widget]]],
+ key_format: str = "key",
+ value_format: str = "text",
+ indent: int = 0
+) -> typing.List[urwid.Columns]:
"""
- Format a list of (key, value) tuples.
-
- If key is None, it's treated specially:
- - We assume a sub-value, and add an extra indent.
- - The value is treated as a pre-formatted list of directives.
+ Format a list of (key, value) tuples.
+
+ Args:
+ entries: The list to format. keys must be strings, values can also be None or urwid widgets.
+ The latter makes it possible to use the result of format_keyvals() as a value.
+ key_format: The display attribute for the key.
+ value_format: The display attribute for the value.
+ indent: Additional indent to apply.
"""
+ max_key_len = max((len(k) for k, v in entries if k is not None), default=0)
+ max_key_len = min(max_key_len, KEY_MAX)
+
+ if indent > 2:
+ indent -= 2 # We use dividechars=2 below, which already adds two empty spaces
+
ret = []
- if lst:
- maxk = min(max(len(i[0]) for i in lst if i and i[0]), KEY_MAX)
- for i, kv in enumerate(lst):
- if kv is None:
- ret.append(urwid.Text(""))
- else:
- if isinstance(kv[1], urwid.Widget):
- v = kv[1]
- elif kv[1] is None:
- v = urwid.Text("")
- else:
- v = urwid.Text([(val, kv[1])])
- ret.append(
- urwid.Columns(
- [
- ("fixed", indent, urwid.Text("")),
- (
- "fixed",
- maxk,
- urwid.Text([(key, kv[0] or "")])
- ),
- v
- ],
- dividechars = 2
- )
- )
+ for k, v in entries:
+ if v is None:
+ v = urwid.Text("")
+ elif not isinstance(v, urwid.Widget):
+ v = urwid.Text([(value_format, v)])
+ ret.append(
+ urwid.Columns(
+ [
+ ("fixed", indent, urwid.Text("")),
+ (
+ "fixed",
+ max_key_len,
+ urwid.Text([(key_format, k)])
+ ),
+ v
+ ],
+ dividechars=2
+ )
+ )
return ret
@@ -205,19 +213,15 @@ def format_flow(f, focus, extended=False, hostheader=False, max_url_len=False):
focus=focus,
extended=extended,
max_url_len=max_url_len,
-
- intercepted = f.intercepted,
- acked = acked,
-
- req_timestamp = f.request.timestamp_start,
- req_is_replay = f.request.is_replay,
- req_method = f.request.method,
- req_url = f.request.pretty_url if hostheader else f.request.url,
- req_http_version = f.request.http_version,
-
- err_msg = f.error.msg if f.error else None,
-
- marked = f.marked,
+ intercepted=f.intercepted,
+ acked=acked,
+ req_timestamp=f.request.timestamp_start,
+ req_is_replay=f.request.is_replay,
+ req_method=f.request.method,
+ req_url=f.request.pretty_url if hostheader else f.request.url,
+ req_http_version=f.request.http_version,
+ err_msg=f.error.msg if f.error else None,
+ marked=f.marked,
)
if f.response:
if f.response.raw_content:
@@ -232,11 +236,11 @@ def format_flow(f, focus, extended=False, hostheader=False, max_url_len=False):
roundtrip = human.pretty_duration(duration)
d.update(dict(
- resp_code = f.response.status_code,
- resp_reason = f.response.reason,
- resp_is_replay = f.response.is_replay,
- resp_clen = contentdesc,
- roundtrip = roundtrip,
+ resp_code=f.response.status_code,
+ resp_reason=f.response.reason,
+ resp_is_replay=f.response.is_replay,
+ resp_clen=contentdesc,
+ roundtrip=roundtrip,
))
t = f.response.headers.get("content-type")
diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py
index f8a3df2d..d01d9b7e 100644
--- a/mitmproxy/tools/console/defaultkeys.py
+++ b/mitmproxy/tools/console/defaultkeys.py
@@ -59,7 +59,7 @@ def map(km):
km.add("M", "view.marked.toggle", ["flowlist"], "Toggle viewing marked flows")
km.add(
"n",
- "console.command view.create get https://google.com",
+ "console.command view.create get https://example.com/",
["flowlist"],
"Create a new flow"
)
@@ -67,14 +67,14 @@ def map(km):
"o",
"""
console.choose.cmd Order view.order.options
- set console_order={choice}
+ set view_order={choice}
""",
["flowlist"],
"Set flow list order"
)
km.add("r", "replay.client @focus", ["flowlist", "flowview"], "Replay this flow")
km.add("S", "console.command replay.server ", ["flowlist"], "Start server replay")
- km.add("v", "set console_order_reversed=toggle", ["flowlist"], "Reverse flow list order")
+ km.add("v", "set view_order_reversed=toggle", ["flowlist"], "Reverse flow list order")
km.add("U", "flow.mark @all false", ["flowlist"], "Un-set all marks")
km.add("w", "console.command save.file @shown ", ["flowlist"], "Save listed flows to file")
km.add("V", "flow.revert @focus", ["flowlist", "flowview"], "Revert changes to this flow")
diff --git a/mitmproxy/tools/console/flowdetailview.py b/mitmproxy/tools/console/flowdetailview.py
index 28fe1fbc..32ac4b60 100644
--- a/mitmproxy/tools/console/flowdetailview.py
+++ b/mitmproxy/tools/console/flowdetailview.py
@@ -23,157 +23,157 @@ def flowdetails(state, flow: http.HTTPFlow):
metadata = flow.metadata
if metadata is not None and len(metadata) > 0:
- parts = [[str(k), repr(v)] for k, v in metadata.items()]
+ parts = [(str(k), repr(v)) for k, v in metadata.items()]
text.append(urwid.Text([("head", "Metadata:")]))
- text.extend(common.format_keyvals(parts, key="key", val="text", indent=4))
+ text.extend(common.format_keyvals(parts, indent=4))
if sc is not None and sc.ip_address:
text.append(urwid.Text([("head", "Server Connection:")]))
parts = [
- ["Address", human.format_address(sc.address)],
+ ("Address", human.format_address(sc.address)),
]
if sc.ip_address:
- parts.append(["Resolved Address", human.format_address(sc.ip_address)])
+ parts.append(("Resolved Address", human.format_address(sc.ip_address)))
if resp:
- parts.append(["HTTP Version", resp.http_version])
+ parts.append(("HTTP Version", resp.http_version))
if sc.alpn_proto_negotiated:
- parts.append(["ALPN", sc.alpn_proto_negotiated])
+ parts.append(("ALPN", sc.alpn_proto_negotiated))
text.extend(
- common.format_keyvals(parts, key="key", val="text", indent=4)
+ common.format_keyvals(parts, indent=4)
)
c = sc.cert
if c:
text.append(urwid.Text([("head", "Server Certificate:")]))
parts = [
- ["Type", "%s, %s bits" % c.keyinfo],
- ["SHA1 digest", c.digest("sha1")],
- ["Valid to", str(c.notafter)],
- ["Valid from", str(c.notbefore)],
- ["Serial", str(c.serial)],
- [
+ ("Type", "%s, %s bits" % c.keyinfo),
+ ("SHA1 digest", c.digest("sha1")),
+ ("Valid to", str(c.notafter)),
+ ("Valid from", str(c.notbefore)),
+ ("Serial", str(c.serial)),
+ (
"Subject",
urwid.BoxAdapter(
urwid.ListBox(
common.format_keyvals(
c.subject,
- key="highlight",
- val="text"
+ key_format="highlight"
)
),
len(c.subject)
)
- ],
- [
+ ),
+ (
"Issuer",
urwid.BoxAdapter(
urwid.ListBox(
common.format_keyvals(
- c.issuer, key="highlight", val="text"
+ c.issuer,
+ key_format="highlight"
)
),
len(c.issuer)
)
- ]
+ )
]
if c.altnames:
parts.append(
- [
+ (
"Alt names",
", ".join(strutils.bytes_to_escaped_str(x) for x in c.altnames)
- ]
+ )
)
text.extend(
- common.format_keyvals(parts, key="key", val="text", indent=4)
+ common.format_keyvals(parts, indent=4)
)
if cc is not None:
text.append(urwid.Text([("head", "Client Connection:")]))
parts = [
- ["Address", "{}:{}".format(cc.address[0], cc.address[1])],
+ ("Address", "{}:{}".format(cc.address[0], cc.address[1])),
]
if req:
- parts.append(["HTTP Version", req.http_version])
+ parts.append(("HTTP Version", req.http_version))
if cc.tls_version:
- parts.append(["TLS Version", cc.tls_version])
+ parts.append(("TLS Version", cc.tls_version))
if cc.sni:
- parts.append(["Server Name Indication", cc.sni])
+ parts.append(("Server Name Indication", cc.sni))
if cc.cipher_name:
- parts.append(["Cipher Name", cc.cipher_name])
+ parts.append(("Cipher Name", cc.cipher_name))
if cc.alpn_proto_negotiated:
- parts.append(["ALPN", cc.alpn_proto_negotiated])
+ parts.append(("ALPN", cc.alpn_proto_negotiated))
text.extend(
- common.format_keyvals(parts, key="key", val="text", indent=4)
+ common.format_keyvals(parts, indent=4)
)
parts = []
if cc is not None and cc.timestamp_start:
parts.append(
- [
+ (
"Client conn. established",
maybe_timestamp(cc, "timestamp_start")
- ]
+ )
)
if cc.ssl_established:
parts.append(
- [
+ (
"Client conn. TLS handshake",
maybe_timestamp(cc, "timestamp_ssl_setup")
- ]
+ )
)
if sc is not None and sc.timestamp_start:
parts.append(
- [
+ (
"Server conn. initiated",
maybe_timestamp(sc, "timestamp_start")
- ]
+ )
)
parts.append(
- [
+ (
"Server conn. TCP handshake",
maybe_timestamp(sc, "timestamp_tcp_setup")
- ]
+ )
)
if sc.ssl_established:
parts.append(
- [
+ (
"Server conn. TLS handshake",
maybe_timestamp(sc, "timestamp_ssl_setup")
- ]
+ )
)
if req is not None and req.timestamp_start:
parts.append(
- [
+ (
"First request byte",
maybe_timestamp(req, "timestamp_start")
- ]
+ )
)
parts.append(
- [
+ (
"Request complete",
maybe_timestamp(req, "timestamp_end")
- ]
+ )
)
if resp is not None and resp.timestamp_start:
parts.append(
- [
+ (
"First response byte",
maybe_timestamp(resp, "timestamp_start")
- ]
+ )
)
parts.append(
- [
+ (
"Response complete",
maybe_timestamp(resp, "timestamp_end")
- ]
+ )
)
if parts:
@@ -181,6 +181,6 @@ def flowdetails(state, flow: http.HTTPFlow):
parts = sorted(parts, key=lambda p: p[1])
text.append(urwid.Text([("head", "Timing:")]))
- text.extend(common.format_keyvals(parts, key="key", val="text", indent=4))
+ text.extend(common.format_keyvals(parts, indent=4))
return searchable.Searchable(text)
diff --git a/mitmproxy/tools/console/flowview.py b/mitmproxy/tools/console/flowview.py
index 05d2573f..a4b629d4 100644
--- a/mitmproxy/tools/console/flowview.py
+++ b/mitmproxy/tools/console/flowview.py
@@ -13,6 +13,7 @@ from mitmproxy.tools.console import flowdetailview
from mitmproxy.tools.console import searchable
from mitmproxy.tools.console import tabs
import mitmproxy.tools.console.master # noqa
+from mitmproxy.utils import strutils
class SearchError(Exception):
@@ -152,10 +153,31 @@ class FlowDetails(tabs.Tabs):
def conn_text(self, conn):
if conn:
+ hdrs = []
+ for k, v in conn.headers.fields:
+ # This will always force an ascii representation of headers. For example, if the server sends a
+ #
+ # X-Authors: Made with ❤ in Hamburg
+ #
+ # header, mitmproxy will display the following:
+ #
+ # X-Authors: Made with \xe2\x9d\xa4 in Hamburg.
+ #
+ # The alternative would be to just use the header's UTF-8 representation and maybe
+ # do `str.replace("\t", "\\t")` to exempt tabs from urwid's special characters escaping [1].
+ # That would in some terminals allow rendering UTF-8 characters, but the mapping
+ # wouldn't be bijective, i.e. a user couldn't distinguish "\\t" and "\t".
+ # Also, from a security perspective, a mitmproxy user couldn't be fooled by homoglyphs.
+ #
+ # 1) https://github.com/mitmproxy/mitmproxy/issues/1833
+ # https://github.com/urwid/urwid/blob/6608ee2c9932d264abd1171468d833b7a4082e13/urwid/display_common.py#L35-L36,
+
+ k = strutils.bytes_to_escaped_str(k) + ":"
+ v = strutils.bytes_to_escaped_str(v)
+ hdrs.append((k, v))
txt = common.format_keyvals(
- [(h + ":", v) for (h, v) in conn.headers.items(multi=True)],
- key = "header",
- val = "text"
+ hdrs,
+ key_format="header"
)
viewmode = self.master.commands.call("console.flowview.mode")
msg, body = self.content_view(viewmode, conn)
diff --git a/mitmproxy/tools/console/grideditor/col.py b/mitmproxy/tools/console/grideditor/col.py
deleted file mode 100644
index 3331f3e7..00000000
--- a/mitmproxy/tools/console/grideditor/col.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import typing
-
-import urwid
-
-from mitmproxy.tools.console import signals
-from mitmproxy.tools.console.grideditor import base
-from mitmproxy.utils import strutils
-
-strbytes = typing.Union[str, bytes]
-
-
-class Column(base.Column):
- def Display(self, data):
- return Display(data)
-
- def Edit(self, data):
- return Edit(data)
-
- def blank(self):
- return ""
-
- def keypress(self, key, editor):
- if key in ["m_select"]:
- editor.walker.start_edit()
- else:
- return key
-
-
-class Display(base.Cell):
- def __init__(self, data: strbytes) -> None:
- self.data = data
- if isinstance(data, bytes):
- escaped = strutils.bytes_to_escaped_str(data)
- else:
- escaped = data.encode()
- w = urwid.Text(escaped, wrap="any")
- super().__init__(w)
-
- def get_data(self) -> strbytes:
- return self.data
-
-
-class Edit(base.Cell):
- def __init__(self, data: strbytes) -> None:
- if isinstance(data, bytes):
- escaped = strutils.bytes_to_escaped_str(data)
- else:
- escaped = data.encode()
- self.type = type(data) # type: typing.Type
- w = urwid.Edit(edit_text=escaped, wrap="any", multiline=True)
- w = urwid.AttrWrap(w, "editfield")
- super().__init__(w)
-
- def get_data(self) -> strbytes:
- txt = self._w.get_text()[0].strip()
- try:
- if self.type == bytes:
- return strutils.escaped_str_to_bytes(txt)
- else:
- return txt.decode()
- except ValueError:
- signals.status_message.send(
- self,
- message="Invalid Python-style string encoding.",
- expire=1000
- )
- raise
diff --git a/mitmproxy/tools/console/grideditor/col_text.py b/mitmproxy/tools/console/grideditor/col_text.py
index f0ac06f8..32518670 100644
--- a/mitmproxy/tools/console/grideditor/col_text.py
+++ b/mitmproxy/tools/console/grideditor/col_text.py
@@ -21,7 +21,7 @@ class Column(col_bytes.Column):
return TEdit(data, self.encoding_args)
def blank(self):
- return u""
+ return ""
# This is the same for both edit and display.
diff --git a/mitmproxy/tools/console/grideditor/col_viewany.py b/mitmproxy/tools/console/grideditor/col_viewany.py
new file mode 100644
index 00000000..f5d35eee
--- /dev/null
+++ b/mitmproxy/tools/console/grideditor/col_viewany.py
@@ -0,0 +1,33 @@
+"""
+A display-only column that displays any data type.
+"""
+
+import typing
+
+import urwid
+from mitmproxy.tools.console.grideditor import base
+from mitmproxy.utils import strutils
+
+
+class Column(base.Column):
+ def Display(self, data):
+ return Display(data)
+
+ Edit = Display
+
+ def blank(self):
+ return ""
+
+
+class Display(base.Cell):
+ def __init__(self, data: typing.Any) -> None:
+ self.data = data
+ if isinstance(data, bytes):
+ data = strutils.bytes_to_escaped_str(data)
+ if not isinstance(data, str):
+ data = repr(data)
+ w = urwid.Text(data, wrap="any")
+ super().__init__(w)
+
+ def get_data(self) -> typing.Any:
+ return self.data
diff --git a/mitmproxy/tools/console/grideditor/editors.py b/mitmproxy/tools/console/grideditor/editors.py
index b5d16737..fbe48a1a 100644
--- a/mitmproxy/tools/console/grideditor/editors.py
+++ b/mitmproxy/tools/console/grideditor/editors.py
@@ -1,13 +1,14 @@
+import typing
from mitmproxy import exceptions
+from mitmproxy.net.http import Headers
from mitmproxy.tools.console import layoutwidget
+from mitmproxy.tools.console import signals
from mitmproxy.tools.console.grideditor import base
-from mitmproxy.tools.console.grideditor import col
-from mitmproxy.tools.console.grideditor import col_text
from mitmproxy.tools.console.grideditor import col_bytes
from mitmproxy.tools.console.grideditor import col_subgrid
-from mitmproxy.tools.console import signals
-from mitmproxy.net.http import Headers
+from mitmproxy.tools.console.grideditor import col_text
+from mitmproxy.tools.console.grideditor import col_viewany
class QueryEditor(base.FocusEditor):
@@ -67,7 +68,6 @@ class RequestFormEditor(base.FocusEditor):
class PathEditor(base.FocusEditor):
# TODO: Next row on enter?
-
title = "Edit Path Components"
columns = [
col_text.Column("Component"),
@@ -175,11 +175,22 @@ class OptionsEditor(base.GridEditor, layoutwidget.LayoutWidget):
class DataViewer(base.GridEditor, layoutwidget.LayoutWidget):
title = None # type: str
- def __init__(self, master, vals):
+ def __init__(
+ self,
+ master,
+ vals: typing.Union[
+ typing.List[typing.List[typing.Any]],
+ typing.List[typing.Any],
+ str,
+ ]) -> None:
if vals:
+ # Whatever vals is, make it a list of rows containing lists of column values.
+ if isinstance(vals, str):
+ vals = [vals]
if not isinstance(vals[0], list):
vals = [[i] for i in vals]
- self.columns = [col.Column("")] * len(vals[0])
+
+ self.columns = [col_viewany.Column("")] * len(vals[0])
super().__init__(master, vals, self.callback)
def callback(self, vals):
diff --git a/mitmproxy/tools/console/help.py b/mitmproxy/tools/console/help.py
index 439289f6..1b4b9ac6 100644
--- a/mitmproxy/tools/console/help.py
+++ b/mitmproxy/tools/console/help.py
@@ -76,7 +76,7 @@ class HelpView(tabs.Tabs, layoutwidget.LayoutWidget):
def filtexp(self):
text = []
- text.extend(common.format_keyvals(flowfilter.help, key="key", val="text", indent=4))
+ text.extend(common.format_keyvals(flowfilter.help, indent=4))
text.append(
urwid.Text(
[
@@ -96,7 +96,7 @@ class HelpView(tabs.Tabs, layoutwidget.LayoutWidget):
("!(~q & ~t \"text/html\")", "Anything but requests with a text/html content type."),
]
text.extend(
- common.format_keyvals(examples, key="key", val="text", indent=4)
+ common.format_keyvals(examples, indent=4)
)
return CListBox(text)
diff --git a/mitmproxy/tools/console/window.py b/mitmproxy/tools/console/window.py
index 87680f6e..c7bce7d3 100644
--- a/mitmproxy/tools/console/window.py
+++ b/mitmproxy/tools/console/window.py
@@ -16,7 +16,10 @@ from mitmproxy.tools.console import eventlog
class StackWidget(urwid.Frame):
- def __init__(self, widget, title, focus):
+ def __init__(self, window, widget, title, focus):
+ self.is_focused = focus
+ self.window = window
+
if title:
header = urwid.AttrWrap(
urwid.Text(title),
@@ -29,6 +32,11 @@ class StackWidget(urwid.Frame):
header=header
)
+ def mouse_event(self, size, event, button, col, row, focus):
+ if event == "mouse press" and button == 1 and not self.is_focused:
+ self.window.switch()
+ return super().mouse_event(size, event, button, col, row, focus)
+
def keypress(self, size, key):
# Make sure that we don't propagate cursor events outside of the widget.
# Otherwise, in a horizontal layout, urwid's Pile would change the focused widget
@@ -162,6 +170,7 @@ class Window(urwid.Frame):
else:
title = None
return StackWidget(
+ self,
widget,
title,
self.pane == idx
@@ -234,28 +243,34 @@ class Window(urwid.Frame):
self.view_changed()
self.focus_changed()
- def current(self, keyctx):
+ def stacks_sorted_by_focus(self):
"""
- Returns the active widget, but only the current focus or overlay has
- a matching key context.
+ Returns:
+ self.stacks, with the focused stack first.
"""
- t = self.focus_stack().top_widget()
- if t.keyctx == keyctx:
- return t
+ stacks = self.stacks.copy()
+ stacks.insert(0, stacks.pop(self.pane))
+ return stacks
- def current_window(self, keyctx):
+ def current(self, keyctx):
"""
- Returns the active window, ignoring overlays.
+ Returns the active widget with a matching key context, including overlays.
+ If multiple stacks have an active widget with a matching key context,
+ the currently focused stack is preferred.
"""
- t = self.focus_stack().top_window()
- if t.keyctx == keyctx:
- return t
+ for s in self.stacks_sorted_by_focus():
+ t = s.top_widget()
+ if t.keyctx == keyctx:
+ return t
- def any(self, keyctx):
+ def current_window(self, keyctx):
"""
- Returns the top window of either stack if they match the context.
+ Returns the active window with a matching key context, ignoring overlays.
+ If multiple stacks have an active widget with a matching key context,
+ the currently focused stack is preferred.
"""
- for t in [x.top_window() for x in self.stacks]:
+ for s in self.stacks_sorted_by_focus():
+ t = s.top_window()
if t.keyctx == keyctx:
return t
diff --git a/mitmproxy/utils/arg_check.py b/mitmproxy/utils/arg_check.py
index 73f7047c..873bef06 100644
--- a/mitmproxy/utils/arg_check.py
+++ b/mitmproxy/utils/arg_check.py
@@ -66,9 +66,9 @@ REPLACEMENTS = {
"--palette": "console_palette",
"--palette-transparent": "console_palette_transparent:",
"--follow": "console_focus_follow",
- "--order": "console_order",
+ "--order": "view_order",
"--no-mouse": "console_mouse",
- "--reverse": "console_order_reversed",
+ "--reverse": "view_order_reversed",
"--no-http2-priority": "http2_priority",
"--no-websocket": "websocket",
"--no-upstream-cert": "upstream_cert",
diff --git a/mitmproxy/version.py b/mitmproxy/version.py
index 3073c3d3..20a303e8 100644
--- a/mitmproxy/version.py
+++ b/mitmproxy/version.py
@@ -48,7 +48,7 @@ def get_version(dev: bool = False, build: bool = False, refresh: bool = False) -
# Add suffix for non-tagged releases
if tag_dist > 0:
- mitmproxy_version += ".dev{tag_dist:04}".format(tag_dist=tag_dist)
+ mitmproxy_version += ".dev{tag_dist}".format(tag_dist=tag_dist)
# The wheel build tag (we use the commit) must start with a digit, so we include "0x"
mitmproxy_version += "-0x{commit}".format(commit=commit)
@@ -60,5 +60,5 @@ def get_version(dev: bool = False, build: bool = False, refresh: bool = False) -
return mitmproxy_version
-if __name__ == "__main__":
+if __name__ == "__main__": # pragma: no cover
print(VERSION)
diff --git a/release/.gitignore b/release/.gitignore
index 2247d5f9..905eec6e 100644
--- a/release/.gitignore
+++ b/release/.gitignore
@@ -1,2 +1,3 @@
/build
/dist
+known_hosts
diff --git a/release/known_hosts.enc b/release/known_hosts.enc
new file mode 100644
index 00000000..585ee678
--- /dev/null
+++ b/release/known_hosts.enc
@@ -0,0 +1 @@
+gAAAAABaTif138dCP2-G3sAJxqh5icnwM0Zy7qh4HFCxeKQBMiVDr4nJyf9T82U677M_QKWRJmp_PsbnrshHXPylq0FuHwak7Yx7kdiLue6d85VQ7_kkMs-MlPM7_Xn54_zyuj1c0b3TVAuix2xHfFLdSd_mCxygFukLzf47OyYbno7lMY_-q0HZfVPz3PBZdk95wDcbYprmgEkVJZd64Tu_LG1JDDiz56LlqADMA4znMcSAoRmbVtHu-II09HMcX3TkmcqJsNv-IVHMs4fxW_DFsq9w5ARggL6ANMfhnFQPyMtgVHjGLkSjOMRshLkQUBVYx8yWEGaQOkP0doVtDS3fZ-MKc6OJC_NSs6gkm1rswjVsQsmgZGPIqjcVf9oCbFYcw0m-JrfB1irdsLoGzpfJaSGxveC7XqOd9ArBpCHFPVO-6ilu-E1qZelvL0HiplrFvJCMEev1U2YvznC1BWKpy81vJfH--64QKZ35yQBHMV_VoH-wi80EfWtz4ISvCMQWdjRAvhLHKHSYYhUSIgBZvCCQcPySdFpbDtwsQnzIqC8MQKG787w1FiYAwzdIHTWZuanENaPMALo0t0GgMSqPV4UUyw7dto8XSMqoUXOCuZNYjunVh7AzAKS7oMUYjDs38o92sWh5sZUpPfv2WYIiecTiQw4uPae7PdSwMhkI3WIOsSb8LURnG484vvgFc2jMpQThw-BHJx7tGYC0yFLouRH2O7m9x6xgiCiVA_u_BdOj_2PFufvOCaB9wno5Vo7C1hUERGWqoBZH0htBqxYci27hh8GFwkvj6OjFUyV_kk920cBYBDG4jS4bTrTzn_znJ9TNw2XkP98nA8cwlRYhDQG9FypJG0WwYkft3TVLSQ3Hq7t0nhvhSZvXts-3LR4S0_Hm0QgFUpUc-VHViinwK8_vQH3ZjvVlEWiXnzPdpAujjX_tQXsi13UE1Zp90wGeLrmdxGXq2K76Shytu8IwTcLNZ7m0jh8KmmfNwn6oZv-czqNmC4hh0OqRDFBrv3nnjDg2Vw74uKSZmXgtZlF_Zj9hPqxVWzj7lJUcyRqABBFbBH6lTSWPHLrzQ4eTex5dnOkXC8c3hRYDUt06xUkmDqaLK0rGFcfNXawZj1YqpUJW0qaNgbtBZRsSs92kblkETxCzcwxOfupmAhWdSkmCoxt019crodz3heREcyN2xcD9qHvdY49_FD3l3U6UhrWvmkDkzyLMd7VmRPWqlW0lkzrwav8e92leIq-xKFcvbnWgSdSCWWbXvIVJKcQ6hML3jX4oY7SoBs33U1Q0HfC7SuS5lqTASuRIOVCfIGeFfRwlIfEszbWg_WDoUjR6StaVq9tbtIC3mimWND82Z9r1NfUNxr8kFYIpH_6hbxhcW26HNBKr4wLxWFFE9l1QZORPM3s6z-lT4LzUPCkFExd_eYFx3X6yUJ3cHZhkQQzCLQqG7jQqvcMwDIfM-MXkJnttLfpBq0yiq0-mc-SEas5uy27iSJgbXnsV7G3YiKEelKW_uWP2bw-rQGG_AXMGNGF2A_aREsvGrEqPnyeHAxfS1bBcnqslpIzEwr9vyyJ5v_bxfHFQC4bwYMUvPGkjHVFc0Wrk7ss9P5Kd1bzh46H7OfroUbocmYBmHMMWEg-LvsG0RZil3KWh_CSyIIPETkDjuC3W7teT-wZK0zbTEaKCuz99Dg-tjzT6fP25ipoI70cX5R3KPwrLP3XNODRTsg_Jh7IpaXo9O3o8yLV9R6_rST_1KKJwzR2MMIXIvKaJQD9w2DZIaYx3tcVsXGCDnU4Tw2hhdB5wMCl3vHx83UHfjLxnc1tJ6ObpQUjwHM1SgHK8wLW409SVHphBbSjSilX5mIaR1S1SOTK53iFj5z6asZHY9JgDj11rng1uLKeirbrNZDnUme3NNYU-HX8Ret6oOesn3374uIHux1giqgR8VsPdkcMhvunx2oTP9R2fRBTSQ8sKNqDznRC8_qlQaRC94RnWO6VRNXVBT24cXq7HTepNp4f02UvUqQRyaIUmyn2S02mjLFECDm1iMxRhuacCKbI-WSKwJcm-7p39_Uh7m_nTl2VTseeQ-3NS6i-BiGmCHt3iDxR1Fkm31b50kWW3jCe6fcwMDeu3I_8mkQs_7mCFUjSDbvFUr2Y45a5guRlw63_KUW_mNN9td9hk8POWfxWEGhcZ9eRXh_eEdEaYZmviZdHi0I8pV52CqiEO-ZrnMw-w4rSpUQeRn9oKwp3GgB9j51RNlLqK9LTp-jfSGGi5GM-ab9sPgFCJLQ-HvHdGu0tQsF2wTD3qbJwNqapx28yNVfY6e8F2jOWjmP-zzFez8VNXcfoS--Ji_zI-VqsDx-cfz3DccWEjL6vjQOvaQTRwzhI7 \ No newline at end of file
diff --git a/release/rtool.py b/release/rtool.py
index 4a07885c..9050107e 100755
--- a/release/rtool.py
+++ b/release/rtool.py
@@ -299,11 +299,15 @@ def upload_snapshot(host, port, user, private_key, private_key_password, wheel,
"""
Upload snapshot to snapshot server
"""
+ cnopts = pysftp.CnOpts(
+ knownhosts=join(RELEASE_DIR, 'known_hosts')
+ )
with pysftp.Connection(host=host,
port=port,
username=user,
private_key=private_key,
- private_key_pass=private_key_password) as sftp:
+ private_key_pass=private_key_password,
+ cnopts=cnopts) as sftp:
dir_name = "snapshots/v{}".format(get_version())
sftp.makedirs(dir_name)
with sftp.cd(dir_name):
diff --git a/setup.py b/setup.py
index c66d1382..06961ca2 100644
--- a/setup.py
+++ b/setup.py
@@ -105,7 +105,7 @@ setup(
],
'examples': [
"beautifulsoup4>=4.4.1, <4.7",
- "Pillow>=4.3,<4.4",
+ "Pillow>=4.3,<5.1",
]
}
)
diff --git a/test/mitmproxy/addons/test_cut.py b/test/mitmproxy/addons/test_cut.py
index 71e699db..97577c60 100644
--- a/test/mitmproxy/addons/test_cut.py
+++ b/test/mitmproxy/addons/test_cut.py
@@ -23,8 +23,8 @@ def test_extract():
["request.text", "content"],
["request.content", b"content"],
["request.raw_content", b"content"],
- ["request.timestamp_start", "1"],
- ["request.timestamp_end", "2"],
+ ["request.timestamp_start", "946681200"],
+ ["request.timestamp_end", "946681201"],
["request.header[header]", "qvalue"],
["response.status_code", "200"],
@@ -33,8 +33,8 @@ def test_extract():
["response.content", b"message"],
["response.raw_content", b"message"],
["response.header[header-response]", "svalue"],
- ["response.timestamp_start", "1"],
- ["response.timestamp_end", "2"],
+ ["response.timestamp_start", "946681202"],
+ ["response.timestamp_end", "946681203"],
["client_conn.address.port", "22"],
["client_conn.address.host", "127.0.0.1"],
@@ -49,10 +49,9 @@ def test_extract():
["server_conn.sni", "address"],
["server_conn.ssl_established", "false"],
]
- for t in tests:
- ret = cut.extract(t[0], tf)
- if ret != t[1]:
- raise AssertionError("%s: Expected %s, got %s" % (t[0], t[1], ret))
+ for spec, expected in tests:
+ ret = cut.extract(spec, tf)
+ assert spec and ret == expected
with open(tutils.test_data.path("mitmproxy/net/data/text_cert"), "rb") as f:
d = f.read()
diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py
index 1c76eb21..6e4af367 100644
--- a/test/mitmproxy/addons/test_view.py
+++ b/test/mitmproxy/addons/test_view.py
@@ -41,7 +41,7 @@ def test_order_generators():
tf = tflow.tflow(resp=True)
rs = view.OrderRequestStart(v)
- assert rs.generate(tf) == 1
+ assert rs.generate(tf) == 946681200
rm = view.OrderRequestMethod(v)
assert rm.generate(tf) == tf.request.method
diff --git a/test/mitmproxy/net/http/test_response.py b/test/mitmproxy/net/http/test_response.py
index a77435c9..af35bab3 100644
--- a/test/mitmproxy/net/http/test_response.py
+++ b/test/mitmproxy/net/http/test_response.py
@@ -150,10 +150,10 @@ class TestResponseUtils:
n = time.time()
r.headers["date"] = email.utils.formatdate(n)
pre = r.headers["date"]
- r.refresh(1)
+ r.refresh(946681202)
assert pre == r.headers["date"]
- r.refresh(61)
+ r.refresh(946681262)
d = email.utils.parsedate_tz(r.headers["date"])
d = email.utils.mktime_tz(d)
# Weird that this is not exact...
diff --git a/test/mitmproxy/test_version.py b/test/mitmproxy/test_version.py
index f8d646dc..b5b33ba1 100644
--- a/test/mitmproxy/test_version.py
+++ b/test/mitmproxy/test_version.py
@@ -1,3 +1,4 @@
+import pathlib
import runpy
import subprocess
from unittest import mock
@@ -6,7 +7,9 @@ from mitmproxy import version
def test_version(capsys):
- runpy.run_module('mitmproxy.version', run_name='__main__')
+ here = pathlib.Path(__file__).absolute().parent
+ version_file = here / ".." / ".." / "mitmproxy" / "version.py"
+ runpy.run_path(str(version_file), run_name='__main__')
stdout, stderr = capsys.readouterr()
assert len(stdout) > 0
assert stdout.strip() == version.VERSION
@@ -27,7 +30,7 @@ def test_get_version():
assert version.get_version(True, True) == "3.0.0"
m.return_value = b"tag-2-cafecafe"
- assert version.get_version(True, True) == "3.0.0.dev0002-0xcafecaf"
+ assert version.get_version(True, True) == "3.0.0.dev2-0xcafecaf"
m.side_effect = subprocess.CalledProcessError(-1, 'git describe --tags --long')
assert version.get_version(True, True) == "3.0.0"
diff --git a/test/mitmproxy/tools/console/test_common.py b/test/mitmproxy/tools/console/test_common.py
index 3ab4fd67..72438c49 100644
--- a/test/mitmproxy/tools/console/test_common.py
+++ b/test/mitmproxy/tools/console/test_common.py
@@ -1,12 +1,34 @@
+import urwid
+
from mitmproxy.test import tflow
from mitmproxy.tools.console import common
-from ....conftest import skip_appveyor
-
-@skip_appveyor
def test_format_flow():
f = tflow.tflow(resp=True)
assert common.format_flow(f, True)
assert common.format_flow(f, True, hostheader=True)
assert common.format_flow(f, True, extended=True)
+
+
+def test_format_keyvals():
+ assert common.format_keyvals(
+ [
+ ("aa", "bb"),
+ ("cc", "dd"),
+ ("ee", None),
+ ]
+ )
+ wrapped = urwid.BoxAdapter(
+ urwid.ListBox(
+ urwid.SimpleFocusListWalker(
+ common.format_keyvals([("foo", "bar")])
+ )
+ ), 1
+ )
+ assert wrapped.render((30, ))
+ assert common.format_keyvals(
+ [
+ ("aa", wrapped)
+ ]
+ )
diff --git a/test/mitmproxy/tools/console/test_master.py b/test/mitmproxy/tools/console/test_master.py
index 3aa0dc54..9779a482 100644
--- a/test/mitmproxy/tools/console/test_master.py
+++ b/test/mitmproxy/tools/console/test_master.py
@@ -4,22 +4,9 @@ from mitmproxy import options
from mitmproxy.test import tflow
from mitmproxy.test import tutils
from mitmproxy.tools import console
-from mitmproxy.tools.console import common
from ... import tservers
-def test_format_keyvals():
- assert common.format_keyvals(
- [
- ("aa", "bb"),
- None,
- ("cc", "dd"),
- (None, "dd"),
- (None, "dd"),
- ]
- )
-
-
def test_options():
assert options.Options(replay_kill_extra=True)
diff --git a/tox.ini b/tox.ini
index 02d9a57b..17790b96 100644
--- a/tox.ini
+++ b/tox.ini
@@ -56,7 +56,7 @@ deps =
-rrequirements.txt
pyinstaller==3.3.1
twine==1.9.1
- pysftp==0.2.8
+ pysftp==0.2.9
commands =
mitmdump --version
diff --git a/web/src/js/filt/filt.js b/web/src/js/filt/filt.js
index 26058649..19a41af2 100644
--- a/web/src/js/filt/filt.js
+++ b/web/src/js/filt/filt.js
@@ -1929,7 +1929,7 @@ module.exports = (function() {
function body(regex){
regex = new RegExp(regex, "i");
function bodyFilter(flow){
- return True;
+ return true;
}
bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return bodyFilter;
@@ -1937,7 +1937,7 @@ module.exports = (function() {
function requestBody(regex){
regex = new RegExp(regex, "i");
function requestBodyFilter(flow){
- return True;
+ return true;
}
requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return requestBodyFilter;
@@ -1945,7 +1945,7 @@ module.exports = (function() {
function responseBody(regex){
regex = new RegExp(regex, "i");
function responseBodyFilter(flow){
- return True;
+ return true;
}
responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return responseBodyFilter;
@@ -2104,4 +2104,4 @@ module.exports = (function() {
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
-})(); \ No newline at end of file
+})();
diff --git a/web/src/js/filt/filt.peg b/web/src/js/filt/filt.peg
index 12959474..e4b151ad 100644
--- a/web/src/js/filt/filt.peg
+++ b/web/src/js/filt/filt.peg
@@ -1,4 +1,4 @@
-// PEG.js filter rules - see http://pegjs.majda.cz/online
+// PEG.js filter rules - see https://pegjs.org/
{
var flowutils = require("../flow/utils.js");
@@ -72,7 +72,7 @@ function responseCode(code){
function body(regex){
regex = new RegExp(regex, "i");
function bodyFilter(flow){
- return True;
+ return true;
}
bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return bodyFilter;
@@ -80,7 +80,7 @@ function body(regex){
function requestBody(regex){
regex = new RegExp(regex, "i");
function requestBodyFilter(flow){
- return True;
+ return true;
}
requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return requestBodyFilter;
@@ -88,7 +88,7 @@ function requestBody(regex){
function responseBody(regex){
regex = new RegExp(regex, "i");
function responseBodyFilter(flow){
- return True;
+ return true;
}
responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
return responseBodyFilter;