codekingpro/portable-devtools
114k
1import json2import logging3import os.path4import pathlib5import shutil6import time7from collections.abc import Iterable8from typing import Optional9 10from mitmproxy import contentviews11from mitmproxy import ctx12from mitmproxy import flow13from mitmproxy import flowfilter14from mitmproxy import http15from mitmproxy import io16from mitmproxy import version17from mitmproxy.tools.web.app import flow_to_json18 19web_dir = pathlib.Path(__file__).absolute().parent20 21 22def save_static(path: pathlib.Path) -> None:23 """24 Save the files for the static web view.25 """26 # We want to overwrite the static files to keep track of the update.27 if (path / "static").exists():28 shutil.rmtree(str(path / "static"))29 shutil.copytree(str(web_dir / "static"), str(path / "static"))30 shutil.copyfile(str(web_dir / "index.html"), str(path / "index.html"))31 32 with open(str(path / "static" / "static.js"), "w") as f:33 f.write("MITMWEB_STATIC = true;")34 35 36def save_filter_help(path: pathlib.Path) -> None:37 with open(str(path / "filter-help.json"), "w") as f:38 json.dump(dict(commands=flowfilter.help), f)39 40 41def save_settings(path: pathlib.Path) -> None:42 with open(str(path / "settings.json"), "w") as f:43 json.dump(dict(version=version.VERSION), f)44 45 46def save_flows(path: pathlib.Path, flows: Iterable[flow.Flow]) -> None:47 with open(str(path / "flows.json"), "w") as f:48 json.dump([flow_to_json(f) for f in flows], f)49 50 51def save_flows_content(path: pathlib.Path, flows: Iterable[flow.Flow]) -> None:52 for f in flows:53 assert isinstance(f, http.HTTPFlow)54 for m in ("request", "response"):55 message = getattr(f, m)56 message_path = path / "flows" / f.id / m57 os.makedirs(str(message_path / "content"), exist_ok=True)58 59 with open(str(message_path / "content.data"), "wb") as content_file:60 # don't use raw_content here as this is served with a default content type61 if message:62 content_file.write(message.content)63 else:64 content_file.write(b"No content.")65 66 # content_view67 t = time.time()68 if message:69 pretty = contentviews.prettify_message(70 message=message,71 flow=f,72 )73 else:74 pretty = contentviews.ContentviewResult(75 text="No content.",76 syntax_highlight="none",77 view_name="/",78 description="",79 )80 if time.time() - t > 0.1:81 logging.info(82 f"Slow content view: {pretty.view_name} took {round(time.time() - t, 1)}s",83 )84 with (message_path / "content" / "Auto.json").open(85 "w"86 ) as content_view_file:87 json.dump(88 dict(89 text=pretty.text,90 syntax_highlight=pretty.syntax_highlight,91 view_name=pretty.view_name,92 description=pretty.description,93 ),94 content_view_file,95 )96 97 98class StaticViewer:99 # TODO: make this a command at some point.100 def load(self, loader):101 loader.add_option(102 "web_static_viewer",103 Optional[str],104 "",105 "The path to output a static viewer.",106 )107 108 def configure(self, updated):109 if "web_static_viewer" in updated and ctx.options.web_static_viewer:110 flows = io.read_flows_from_paths([ctx.options.rfile])111 p = pathlib.Path(ctx.options.web_static_viewer).expanduser()112 self.export(p, flows)113 114 def export(self, path: pathlib.Path, flows: Iterable[flow.Flow]) -> None:115 save_static(path)116 save_filter_help(path)117 save_flows(path, flows)118 save_flows_content(path, flows)119 