mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-30 03:14:22 +00:00
Merge branch 'master' into py38
This commit is contained in:
commit
276b741482
@ -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 ---
|
||||||
|
|
||||||
|
@ -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,8 +460,12 @@ 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)
|
||||||
c = connections.ClientConnection.make_dummy(("", 0))
|
if compat.new_proxy_core: # pragma: no cover
|
||||||
s = connections.ServerConnection.make_dummy((req.host, req.port))
|
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 = http.HTTPFlow(c, s)
|
||||||
f.request = req
|
f.request = req
|
||||||
f.request.headers["Host"] = req.host
|
f.request.headers["Host"] = req.host
|
||||||
|
@ -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):
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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):
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -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>
|
||||||
|
@ -24,7 +24,7 @@ export function ConnectionInfo({ conn }) {
|
|||||||
return (
|
return (
|
||||||
<table className="connection-table">
|
<table className="connection-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr key="address">
|
<tr key="address">
|
||||||
<td>Address:</td>
|
<td>Address:</td>
|
||||||
<td>{conn.address.join(':')}</td>
|
<td>{conn.address.join(':')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -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 &&
|
||||||
<ConnectionInfo conn={flow.server_conn}/>
|
[
|
||||||
|
<h4>Server Connection</h4>,
|
||||||
|
<ConnectionInfo conn={flow.server_conn}/>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
<CertificateInfo flow={flow}/>
|
<CertificateInfo flow={flow}/>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user