From aeefcd08a40a8bd886d3bca6eca00ec5ace325ee Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 10 Dec 2017 17:35:59 +1300 Subject: [PATCH] browser addon: start an isolated browser attached to the proxy A simple addon that starts an instance of Chrome attached to the current proxy. The instance is isolated in its own user data directory, and addons are turned off. Future work: - I wasn't able to test the Windows executable path - a Windows dev should confirm this for us. - In future it would be nice to support other browsers like Firefox. --- mitmproxy/addons/__init__.py | 2 + mitmproxy/addons/browser.py | 62 ++++++++++++++++++++++++++ mitmproxy/tools/console/defaultkeys.py | 3 +- test/mitmproxy/addons/test_browser.py | 20 +++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 mitmproxy/addons/browser.py create mode 100644 test/mitmproxy/addons/test_browser.py diff --git a/mitmproxy/addons/__init__.py b/mitmproxy/addons/__init__.py index 62135765c..8f84c20d9 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 000000000..6e8b2585f --- /dev/null +++ b/mitmproxy/addons/browser.py @@ -0,0 +1,62 @@ +import subprocess +import sys +import tempfile + +from mitmproxy import command +from mitmproxy import ctx + +platformPaths = { + "linux": "google-chrome", + "win32": "chrome.exe", + "darwin": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", +} + + +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 = platformPaths.get(sys.platform) + if not cmd: # pragma: no cover + 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 \ No newline at end of file diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py index 8139569ed..8c28524ad 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/test/mitmproxy/addons/test_browser.py b/test/mitmproxy/addons/test_browser.py new file mode 100644 index 000000000..d1b32186f --- /dev/null +++ b/test/mitmproxy/addons/test_browser.py @@ -0,0 +1,20 @@ +from unittest import mock + +from mitmproxy.addons import browser +from mitmproxy.test import taddons + + +def test_browser(): + with mock.patch("subprocess.Popen") as po: + 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