2025-12-25 upload
This commit is contained in:
151
venv/Lib/site-packages/mitmproxy/tools/web/webaddons.py
Normal file
151
venv/Lib/site-packages/mitmproxy/tools/web/webaddons.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hmac
|
||||
import logging
|
||||
import secrets
|
||||
import webbrowser
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import argon2
|
||||
|
||||
from mitmproxy import ctx
|
||||
from mitmproxy import exceptions
|
||||
from mitmproxy.tools.web.web_columns import AVAILABLE_WEB_COLUMNS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mitmproxy.tools.web.master import WebMaster
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebAuth:
|
||||
_password: str
|
||||
_hasher: argon2.PasswordHasher
|
||||
|
||||
def __init__(self):
|
||||
self._password = secrets.token_hex(16)
|
||||
self._hasher = argon2.PasswordHasher()
|
||||
|
||||
def load(self, loader):
|
||||
loader.add_option(
|
||||
"web_password",
|
||||
str,
|
||||
"",
|
||||
"Password to protect the mitmweb user interface. "
|
||||
"Values starting with `$` are interpreted as an argon2 hash, "
|
||||
"everything else is considered a plaintext password. "
|
||||
"If no password is provided, a random token is generated on startup."
|
||||
"For automated calls, you can pass the password as token query parameter"
|
||||
"or as `Authorization: Bearer ...` header.",
|
||||
)
|
||||
|
||||
def configure(self, updated) -> None:
|
||||
if "web_password" in updated:
|
||||
if ctx.options.web_password.startswith("$"):
|
||||
try:
|
||||
argon2.extract_parameters(ctx.options.web_password)
|
||||
except argon2.exceptions.InvalidHashError:
|
||||
raise exceptions.OptionsError(
|
||||
"`web_password` starts with `$`, but it's not a valid argon2 hash."
|
||||
)
|
||||
elif ctx.options.web_password:
|
||||
logger.warning(
|
||||
"Using a plaintext password to protect the mitmweb user interface. "
|
||||
"Consider using an argon2 hash for `web_password` instead."
|
||||
)
|
||||
self._password = ctx.options.web_password or secrets.token_hex(16)
|
||||
|
||||
@property
|
||||
def web_url(self) -> str:
|
||||
if ctx.options.web_password:
|
||||
auth = "" # We don't want to print plaintext passwords (and it doesn't work for argon2 anyhow).
|
||||
else:
|
||||
auth = f"?token={self._password}"
|
||||
web_host = ctx.options.web_host
|
||||
if ":" in web_host: # ipv6
|
||||
web_host = f"[{web_host}]"
|
||||
# noinspection HttpUrlsUsage
|
||||
return f"http://{web_host}:{ctx.options.web_port}/{auth}"
|
||||
|
||||
@staticmethod
|
||||
def auth_cookie_name() -> str:
|
||||
return f"mitmproxy-auth-{ctx.options.web_port}"
|
||||
|
||||
def is_valid_password(self, password: str) -> bool:
|
||||
if self._password.startswith("$"):
|
||||
try:
|
||||
return self._hasher.verify(self._password, password)
|
||||
except argon2.exceptions.VerificationError:
|
||||
return False
|
||||
else:
|
||||
return hmac.compare_digest(
|
||||
self._password,
|
||||
password,
|
||||
)
|
||||
|
||||
|
||||
class WebAddon:
|
||||
def load(self, loader):
|
||||
loader.add_option("web_open_browser", bool, True, "Start a browser.")
|
||||
loader.add_option("web_debug", bool, False, "Enable mitmweb debugging.")
|
||||
loader.add_option("web_port", int, 8081, "Web UI port.")
|
||||
loader.add_option("web_host", str, "127.0.0.1", "Web UI host.")
|
||||
loader.add_option(
|
||||
"web_columns",
|
||||
Sequence[str],
|
||||
["tls", "icon", "path", "method", "status", "size", "time"],
|
||||
f"Columns to show in the flow list. Can be one of the following: {', '.join(AVAILABLE_WEB_COLUMNS)}",
|
||||
)
|
||||
|
||||
def running(self):
|
||||
if hasattr(ctx.options, "web_open_browser") and ctx.options.web_open_browser:
|
||||
master: WebMaster = ctx.master # type: ignore
|
||||
success = open_browser(master.web_url)
|
||||
if not success:
|
||||
logger.info(
|
||||
f"No web browser found. Please open a browser and point it to {master.web_url}",
|
||||
)
|
||||
if not success and not ctx.options.web_password:
|
||||
logger.info(
|
||||
f"You can configure a fixed authentication token by setting the `web_password` option "
|
||||
f"(https://docs.mitmproxy.org/stable/concepts-options/#web_password).",
|
||||
)
|
||||
|
||||
|
||||
def open_browser(url: str) -> bool:
|
||||
"""
|
||||
Open a URL in a browser window.
|
||||
In contrast to webbrowser.open, we limit the list of suitable browsers.
|
||||
This gracefully degrades to a no-op on headless servers, where webbrowser.open
|
||||
would otherwise open lynx.
|
||||
|
||||
Returns:
|
||||
True, if a browser has been opened
|
||||
False, if no suitable browser has been found.
|
||||
"""
|
||||
browsers = (
|
||||
"windows-default",
|
||||
"macosx",
|
||||
"wslview %s",
|
||||
"gio",
|
||||
"x-www-browser",
|
||||
"gnome-open %s",
|
||||
"xdg-open",
|
||||
"google-chrome",
|
||||
"chrome",
|
||||
"chromium",
|
||||
"chromium-browser",
|
||||
"firefox",
|
||||
"opera",
|
||||
"safari",
|
||||
)
|
||||
for browser in browsers:
|
||||
try:
|
||||
b = webbrowser.get(browser)
|
||||
except webbrowser.Error:
|
||||
pass
|
||||
else:
|
||||
if b.open(url):
|
||||
return True
|
||||
return False
|
||||
Reference in New Issue
Block a user