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 ._view_zip import zip 43from .base import View 44import mitmproxy_rs.contentviews 45from mitmproxy import flow 46from mitmproxy.utils import strutils 47 48logger = logging.getLogger(__name__) 49 50 51@dataclass 52class ContentviewResult: 53 text: str 54 syntax_highlight: SyntaxHighlight 55 view_name: str | None 56 description: str 57 58 59registry = ContentviewRegistry() 60 61 62def prettify_message( 63 message: ContentviewMessage, 64 flow: flow.Flow, 65 view_name: str = "auto", 66 registry: ContentviewRegistry = registry, 67) -> ContentviewResult: 68 data, enc = get_data(message) 69 if data is None: 70 return ContentviewResult( 71 text="Content is missing.", 72 syntax_highlight="error", 73 description="", 74 view_name=None, 75 ) 76 77 # Determine the correct view 78 metadata = make_metadata(message, flow) 79 view = registry.get_view(data, metadata, view_name) 80 81 # Finally, we can pretty-print! 82 try: 83 ret = ContentviewResult( 84 text=view.prettify(data, metadata), 85 syntax_highlight=view.syntax_highlight, 86 view_name=view.name, 87 description=enc, 88 ) 89 except Exception as e: 90 logger.debug(f"Contentview {view.name!r} failed: {e}", exc_info=True) 91 if view_name == "auto": 92 # If the contentview was chosen as the best matching one, fall back to raw. 93 ret = ContentviewResult( 94 text=raw.prettify(data, metadata), 95 syntax_highlight=raw.syntax_highlight, 96 view_name=raw.name, 97 description=f"{enc}[failed to parse as {view.name}]", 98 ) 99 else: 100 # Cut the exception traceback for display. 101 exc, value, tb = sys.exc_info() 102 tb_cut = cut_traceback(tb, "prettify_message") 103 if ( 104 tb_cut == tb 105 ): # If there are no extra frames, just skip displaying the traceback. 106 tb_cut = None 107 # If the contentview has been set explicitly, we display a hard error. 108 err = "".join(traceback.format_exception(exc, value=value, tb=tb_cut)) 109 ret = ContentviewResult( 110 text=f"Couldn't parse as {view.name}:\n{err}", 111 syntax_highlight="error", 112 view_name=view.name, 113 description=enc, 114 ) 115 116 ret.text = strutils.escape_control_characters(ret.text) 117 return ret 118 119 120def reencode_message( 121 prettified: str, 122 message: ContentviewMessage, 123 flow: flow.Flow, 124 view_name: str, 125) -> bytes: 126 metadata = make_metadata(message, flow) 127 view = registry[view_name.lower()] 128 if not isinstance(view, InteractiveContentview): 129 raise ValueError(f"Contentview {view.name} is not interactive.") 130 return view.reencode(prettified, metadata) 131 132 133_views: list[Contentview] = [ 134 css, 135 dns, 136 graphql, 137 http3, 138 image, 139 javascript, 140 json_view, 141 mqtt, 142 multipart, 143 query, 144 raw, 145 socket_io, 146 urlencoded, 147 wbxml, 148 xml_html, 149 zip, 150] 151for view in _views: 152 registry.register(view) 153for name in mitmproxy_rs.contentviews.__all__: 154 if name.startswith("_"): 155 continue 156 cv = getattr(mitmproxy_rs.contentviews, name) 157 if isinstance(cv, Contentview) and not isinstance(cv, type): 158 registry.register(cv) 159 160 161def add(contentview: Contentview | type[Contentview]) -> None: 162 """ 163 Register a contentview for use in mitmproxy. 164 165 You may pass a `Contentview` instance or the class itself. 166 When passing the class, its constructor will be invoked with no arguments. 167 """ 168 if isinstance(contentview, View): 169 warnings.warn( 170 f"`mitmproxy.contentviews.View` is deprecated since mitmproxy 12, " 171 f"migrate {contentview.__class__.__name__} to `mitmproxy.contentviews.Contentview` instead.", 172 stacklevel=2, 173 ) 174 contentview = LegacyContentview(contentview) 175 registry.register(contentview) 176 177 178# hack: docstring where pdoc finds it. 179SyntaxHighlight = SyntaxHighlight 180""" 181Syntax highlighting formats currently supported by mitmproxy. 182Note that YAML is a superset of JSON; so if you'd like to highlight JSON, pick the YAML highlighter. 183 184*If you have a concrete use case for additional formats, please open an issue.* 185""" 186 187 188__all__ = [ 189 # Public Contentview API 190 "Contentview", 191 "InteractiveContentview", 192 "SyntaxHighlight", 193 "add", 194 "Metadata", 195]
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.
162def add(contentview: Contentview | type[Contentview]) -> None: 163 """ 164 Register a contentview for use in mitmproxy. 165 166 You may pass a `Contentview` instance or the class itself. 167 When passing the class, its constructor will be invoked with no arguments. 168 """ 169 if isinstance(contentview, View): 170 warnings.warn( 171 f"`mitmproxy.contentviews.View` is deprecated since mitmproxy 12, " 172 f"migrate {contentview.__class__.__name__} to `mitmproxy.contentviews.Contentview` instead.", 173 stacklevel=2, 174 ) 175 contentview = LegacyContentview(contentview) 176 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.