diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py index 5d55cf58b..d235a327e 100644 --- a/libmproxy/console/__init__.py +++ b/libmproxy/console/__init__.py @@ -129,6 +129,10 @@ class StatusBar(common.WWrap): r.append(":%s in file]"%self.master.server_playback.count()) else: r.append(":%s to go]"%self.master.server_playback.count()) + if self.master.get_ignore(): + r.append("[") + r.append(("heading_key", "I")) + r.append("gnore:%d]"%len(self.master.get_ignore())) if self.master.state.intercept_txt: r.append("[") r.append(("heading_key", "i")) @@ -795,6 +799,10 @@ class ConsoleMaster(flow.FlowMaster): for command in commands: self.load_script(command) + def edit_ignore(self, ignore): + patterns = (x[0] for x in ignore) + self.set_ignore(patterns) + def loop(self): changed = True try: @@ -851,6 +859,14 @@ class ConsoleMaster(flow.FlowMaster): self.setheaders.set ) ) + elif k == "I": + self.view_grideditor( + grideditor.IgnoreEditor( + self, + [[x] for x in self.get_ignore()], + self.edit_ignore + ) + ) elif k == "i": self.prompt( "Intercept filter: ", diff --git a/libmproxy/console/grideditor.py b/libmproxy/console/grideditor.py index 642d8638f..d629ec82e 100644 --- a/libmproxy/console/grideditor.py +++ b/libmproxy/console/grideditor.py @@ -493,3 +493,15 @@ class ScriptEditor(GridEditor): script.Script.parse_command(val) except script.ScriptError, v: return str(v) + + +class IgnoreEditor(GridEditor): + title = "Editing ignore patterns" + columns = 1 + headings = ("Regex (matched on hostname:port / ip:port)",) + + def is_error(self, col, val): + try: + re.compile(val, re.IGNORECASE) + except re.error as e: + return "Invalid regex: %s" % str(e) \ No newline at end of file diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 343466e2b..d263ccdde 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -1,6 +1,5 @@ """ - This module provides more sophisticated flow tracking. These match requests - with their responses, and provide filtering and interception facilities. + This module provides more sophisticated flow tracking and provides filtering and interception facilities. """ from __future__ import absolute_import import base64 @@ -8,12 +7,11 @@ import hashlib, Cookie, cookielib, re, threading import os import flask import requests -from netlib import odict, wsgi, tcp +from netlib import odict, wsgi import netlib.http from . import controller, protocol, tnetstring, filt, script, version, app from .protocol import http, handle -from .proxy.connection import ServerConnection -from .proxy.primitives import ProxyError +from .proxy.config import parse_host_pattern ODict = odict.ODict ODictCaseless = odict.ODictCaseless @@ -522,6 +520,12 @@ class FlowMaster(controller.Master): for script in self.scripts: self.run_single_script_hook(script, name, *args, **kwargs) + def get_ignore(self): + return [i.pattern for i in self.server.config.ignore] + + def set_ignore(self, ignore): + self.server.config.ignore = parse_host_pattern(ignore) + def set_stickycookie(self, txt): if txt: flt = filt.parse(txt) diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index ea815c697..0ff08c6fd 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -10,6 +10,10 @@ CONF_BASENAME = "mitmproxy" CONF_DIR = "~/.mitmproxy" +def parse_host_pattern(patterns): + return [re.compile(p, re.IGNORECASE) for p in patterns] + + class ProxyConfig: def __init__(self, confdir=CONF_DIR, ca_file=None, clientcerts=None, no_upstream_cert=False, body_size_limit=None, @@ -41,7 +45,7 @@ class ProxyConfig: self.get_upstream_server = get_upstream_server self.http_form_in = http_form_in self.http_form_out = http_form_out - self.ignore = [re.compile(i, re.IGNORECASE) for i in ignore] + self.ignore = parse_host_pattern(ignore) self.authenticator = authenticator self.confdir = os.path.expanduser(confdir) self.ca_file = ca_file or os.path.join(self.confdir, CONF_BASENAME + "-ca.pem") diff --git a/test/test_flow.py b/test/test_flow.py index 914138c91..a297bf5f4 100644 --- a/test/test_flow.py +++ b/test/test_flow.py @@ -1,6 +1,7 @@ import Queue, time, os.path from cStringIO import StringIO import email.utils +import mock from libmproxy import filt, protocol, controller, utils, tnetstring, flow from libmproxy.protocol.primitives import Error, Flow from libmproxy.protocol.http import decoded, CONTENT_MISSING @@ -541,6 +542,14 @@ class TestFlowMaster: assert "ValueError" in fm.load_script(tutils.test_data.path("scripts/starterr.py")) assert len(fm.scripts) == 0 + def test_getset_ignore(self): + p = mock.Mock() + p.config.ignore = [] + fm = flow.FlowMaster(p, flow.State()) + assert not fm.get_ignore() + fm.set_ignore(["^apple\.com:", ":443$"]) + assert fm.get_ignore() + def test_replay(self): s = flow.State() fm = flow.FlowMaster(None, s) diff --git a/test/test_server.py b/test/test_server.py index b128d0a20..0ce5d056d 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -1,5 +1,5 @@ import socket, time -from libmproxy.proxy.config import ProxyConfig +from libmproxy.proxy.config import parse_host_pattern from netlib import tcp, http_auth, http from libpathod import pathoc, pathod from netlib.certutils import SSLCert @@ -79,8 +79,8 @@ class CommonMixin: class TcpMixin: def _ignore_on(self): - conf = ProxyConfig(ignore=[".+:%s" % self.server.port]) - self.config.ignore.append(conf.ignore[0]) + ignore = parse_host_pattern([".+:%s" % self.server.port])[0] + self.config.ignore.append(ignore) def _ignore_off(self): self.config.ignore.pop() @@ -581,9 +581,9 @@ class TestUpstreamProxySSL(tservers.HTTPUpstreamProxTest, CommonMixin, TcpMixin) def _ignore_on(self): super(TestUpstreamProxySSL, self)._ignore_on() - conf = ProxyConfig(ignore=[".+:%s" % self.server.port]) + ignore = parse_host_pattern([".+:%s" % self.server.port])[0] for proxy in self.chain: - proxy.tmaster.server.config.ignore.append(conf.ignore[0]) + proxy.tmaster.server.config.ignore.append(ignore) def _ignore_off(self): super(TestUpstreamProxySSL, self)._ignore_off()