aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/addons/__init__.py2
-rw-r--r--mitmproxy/addons/browser.py74
-rw-r--r--mitmproxy/command.py9
-rw-r--r--mitmproxy/flow.py6
-rw-r--r--mitmproxy/tools/console/consoleaddons.py4
-rw-r--r--mitmproxy/tools/console/defaultkeys.py3
-rw-r--r--pathod/pathoc.py1
-rw-r--r--setup.py4
-rw-r--r--test/mitmproxy/addons/test_browser.py31
-rw-r--r--test/mitmproxy/test_command.py7
-rw-r--r--test/mitmproxy/test_http.py9
11 files changed, 143 insertions, 7 deletions
diff --git a/mitmproxy/addons/__init__.py b/mitmproxy/addons/__init__.py
index 62135765..8f84c20d 100644
--- a/mitmproxy/addons/__init__.py
+++ b/mitmproxy/addons/__init__.py
@@ -1,6 +1,7 @@
from mitmproxy.addons import allowremote
from mitmproxy.addons import anticache
from mitmproxy.addons import anticomp
+from mitmproxy.addons import browser
from mitmproxy.addons import check_ca
from mitmproxy.addons import clientplayback
from mitmproxy.addons import core_option_validation
@@ -25,6 +26,7 @@ def default_addons():
return [
core.Core(),
core_option_validation.CoreOptionValidation(),
+ browser.Browser(),
allowremote.AllowRemote(),
anticache.AntiCache(),
anticomp.AntiComp(),
diff --git a/mitmproxy/addons/browser.py b/mitmproxy/addons/browser.py
new file mode 100644
index 00000000..247c356b
--- /dev/null
+++ b/mitmproxy/addons/browser.py
@@ -0,0 +1,74 @@
+import shutil
+import subprocess
+import tempfile
+import typing
+
+from mitmproxy import command
+from mitmproxy import ctx
+
+
+def get_chrome_executable() -> typing.Optional[str]:
+ for browser in (
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
+ # https://stackoverflow.com/questions/40674914/google-chrome-path-in-windows-10
+ r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
+ r"C:\Program Files (x86)\Google\Application\chrome.exe",
+ # Linux binary names from Python's webbrowser module.
+ "google-chrome",
+ "chrome",
+ "chromium",
+ "chromium-browser",
+ ):
+ if shutil.which(browser):
+ return browser
+ return None
+
+
+class Browser:
+ browser = None
+ tdir = None
+
+ @command.command("browser.start")
+ def start(self) -> None:
+ """
+ Start an isolated instance of Chrome that points to the currently
+ running proxy.
+ """
+ if self.browser:
+ if self.browser.poll() is None:
+ ctx.log.alert("Browser already running")
+ return
+ else:
+ self.done()
+
+ cmd = get_chrome_executable()
+ if not cmd:
+ ctx.log.alert("Your platform is not supported yet - please submit a patch.")
+ return
+
+ self.tdir = tempfile.TemporaryDirectory()
+ self.browser = subprocess.Popen(
+ [
+ cmd,
+ "--user-data-dir=%s" % str(self.tdir.name),
+ "--proxy-server=%s:%s" % (
+ ctx.options.listen_host or "127.0.0.1",
+ ctx.options.listen_port
+ ),
+ "--disable-fre",
+ "--no-default-browser-check",
+ "--no-first-run",
+ "--disable-extensions",
+
+ "about:blank",
+ ],
+ stdout = subprocess.DEVNULL,
+ stderr = subprocess.DEVNULL,
+ )
+
+ def done(self):
+ if self.browser:
+ self.browser.kill()
+ self.tdir.cleanup()
+ self.browser = None
+ self.tdir = None
diff --git a/mitmproxy/command.py b/mitmproxy/command.py
index c9776bc3..eae3d80c 100644
--- a/mitmproxy/command.py
+++ b/mitmproxy/command.py
@@ -190,10 +190,19 @@ def parsearg(manager: CommandManager, spec: str, argtype: type) -> typing.Any:
raise exceptions.CommandError("Unsupported argument type: %s" % argtype)
+def verify_arg_signature(f: typing.Callable, args: list, kwargs: dict) -> None:
+ sig = inspect.signature(f)
+ try:
+ sig.bind(*args, **kwargs)
+ except TypeError as v:
+ raise exceptions.CommandError("Argument mismatch: %s" % v.args[0])
+
+
def command(path):
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
+ verify_arg_signature(function, args, kwargs)
return function(*args, **kwargs)
wrapper.__dict__["command_path"] = path
return wrapper
diff --git a/mitmproxy/flow.py b/mitmproxy/flow.py
index dc778404..111566b8 100644
--- a/mitmproxy/flow.py
+++ b/mitmproxy/flow.py
@@ -179,5 +179,7 @@ class Flow(stateobject.StateObject):
if not self.intercepted:
return
self.intercepted = False
- self.reply.ack()
- self.reply.commit()
+ # If a flow is intercepted and then duplicated, the duplicated one is not taken.
+ if self.reply.state == "taken":
+ self.reply.ack()
+ self.reply.commit()
diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py
index 49934e4d..1bda219f 100644
--- a/mitmproxy/tools/console/consoleaddons.py
+++ b/mitmproxy/tools/console/consoleaddons.py
@@ -329,9 +329,9 @@ class ConsoleAddon:
correct viewier, and fall back to the programs in $PAGER or $EDITOR
if necessary.
"""
- fpart = getattr(f, part)
+ fpart = getattr(f, part, None)
if not fpart:
- raise exceptions.CommandError("Could not view part %s." % part)
+ raise exceptions.CommandError("Part must be either request or response, not %s." % part)
t = fpart.headers.get("content-type")
content = fpart.get_content(strict=False)
if not content:
diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py
index 8139569e..8c28524a 100644
--- a/mitmproxy/tools/console/defaultkeys.py
+++ b/mitmproxy/tools/console/defaultkeys.py
@@ -2,7 +2,8 @@
def map(km):
km.add(":", "console.command ", ["global"], "Command prompt")
km.add("?", "console.view.help", ["global"], "View help")
- km.add("C", "console.view.commands", ["global"], "View commands")
+ km.add("B", "browser.start", ["global"], "View commands")
+ km.add("C", "console.view.commands", ["global"], "Start an attached browser")
km.add("K", "console.view.keybindings", ["global"], "View key bindings")
km.add("O", "console.view.options", ["global"], "View options")
km.add("E", "console.view.eventlog", ["global"], "View event log")
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index e1052750..20a915c0 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -244,6 +244,7 @@ class Pathoc(tcp.TCPClient):
port=connect_to[1],
path=None,
http_version='HTTP/1.1',
+ headers=[(b"Host", connect_to[0].encode("idna"))],
content=b'',
)
self.wfile.write(net_http.http1.assemble_request(req))
diff --git a/setup.py b/setup.py
index 11a72a00..54c2811d 100644
--- a/setup.py
+++ b/setup.py
@@ -70,8 +70,8 @@ setup(
"kaitaistruct>=0.7, <0.8",
"ldap3>=2.4,<2.5",
"passlib>=1.6.5, <1.8",
- "pyasn1>=0.3.1, <0.4",
- "pyOpenSSL>=17.2,<17.4",
+ "pyasn1>=0.3.1,<0.5",
+ "pyOpenSSL>=17.2,<17.6",
"pyparsing>=2.1.3, <2.3",
"pyperclip>=1.5.22, <1.7",
"requests>=2.9.1, <3",
diff --git a/test/mitmproxy/addons/test_browser.py b/test/mitmproxy/addons/test_browser.py
new file mode 100644
index 00000000..407a3fe6
--- /dev/null
+++ b/test/mitmproxy/addons/test_browser.py
@@ -0,0 +1,31 @@
+from unittest import mock
+
+from mitmproxy.addons import browser
+from mitmproxy.test import taddons
+
+
+def test_browser():
+ with mock.patch("subprocess.Popen") as po, mock.patch("shutil.which") as which:
+ which.return_value = "chrome"
+ b = browser.Browser()
+ with taddons.context() as tctx:
+ b.start()
+ assert po.called
+ b.start()
+
+ assert not tctx.master.has_log("already running")
+ b.browser.poll = lambda: None
+ b.start()
+ assert tctx.master.has_log("already running")
+ b.done()
+ assert not b.browser
+
+
+def test_no_browser():
+ with mock.patch("shutil.which") as which:
+ which.return_value = False
+
+ b = browser.Browser()
+ with taddons.context() as tctx:
+ b.start()
+ assert tctx.master.has_log("platform is not supported")
diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py
index 87432163..43b97742 100644
--- a/test/mitmproxy/test_command.py
+++ b/test/mitmproxy/test_command.py
@@ -163,3 +163,10 @@ def test_decorator():
with taddons.context() as tctx:
tctx.master.addons.add(a)
assert tctx.master.commands.call("cmd1 bar") == "ret bar"
+
+
+def test_verify_arg_signature():
+ with pytest.raises(exceptions.CommandError):
+ command.verify_arg_signature(lambda: None, [1, 2], {})
+ print('hello there')
+ command.verify_arg_signature(lambda a, b: None, [1, 2], {}) \ No newline at end of file
diff --git a/test/mitmproxy/test_http.py b/test/mitmproxy/test_http.py
index 4463961a..49e61e25 100644
--- a/test/mitmproxy/test_http.py
+++ b/test/mitmproxy/test_http.py
@@ -203,6 +203,15 @@ class TestHTTPFlow:
f.resume()
assert f.reply.state == "committed"
+ def test_resume_duplicated(self):
+ f = tflow.tflow()
+ f.intercept()
+ f2 = f.copy()
+ assert f.intercepted is f2.intercepted is True
+ f.resume()
+ f2.resume()
+ assert f.intercepted is f2.intercepted is False
+
def test_replace_unicode(self):
f = tflow.tflow(resp=True)
f.response.content = b"\xc2foo"