Merge branch 'py38' of github.com:mhils/mitmproxy into py38

This commit is contained in:
Maximilian Hils 2020-12-06 14:07:53 +01:00
commit 5527c614b0
10 changed files with 263 additions and 12 deletions

View File

@ -12,6 +12,7 @@ Unreleased: mitmproxy next
* Updated imports and styles for web scanner helper addons. (@anneborcherding)
* Inform when underscore-formatted options are used in client arg. (@jrblixt)
* Binaries are now built with Python 3.9 (@mhils)
* Fixed the web UI showing blank page on clicking details tab when server address is missing (@samhita-sopho)
* --- TODO: add new PRs above this line ---

View File

@ -23,7 +23,7 @@ from mitmproxy import ctx
from mitmproxy import io
from mitmproxy import http
from mitmproxy import tcp
from mitmproxy.utils import human
from mitmproxy.utils import compat, human
# The underlying sorted list implementation expects the sort key to be stable
@ -460,8 +460,12 @@ class View(collections.abc.Sequence):
req = http.HTTPRequest.make(method.upper(), url)
except ValueError as e:
raise exceptions.CommandError("Invalid URL: %s" % e)
c = connections.ClientConnection.make_dummy(("", 0))
s = connections.ServerConnection.make_dummy((req.host, req.port))
if compat.new_proxy_core: # pragma: no cover
c = compat.Client(("", 0), ("", 0), req.timestamp_start - 0.0001)
s = compat.Server((req.host, req.port))
else: # pragma: no cover
c = connections.ClientConnection.make_dummy(("", 0))
s = connections.ServerConnection.make_dummy((req.host, req.port))
f = http.HTTPFlow(c, s)
f.request = req
f.request.headers["Host"] = req.host

View File

@ -86,6 +86,15 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
def __hash__(self):
return hash(self.id)
# Sans-io attributes.
state = 0
sockname = ("", 0)
error = None
tls = None
certificate_list = None
alpn_offers = None
cipher_list = None
_stateobject_attributes = dict(
id=str,
address=tuple,
@ -100,6 +109,14 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
alpn_proto_negotiated=bytes,
tls_version=str,
tls_extensions=typing.List[typing.Tuple[int, bytes]],
# sans-io exclusives
state=int,
sockname=tuple,
error=str,
tls=bool,
certificate_list=typing.List[certs.Cert],
alpn_offers=typing.List[bytes],
cipher_list=typing.List[str],
)
def send(self, message):
@ -130,6 +147,13 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
alpn_proto_negotiated=None,
tls_version=None,
tls_extensions=None,
state=0,
sockname=("", 0),
error=None,
tls=False,
certificate_list=[],
alpn_offers=[],
cipher_list=[],
))
def convert_to_tls(self, cert, *args, **kwargs):
@ -221,6 +245,16 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
def __hash__(self):
return hash(self.id)
# Sans-io attributes.
state = 0
error = None
tls = None
certificate_list = None
alpn_offers = None
cipher_name = None
cipher_list = None
via2 = None
_stateobject_attributes = dict(
id=str,
address=tuple,
@ -235,6 +269,15 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_tcp_setup=float,
timestamp_tls_setup=float,
timestamp_end=float,
# sans-io exclusives
state=int,
error=str,
tls=bool,
certificate_list=typing.List[certs.Cert],
alpn_offers=typing.List[bytes],
cipher_name=str,
cipher_list=typing.List[str],
via2=None,
)
@classmethod
@ -259,7 +302,15 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_tcp_setup=None,
timestamp_tls_setup=None,
timestamp_end=None,
via=None
via=None,
state=0,
error=None,
tls=False,
certificate_list=[],
alpn_offers=[],
cipher_name=None,
cipher_list=[],
via2=None,
))
def connect(self):

View File

@ -197,6 +197,39 @@ def convert_8_9(data):
return data
def convert_9_10(data):
data["version"] = 10
def conv_conn(conn):
conn["state"] = 0
conn["error"] = None
conn["tls"] = conn["tls_established"]
alpn = conn["alpn_proto_negotiated"]
conn["alpn_offers"] = [alpn] if alpn else None
cipher = conn["cipher_name"]
conn["cipher_list"] = [cipher] if cipher else None
def conv_cconn(conn):
conn["sockname"] = ("", 0)
cc = conn["clientcert"]
conn["certificate_list"] = [cc] if cc else None
conv_conn(conn)
def conv_sconn(conn):
crt = conn["cert"]
conn["certificate_list"] = [crt] if crt else None
conn["cipher_name"] = None
conn["via2"] = None
conv_conn(conn)
conv_cconn(data["client_conn"])
conv_sconn(data["server_conn"])
if data["server_conn"]["via"]:
conv_sconn(data["server_conn"]["via"])
return data
def _convert_dict_keys(o: Any) -> Any:
if isinstance(o, dict):
return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()}
@ -253,6 +286,7 @@ converters = {
6: convert_6_7,
7: convert_7_8,
8: convert_8_9,
9: convert_9_10,
}

View File

@ -165,6 +165,13 @@ def tclient_conn():
alpn_proto_negotiated=b"http/1.1",
tls_version="TLSv1.2",
tls_extensions=[(0x00, bytes.fromhex("000e00000b6578616d"))],
state=0,
sockname=("", 0),
error=None,
tls=False,
certificate_list=[],
alpn_offers=[],
cipher_list=[],
))
c.reply = controller.DummyReply()
c.rfile = io.BytesIO()
@ -191,6 +198,14 @@ def tserver_conn():
alpn_proto_negotiated=None,
tls_version="TLSv1.2",
via=None,
state=0,
error=None,
tls=False,
certificate_list=[],
alpn_offers=[],
cipher_name=None,
cipher_list=[],
via2=None,
))
c.reply = controller.DummyReply()
c.rfile = io.BytesIO()

View File

@ -12,7 +12,12 @@ def maybe_timestamp(base, attr):
if base is not None and getattr(base, attr):
return human.format_timestamp_with_milli(getattr(base, attr))
else:
return "active"
# in mitmdump we serialize before a connection is closed.
# loading those flows at a later point shouldn't display "active".
# We also use a ndash (and not a regular dash) so that it is sorted
# after other timestamps. We may need to revisit that in the future if it turns out
# to render ugly in consoles.
return ""
def flowdetails(state, flow: mitmproxy.flow.Flow):

View File

@ -8,7 +8,7 @@ MITMPROXY = "mitmproxy " + VERSION
# Serialization format version. This is displayed nowhere, it just needs to be incremented by one
# for each change in the file format.
FLOW_FORMAT_VERSION = 9
FLOW_FORMAT_VERSION = 10
def get_dev_version() -> str:

View File

@ -47,4 +47,24 @@ describe('Details Component', () => {
tree = details.toJSON()
expect(tree).toMatchSnapshot()
})
it('should render correctly when server address is missing', () => {
let tflowServerAddressNull = tflow
tflowServerAddressNull.server_conn.address = null
tflowServerAddressNull.server_conn.ip_address = null
tflowServerAddressNull.server_conn.alpn_proto_negotiated = null
tflowServerAddressNull.server_conn.sni = null
tflowServerAddressNull.server_conn.ssl_established = false
tflowServerAddressNull.server_conn.tls_version = null
tflowServerAddressNull.server_conn.timestamp_tcp_setup = null
tflowServerAddressNull.server_conn.timestamp_ssl_setup = null
tflowServerAddressNull.server_conn.timestamp_start = null
tflowServerAddressNull.server_conn.timestamp_end = null
let details = renderer.create(<Details flow={tflowServerAddressNull}/>),
tree = details.toJSON()
expect(tree).toMatchSnapshot()
})
})

View File

@ -237,6 +237,123 @@ exports[`Details Component should render correctly 1`] = `
</section>
`;
exports[`Details Component should render correctly when server address is missing 1`] = `
<section
className="detail"
>
<h4>
Client Connection
</h4>
<table
className="connection-table"
>
<tbody>
<tr>
<td>
Address:
</td>
<td>
127.0.0.1:22
</td>
</tr>
<tr>
<td>
<abbr
title="TLS Server Name Indication"
>
TLS SNI:
</abbr>
</td>
<td>
address
</td>
</tr>
<tr>
<td>
TLS version:
</td>
<td>
TLSv1.2
</td>
</tr>
<tr>
<td>
cipher name:
</td>
<td>
cipher
</td>
</tr>
<tr>
<td>
<abbr
title="ALPN protocol negotiated"
>
ALPN:
</abbr>
</td>
<td>
http/1.1
</td>
</tr>
</tbody>
</table>
<div />
<div>
<h4>
Timing
</h4>
<table
className="timing-table"
>
<tbody>
<tr />
<tr />
<tr />
<tr />
<tr />
<tr>
<td>
Client conn. established
:
</td>
<td>
1970-01-01 00:00:01.000
</td>
</tr>
<tr>
<td>
Client conn. SSL handshake
:
</td>
<td>
1970-01-01 00:00:02.000
</td>
</tr>
<tr>
<td>
First response byte
:
</td>
<td>
2017-05-21 12:38:32.481
</td>
</tr>
<tr>
<td>
Response complete
:
</td>
<td>
2017-05-21 12:38:32.481
</td>
</tr>
</tbody>
</table>
</div>
</section>
`;
exports[`TimeStamp Component should render correctly 1`] = `
<tr>
<td>

View File

@ -20,14 +20,14 @@ export function TimeStamp({ t, deltaTo, title }) {
)
}
export function ConnectionInfo({ conn }) {
export function ConnectionInfo({ conn }) {
return (
<table className="connection-table">
<tbody>
<tr key="address">
<tr key="address">
<td>Address:</td>
<td>{conn.address.join(':')}</td>
</tr>
</tr>
{conn.sni && (
<tr key="sni">
<td><abbr title="TLS Server Name Indication">TLS SNI:</abbr></td>
@ -136,9 +136,13 @@ export default function Details({ flow }) {
<h4>Client Connection</h4>
<ConnectionInfo conn={flow.client_conn}/>
<h4>Server Connection</h4>
<ConnectionInfo conn={flow.server_conn}/>
{flow.server_conn.address &&
[
<h4>Server Connection</h4>,
<ConnectionInfo conn={flow.server_conn}/>
]
}
<CertificateInfo flow={flow}/>
<Timing flow={flow}/>