Merge branch 'master' into py38

This commit is contained in:
Maximilian Hils 2020-12-06 14:05:14 +01:00 committed by GitHub
commit 276b741482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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) * Updated imports and styles for web scanner helper addons. (@anneborcherding)
* Inform when underscore-formatted options are used in client arg. (@jrblixt) * Inform when underscore-formatted options are used in client arg. (@jrblixt)
* Binaries are now built with Python 3.9 (@mhils) * 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 --- * --- TODO: add new PRs above this line ---

View File

@ -23,7 +23,7 @@ from mitmproxy import ctx
from mitmproxy import io from mitmproxy import io
from mitmproxy import http from mitmproxy import http
from mitmproxy import tcp 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 # The underlying sorted list implementation expects the sort key to be stable
@ -460,6 +460,10 @@ class View(collections.abc.Sequence):
req = http.HTTPRequest.make(method.upper(), url) req = http.HTTPRequest.make(method.upper(), url)
except ValueError as e: except ValueError as e:
raise exceptions.CommandError("Invalid URL: %s" % e) raise exceptions.CommandError("Invalid URL: %s" % e)
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)) c = connections.ClientConnection.make_dummy(("", 0))
s = connections.ServerConnection.make_dummy((req.host, req.port)) s = connections.ServerConnection.make_dummy((req.host, req.port))
f = http.HTTPFlow(c, s) f = http.HTTPFlow(c, s)

View File

@ -86,6 +86,15 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
def __hash__(self): def __hash__(self):
return hash(self.id) 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( _stateobject_attributes = dict(
id=str, id=str,
address=tuple, address=tuple,
@ -100,6 +109,14 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
alpn_proto_negotiated=bytes, alpn_proto_negotiated=bytes,
tls_version=str, tls_version=str,
tls_extensions=typing.List[typing.Tuple[int, bytes]], 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): def send(self, message):
@ -130,6 +147,13 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
alpn_proto_negotiated=None, alpn_proto_negotiated=None,
tls_version=None, tls_version=None,
tls_extensions=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): def convert_to_tls(self, cert, *args, **kwargs):
@ -221,6 +245,16 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
def __hash__(self): def __hash__(self):
return hash(self.id) 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( _stateobject_attributes = dict(
id=str, id=str,
address=tuple, address=tuple,
@ -235,6 +269,15 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_tcp_setup=float, timestamp_tcp_setup=float,
timestamp_tls_setup=float, timestamp_tls_setup=float,
timestamp_end=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 @classmethod
@ -259,7 +302,15 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_tcp_setup=None, timestamp_tcp_setup=None,
timestamp_tls_setup=None, timestamp_tls_setup=None,
timestamp_end=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): def connect(self):

View File

@ -197,6 +197,39 @@ def convert_8_9(data):
return 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: def _convert_dict_keys(o: Any) -> Any:
if isinstance(o, dict): if isinstance(o, dict):
return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()} return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()}
@ -253,6 +286,7 @@ converters = {
6: convert_6_7, 6: convert_6_7,
7: convert_7_8, 7: convert_7_8,
8: convert_8_9, 8: convert_8_9,
9: convert_9_10,
} }

View File

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

View File

@ -12,7 +12,12 @@ def maybe_timestamp(base, attr):
if base is not None and getattr(base, attr): if base is not None and getattr(base, attr):
return human.format_timestamp_with_milli(getattr(base, attr)) return human.format_timestamp_with_milli(getattr(base, attr))
else: 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): 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 # Serialization format version. This is displayed nowhere, it just needs to be incremented by one
# for each change in the file format. # for each change in the file format.
FLOW_FORMAT_VERSION = 9 FLOW_FORMAT_VERSION = 10
def get_dev_version() -> str: def get_dev_version() -> str:

View File

@ -47,4 +47,24 @@ describe('Details Component', () => {
tree = details.toJSON() tree = details.toJSON()
expect(tree).toMatchSnapshot() 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> </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`] = ` exports[`TimeStamp Component should render correctly 1`] = `
<tr> <tr>
<td> <td>

View File

@ -136,8 +136,12 @@ export default function Details({ flow }) {
<h4>Client Connection</h4> <h4>Client Connection</h4>
<ConnectionInfo conn={flow.client_conn}/> <ConnectionInfo conn={flow.client_conn}/>
<h4>Server Connection</h4> {flow.server_conn.address &&
[
<h4>Server Connection</h4>,
<ConnectionInfo conn={flow.server_conn}/> <ConnectionInfo conn={flow.server_conn}/>
]
}
<CertificateInfo flow={flow}/> <CertificateInfo flow={flow}/>