Add WebSocketMessage.injected flag (#5105)

* Add WebSocketMessage.injected flag

* add flow format migration

Co-authored-by: Maximilian Hils <git@maximilianhils.com>
This commit is contained in:
Alexander Prinzhorn 2022-03-16 08:10:23 +01:00 committed by GitHub
parent cba67aa94c
commit 8e1adbc5df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 7 deletions

View File

@ -24,6 +24,7 @@
* Fix random connection stalls (#5040, @EndUser509) * Fix random connection stalls (#5040, @EndUser509)
* Add `n` new flow keybind to mitmweb (#5061, @ianklatzco) * Add `n` new flow keybind to mitmweb (#5061, @ianklatzco)
* Fix compatibility with BoringSSL (@pmoulton) * Fix compatibility with BoringSSL (@pmoulton)
* Added `WebSocketMessage.injected` flag (@Prinzhorn)
* Add example addon for saving streamed data to individual files (@EndUser509) * Add example addon for saving streamed data to individual files (@EndUser509)
* Change connection event hooks to be blocking. * Change connection event hooks to be blocking.
Processing will only resume once the event hook has finished. (@Prinzhorn) Processing will only resume once the event hook has finished. (@Prinzhorn)

View File

@ -319,6 +319,17 @@ def convert_13_14(data):
return data return data
def convert_14_15(data):
data["version"] = 15
if data.get("websocket", None):
# Add "injected" attribute.
data["websocket"]["messages"] = [
msg + [False]
for msg in data["websocket"]["messages"]
]
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()}
@ -380,6 +391,7 @@ converters = {
11: convert_11_12, 11: convert_11_12,
12: convert_12_13, 12: convert_12_13,
13: convert_13_14, 13: convert_13_14,
14: convert_14_15,
} }

View File

@ -124,8 +124,10 @@ class WebsocketLayer(layer.Layer):
if isinstance(event, events.ConnectionEvent): if isinstance(event, events.ConnectionEvent):
from_client = event.connection == self.context.client from_client = event.connection == self.context.client
injected = False
elif isinstance(event, WebSocketMessageInjected): elif isinstance(event, WebSocketMessageInjected):
from_client = event.message.from_client from_client = event.message.from_client
injected = True
else: else:
raise AssertionError(f"Unexpected event: {event}") raise AssertionError(f"Unexpected event: {event}")
@ -165,7 +167,7 @@ class WebsocketLayer(layer.Layer):
fragmentizer = Fragmentizer(src_ws.frame_buf, is_text) fragmentizer = Fragmentizer(src_ws.frame_buf, is_text)
src_ws.frame_buf = [b""] src_ws.frame_buf = [b""]
message = websocket.WebSocketMessage(typ, from_client, content) message = websocket.WebSocketMessage(typ, from_client, content, injected=injected)
self.flow.websocket.messages.append(message) self.flow.websocket.messages.append(message)
yield WebsocketMessageHook(self.flow) yield WebsocketMessageHook(self.flow)

View File

@ -7,7 +7,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 = 14 FLOW_FORMAT_VERSION = 15
def get_dev_version() -> str: def get_dev_version() -> str:

View File

@ -14,7 +14,7 @@ from mitmproxy import stateobject
from mitmproxy.coretypes import serializable from mitmproxy.coretypes import serializable
from wsproto.frame_protocol import Opcode from wsproto.frame_protocol import Opcode
WebSocketMessageState = Tuple[int, bool, bytes, float, bool] WebSocketMessageState = Tuple[int, bool, bytes, float, bool, bool]
class WebSocketMessage(serializable.Serializable): class WebSocketMessage(serializable.Serializable):
@ -47,6 +47,8 @@ class WebSocketMessage(serializable.Serializable):
"""Timestamp of when this message was received or created.""" """Timestamp of when this message was received or created."""
dropped: bool dropped: bool
"""True if the message has not been forwarded by mitmproxy, False otherwise.""" """True if the message has not been forwarded by mitmproxy, False otherwise."""
injected: bool
"""True if the message was injected and did not originate from a client/server, False otherwise"""
def __init__( def __init__(
self, self,
@ -54,23 +56,25 @@ class WebSocketMessage(serializable.Serializable):
from_client: bool, from_client: bool,
content: bytes, content: bytes,
timestamp: Optional[float] = None, timestamp: Optional[float] = None,
killed: bool = False, dropped: bool = False,
injected: bool = False,
) -> None: ) -> None:
self.from_client = from_client self.from_client = from_client
self.type = Opcode(type) self.type = Opcode(type)
self.content = content self.content = content
self.timestamp: float = timestamp or time.time() self.timestamp: float = timestamp or time.time()
self.dropped = killed self.dropped = dropped
self.injected = injected
@classmethod @classmethod
def from_state(cls, state: WebSocketMessageState): def from_state(cls, state: WebSocketMessageState):
return cls(*state) return cls(*state)
def get_state(self) -> WebSocketMessageState: def get_state(self) -> WebSocketMessageState:
return int(self.type), self.from_client, self.content, self.timestamp, self.dropped return int(self.type), self.from_client, self.content, self.timestamp, self.dropped, self.injected
def set_state(self, state: WebSocketMessageState) -> None: def set_state(self, state: WebSocketMessageState) -> None:
typ, self.from_client, self.content, self.timestamp, self.dropped = state typ, self.from_client, self.content, self.timestamp, self.dropped, self.injected = state
self.type = Opcode(typ) self.type = Opcode(typ)
def __repr__(self): def __repr__(self):

View File

@ -438,6 +438,7 @@ def test_inject_message(ws_testdata):
) )
assert flow.websocket.messages[-1].content == b"hello" assert flow.websocket.messages[-1].content == b"hello"
assert flow.websocket.messages[-1].from_client is False assert flow.websocket.messages[-1].from_client is False
assert flow.websocket.messages[-1].injected is True
assert ( assert (
playbook playbook
>> reply() >> reply()