mitmproxy.contentviews
mitmproxy includes a set of content views which can be used to format/decode/highlight/reencode data. While they are mostly used for HTTP message bodies, the may be used in other contexts, e.g. to decode WebSocket messages.
See "Custom Contentviews" in the mitmproxy documentation for examples.
1""" 2mitmproxy includes a set of content views which can be used to 3format/decode/highlight/reencode data. While they are mostly used for HTTP message 4bodies, the may be used in other contexts, e.g. to decode WebSocket messages. 5 6See "Custom Contentviews" in the mitmproxy documentation for examples. 7""" 8 9import logging 10import sys 11import traceback 12import warnings 13from dataclasses import dataclass 14 15from ..addonmanager import cut_traceback 16from ._api import Contentview 17from ._api import InteractiveContentview 18from ._api import Metadata 19from ._api import SyntaxHighlight 20from ._compat import get # noqa: F401 21from ._compat import LegacyContentview 22from ._compat import remove # noqa: F401 23from ._registry import ContentviewRegistry 24from ._utils import ContentviewMessage 25from ._utils import get_data 26from ._utils import make_metadata 27from ._view_css import css 28from ._view_dns import dns 29from ._view_graphql import graphql 30from ._view_http3 import http3 31from ._view_image import image 32from ._view_javascript import javascript 33from ._view_json import json_view 34from ._view_mqtt import mqtt 35from ._view_multipart import multipart 36from ._view_query import query 37from ._view_raw import raw 38from ._view_socketio import socket_io 39from ._view_urlencoded import urlencoded 40from ._view_wbxml import wbxml 41from ._view_xml_html import xml_html 42from .base import View 43import mitmproxy_rs.contentviews 44from mitmproxy import flow 45from mitmproxy.utils import strutils 46 47logger = logging.getLogger(__name__) 48 49 50@dataclass 51class ContentviewResult: 52 text: str 53 syntax_highlight: SyntaxHighlight 54 view_name: str | None 55 description: str 56 57 58registry = ContentviewRegistry() 59 60 61def prettify_message( 62 message: ContentviewMessage, 63 flow: flow.Flow, 64 view_name: str = "auto", 65 registry: ContentviewRegistry = registry, 66) -> ContentviewResult: 67 data, enc = get_data(message) 68 if data is None: 69 return ContentviewResult( 70 text="Content is missing.", 71 syntax_highlight="error", 72 description="", 73 view_name=None, 74 ) 75 76 # Determine the correct view 77 metadata = make_metadata(message, flow) 78 view = registry.get_view(data, metadata, view_name) 79 80 # Finally, we can pretty-print! 81 try: 82 ret = ContentviewResult( 83 text=view.prettify(data, metadata), 84 syntax_highlight=view.syntax_highlight, 85 view_name=view.name, 86 description=enc, 87 ) 88 except Exception as e: 89 logger.debug(f"Contentview {view.name!r} failed: {e}", exc_info=True) 90 if view_name == "auto": 91 # If the contentview was chosen as the best matching one, fall back to raw. 92 ret = ContentviewResult( 93 text=raw.prettify(data, metadata), 94 syntax_highlight=raw.syntax_highlight, 95 view_name=raw.name, 96 description=f"{enc}[failed to parse as {view.name}]", 97 ) 98 else: 99 # Cut the exception traceback for display. 100 exc, value, tb = sys.exc_info() 101 tb_cut = cut_traceback(tb, "prettify_message") 102 if ( 103 tb_cut == tb 104 ): # If there are no extra frames, just skip displaying the traceback. 105 tb_cut = None 106 # If the contentview has been set explicitly, we display a hard error. 107 err = "".join(traceback.format_exception(exc, value=value, tb=tb_cut)) 108 ret = ContentviewResult( 109 text=f"Couldn't parse as {view.name}:\n{err}", 110 syntax_highlight="error", 111 view_name=view.name, 112 description=enc, 113 ) 114 115 ret.text = strutils.escape_control_characters(ret.text) 116 return ret 117 118 119def reencode_message( 120 prettified: str, 121 message: ContentviewMessage, 122 flow: flow.Flow, 123 view_name: str, 124) -> bytes: 125 metadata = make_metadata(message, flow) 126 view = registry[view_name.lower()] 127 if not isinstance(view, InteractiveContentview): 128 raise ValueError(f"Contentview {view.name} is not interactive.") 129 return view.reencode(prettified, metadata) 130 131 132_views: list[Contentview] = [ 133 css, 134 dns, 135 graphql, 136 http3, 137 image, 138 javascript, 139 json_view, 140 mqtt, 141 multipart, 142 query, 143 raw, 144 socket_io, 145 urlencoded, 146 wbxml, 147 xml_html, 148] 149for view in _views: 150 registry.register(view) 151for name in mitmproxy_rs.contentviews.__all__: 152 if name.startswith("_"): 153 continue 154 cv = getattr(mitmproxy_rs.contentviews, name) 155 if isinstance(cv, Contentview) and not isinstance(cv, type): 156 registry.register(cv) 157 158 159def add(contentview: Contentview | type[Contentview]) -> None: 160 """ 161 Register a contentview for use in mitmproxy. 162 163 You may pass a `Contentview` instance or the class itself. 164 When passing the class, its constructor will be invoked with no arguments. 165 """ 166 if isinstance(contentview, View): 167 warnings.warn( 168 f"`mitmproxy.contentviews.View` is deprecated since mitmproxy 12, " 169 f"migrate {contentview.__class__.__name__} to `mitmproxy.contentviews.Contentview` instead.", 170 stacklevel=2, 171 ) 172 contentview = LegacyContentview(contentview) 173 registry.register(contentview) 174 175 176# hack: docstring where pdoc finds it. 177SyntaxHighlight = SyntaxHighlight 178""" 179Syntax highlighting formats currently supported by mitmproxy. 180Note that YAML is a superset of JSON; so if you'd like to highlight JSON, pick the YAML highlighter. 181 182*If you have a concrete use case for additional formats, please open an issue.* 183""" 184 185 186__all__ = [ 187 # Public Contentview API 188 "Contentview", 189 "InteractiveContentview", 190 "SyntaxHighlight", 191 "add", 192 "Metadata", 193]
24@typing.runtime_checkable 25class Contentview(typing.Protocol): 26 """ 27 Base class for all contentviews. 28 """ 29 30 @property 31 def name(self) -> str: 32 """ 33 The name of this contentview, e.g. "XML/HTML". 34 Inferred from the class name by default. 35 """ 36 return type(self).__name__.removesuffix("Contentview") 37 38 @property 39 def syntax_highlight(self) -> SyntaxHighlight: 40 """Optional syntax highlighting that should be applied to the prettified output.""" 41 return "none" 42 43 @abstractmethod 44 def prettify( 45 self, 46 data: bytes, 47 metadata: Metadata, 48 ) -> str: 49 """ 50 Transform raw data into human-readable output. 51 May raise an exception (e.g. `ValueError`) if data cannot be prettified. 52 """ 53 54 def render_priority( 55 self, 56 data: bytes, 57 metadata: Metadata, 58 ) -> float: 59 """ 60 Return the priority of this view for rendering `data`. 61 If no particular view is chosen by the user, the view with the highest priority is selected. 62 If this view does not support the given data, return a float < 0. 63 """ 64 return 0 65 66 def __lt__(self, other): 67 return self.name.__lt__(other.name)
Base class for all contentviews.
30 @property 31 def name(self) -> str: 32 """ 33 The name of this contentview, e.g. "XML/HTML". 34 Inferred from the class name by default. 35 """ 36 return type(self).__name__.removesuffix("Contentview")
The name of this contentview, e.g. "XML/HTML". Inferred from the class name by default.
38 @property 39 def syntax_highlight(self) -> SyntaxHighlight: 40 """Optional syntax highlighting that should be applied to the prettified output.""" 41 return "none"
Optional syntax highlighting that should be applied to the prettified output.
43 @abstractmethod 44 def prettify( 45 self, 46 data: bytes, 47 metadata: Metadata, 48 ) -> str: 49 """ 50 Transform raw data into human-readable output. 51 May raise an exception (e.g. `ValueError`) if data cannot be prettified. 52 """
Transform raw data into human-readable output.
May raise an exception (e.g. ValueError
) if data cannot be prettified.
54 def render_priority( 55 self, 56 data: bytes, 57 metadata: Metadata, 58 ) -> float: 59 """ 60 Return the priority of this view for rendering `data`. 61 If no particular view is chosen by the user, the view with the highest priority is selected. 62 If this view does not support the given data, return a float < 0. 63 """ 64 return 0
Return the priority of this view for rendering data
.
If no particular view is chosen by the user, the view with the highest priority is selected.
If this view does not support the given data, return a float < 0.
70@typing.runtime_checkable 71class InteractiveContentview(Contentview, typing.Protocol): 72 """A contentview that prettifies raw data and allows for interactive editing.""" 73 74 @abstractmethod 75 def reencode( 76 self, 77 prettified: str, 78 metadata: Metadata, 79 ) -> bytes: 80 """ 81 Reencode the given (modified) `prettified` output into the original data format. 82 May raise an exception (e.g. `ValueError`) if reencoding failed. 83 """
A contentview that prettifies raw data and allows for interactive editing.
74 @abstractmethod 75 def reencode( 76 self, 77 prettified: str, 78 metadata: Metadata, 79 ) -> bytes: 80 """ 81 Reencode the given (modified) `prettified` output into the original data format. 82 May raise an exception (e.g. `ValueError`) if reencoding failed. 83 """
Reencode the given (modified) prettified
output into the original data format.
May raise an exception (e.g. ValueError
) if reencoding failed.
Syntax highlighting formats currently supported by mitmproxy. Note that YAML is a superset of JSON; so if you'd like to highlight JSON, pick the YAML highlighter.
If you have a concrete use case for additional formats, please open an issue.
160def add(contentview: Contentview | type[Contentview]) -> None: 161 """ 162 Register a contentview for use in mitmproxy. 163 164 You may pass a `Contentview` instance or the class itself. 165 When passing the class, its constructor will be invoked with no arguments. 166 """ 167 if isinstance(contentview, View): 168 warnings.warn( 169 f"`mitmproxy.contentviews.View` is deprecated since mitmproxy 12, " 170 f"migrate {contentview.__class__.__name__} to `mitmproxy.contentviews.Contentview` instead.", 171 stacklevel=2, 172 ) 173 contentview = LegacyContentview(contentview) 174 registry.register(contentview)
Register a contentview for use in mitmproxy.
You may pass a Contentview
instance or the class itself.
When passing the class, its constructor will be invoked with no arguments.
86@dataclass 87class Metadata: 88 """ 89 Metadata about the data that is being prettified. 90 91 Do not rely on any given attribute to be present. 92 """ 93 94 flow: Flow | None = None 95 """The flow that the data belongs to, if any.""" 96 97 content_type: str | None = None 98 """The HTTP content type of the data, if any.""" 99 http_message: http.Message | None = None 100 """The HTTP message that the data belongs to, if any.""" 101 tcp_message: tcp.TCPMessage | None = None 102 """The TCP message that the data belongs to, if any.""" 103 udp_message: udp.UDPMessage | None = None 104 """The UDP message that the data belongs to, if any.""" 105 websocket_message: WebSocketMessage | None = None 106 """The websocket message that the data belongs to, if any.""" 107 dns_message: DNSMessage | None = None 108 """The DNS message that the data belongs to, if any.""" 109 110 protobuf_definitions: Path | None = None 111 """Path to a .proto file that's used to resolve Protobuf field names.""" 112 113 original_data: bytes | None = None 114 """When reencoding: The original data that was prettified."""
Metadata about the data that is being prettified.
Do not rely on any given attribute to be present.
The DNS message that the data belongs to, if any.