Merge pull request #1380 from dufferzafar/marked-filter

Marked property and filter
This commit is contained in:
Maximilian Hils 2016-07-23 17:55:12 -07:00 committed by GitHub
commit a87d089561
10 changed files with 96 additions and 106 deletions

View File

@ -410,7 +410,7 @@ def raw_format_flow(f, focus, extended):
return urwid.Pile(pile) return urwid.Pile(pile)
def format_flow(f, focus, extended=False, hostheader=False, marked=False): def format_flow(f, focus, extended=False, hostheader=False):
d = dict( d = dict(
intercepted = f.intercepted, intercepted = f.intercepted,
acked = f.reply.acked, acked = f.reply.acked,
@ -423,7 +423,7 @@ def format_flow(f, focus, extended=False, hostheader=False, marked=False):
err_msg = f.error.msg if f.error else None, err_msg = f.error.msg if f.error else None,
marked = marked, marked = f.marked,
) )
if f.response: if f.response:
if f.response.raw_content: if f.response.raw_content:

View File

@ -120,23 +120,17 @@ class ConnectionItem(urwid.WidgetWrap):
self.flow, self.flow,
self.f, self.f,
hostheader = self.master.options.showhost, hostheader = self.master.options.showhost,
marked=self.state.flow_marked(self.flow)
) )
def selectable(self): def selectable(self):
return True return True
def save_flows_prompt(self, k): def save_flows_prompt(self, k):
if k == "a": if k == "l":
signals.status_prompt_path.send( signals.status_prompt_path.send(
prompt = "Save all flows to", prompt = "Save listed flows to",
callback = self.master.save_flows callback = self.master.save_flows
) )
elif k == "m":
signals.status_prompt_path.send(
prompt = "Save marked flows to",
callback = self.master.save_marked_flows
)
else: else:
signals.status_prompt_path.send( signals.status_prompt_path.send(
prompt = "Save this flow to", prompt = "Save this flow to",
@ -197,10 +191,7 @@ class ConnectionItem(urwid.WidgetWrap):
self.master.state.set_focus_flow(f) self.master.state.set_focus_flow(f)
signals.flowlist_change.send(self) signals.flowlist_change.send(self)
elif key == "m": elif key == "m":
if self.state.flow_marked(self.flow): self.flow.marked = not self.flow.marked
self.state.set_flow_marked(self.flow, False)
else:
self.state.set_flow_marked(self.flow, True)
signals.flowlist_change.send(self) signals.flowlist_change.send(self)
elif key == "M": elif key == "M":
if self.state.mark_filter: if self.state.mark_filter:
@ -235,7 +226,7 @@ class ConnectionItem(urwid.WidgetWrap):
) )
elif key == "U": elif key == "U":
for f in self.state.flows: for f in self.state.flows:
self.state.set_flow_marked(f, False) f.marked = False
signals.flowlist_change.send(self) signals.flowlist_change.send(self)
elif key == "V": elif key == "V":
if not self.flow.modified(): if not self.flow.modified():
@ -249,9 +240,8 @@ class ConnectionItem(urwid.WidgetWrap):
self, self,
prompt = "Save", prompt = "Save",
keys = ( keys = (
("all flows", "a"), ("listed flows", "l"),
("this flow", "t"), ("this flow", "t"),
("marked flows", "m"),
), ),
callback = self.save_flows_prompt, callback = self.save_flows_prompt,
) )

View File

@ -34,6 +34,7 @@ from mitmproxy.console import palettes
from mitmproxy.console import signals from mitmproxy.console import signals
from mitmproxy.console import statusbar from mitmproxy.console import statusbar
from mitmproxy.console import window from mitmproxy.console import window
from mitmproxy.filt import FMarked
from netlib import tcp, strutils from netlib import tcp, strutils
EVENTLOG_SIZE = 500 EVENTLOG_SIZE = 500
@ -48,7 +49,7 @@ class ConsoleState(flow.State):
self.default_body_view = contentviews.get("Auto") self.default_body_view = contentviews.get("Auto")
self.flowsettings = weakref.WeakKeyDictionary() self.flowsettings = weakref.WeakKeyDictionary()
self.last_search = None self.last_search = None
self.last_filter = None self.last_filter = ""
self.mark_filter = False self.mark_filter = False
def __setattr__(self, name, value): def __setattr__(self, name, value):
@ -66,7 +67,6 @@ class ConsoleState(flow.State):
def add_flow(self, f): def add_flow(self, f):
super(ConsoleState, self).add_flow(f) super(ConsoleState, self).add_flow(f)
self.update_focus() self.update_focus()
self.set_flow_marked(f, False)
return f return f
def update_flow(self, f): def update_flow(self, f):
@ -123,48 +123,71 @@ class ConsoleState(flow.State):
self.set_focus(self.focus) self.set_focus(self.focus)
return ret return ret
def filter_marked(self, m): def get_nearest_matching_flow(self, flow, filt):
def actual_func(x): fidx = self.view.index(flow)
if x.id in m: dist = 1
return True
return False fprev = fnext = True
return actual_func while fprev or fnext:
fprev, _ = self.get_from_pos(fidx - dist)
fnext, _ = self.get_from_pos(fidx + dist)
if fprev and fprev.match(filt):
return fprev
elif fnext and fnext.match(filt):
return fnext
dist += 1
return None
def enable_marked_filter(self): def enable_marked_filter(self):
marked_flows = [f for f in self.flows if f.marked]
if not marked_flows:
return
marked_filter = "~%s" % FMarked.code
# Save Focus
last_focus, _ = self.get_focus()
nearest_marked = self.get_nearest_matching_flow(last_focus, marked_filter)
self.last_filter = self.limit_txt self.last_filter = self.limit_txt
marked_flows = [] self.set_limit(marked_filter)
for f in self.flows:
if self.flow_marked(f): # Restore Focus
marked_flows.append(f.id) if last_focus.marked:
if len(marked_flows) > 0: self.set_focus_flow(last_focus)
f = self.filter_marked(marked_flows) else:
self.view._close() self.set_focus_flow(nearest_marked)
self.view = flow.FlowView(self.flows, f)
self.focus = 0 self.mark_filter = True
self.set_focus(self.focus)
self.mark_filter = True
def disable_marked_filter(self): def disable_marked_filter(self):
if self.last_filter is None: marked_filter = "~%s" % FMarked.code
self.view = flow.FlowView(self.flows, None)
# Save Focus
last_focus, _ = self.get_focus()
nearest_marked = self.get_nearest_matching_flow(last_focus, marked_filter)
self.set_limit(self.last_filter)
self.last_filter = ""
# Restore Focus
if last_focus.marked:
self.set_focus_flow(last_focus)
else: else:
self.set_limit(self.last_filter) self.set_focus_flow(nearest_marked)
self.focus = 0
self.set_focus(self.focus)
self.last_filter = None
self.mark_filter = False self.mark_filter = False
def clear(self): def clear(self):
marked_flows = [] marked_flows = [f for f in self.state.view if f.marked]
for f in self.flows:
if self.flow_marked(f):
marked_flows.append(f)
super(ConsoleState, self).clear() super(ConsoleState, self).clear()
for f in marked_flows: for f in marked_flows:
self.add_flow(f) self.add_flow(f)
self.set_flow_marked(f, True) f.marked = True
if len(self.flows.views) == 0: if len(self.flows.views) == 0:
self.focus = None self.focus = None
@ -172,12 +195,6 @@ class ConsoleState(flow.State):
self.focus = 0 self.focus = 0
self.set_focus(self.focus) self.set_focus(self.focus)
def flow_marked(self, flow):
return self.get_flow_setting(flow, "marked", False)
def set_flow_marked(self, flow, marked):
self.add_flow_setting(flow, "marked", marked)
class Options(mitmproxy.options.Options): class Options(mitmproxy.options.Options):
def __init__( def __init__(
@ -615,13 +632,6 @@ class ConsoleMaster(flow.FlowMaster):
def save_flows(self, path): def save_flows(self, path):
return self._write_flows(path, self.state.view) return self._write_flows(path, self.state.view)
def save_marked_flows(self, path):
marked_flows = []
for f in self.state.view:
if self.state.flow_marked(f):
marked_flows.append(f)
return self._write_flows(path, marked_flows)
def load_flows_callback(self, path): def load_flows_callback(self, path):
if not path: if not path:
return return

View File

@ -171,10 +171,6 @@ class StatusBar(urwid.WidgetWrap):
r.append("[") r.append("[")
r.append(("heading_key", "l")) r.append(("heading_key", "l"))
r.append(":%s]" % self.master.state.limit_txt) r.append(":%s]" % self.master.state.limit_txt)
if self.master.state.mark_filter:
r.append("[")
r.append(("heading_key", "Marked Flows"))
r.append("]")
if self.master.options.stickycookie: if self.master.options.stickycookie:
r.append("[") r.append("[")
r.append(("heading_key", "t")) r.append(("heading_key", "t"))

View File

@ -83,6 +83,14 @@ class FErr(_Action):
return True if f.error else False return True if f.error else False
class FMarked(_Action):
code = "marked"
help = "Match marked flows"
def __call__(self, f):
return f.marked
class FHTTP(_Action): class FHTTP(_Action):
code = "http" code = "http"
help = "Match HTTP flows" help = "Match HTTP flows"
@ -401,6 +409,7 @@ filt_unary = [
FAsset, FAsset,
FErr, FErr,
FHTTP, FHTTP,
FMarked,
FReq, FReq,
FResp, FResp,
FTCP, FTCP,

View File

@ -60,6 +60,7 @@ def convert_017_018(data):
data = convert_unicode(data) data = convert_unicode(data)
data["server_conn"]["ip_address"] = data["server_conn"].pop("peer_address") data["server_conn"]["ip_address"] = data["server_conn"].pop("peer_address")
data["marked"] = False
data["version"] = (0, 18) data["version"] = (0, 18)
return data return data

View File

@ -8,6 +8,8 @@ from mitmproxy import stateobject
from mitmproxy.models.connections import ClientConnection from mitmproxy.models.connections import ClientConnection
from mitmproxy.models.connections import ServerConnection from mitmproxy.models.connections import ServerConnection
import six
from netlib import version from netlib import version
from typing import Optional # noqa from typing import Optional # noqa
@ -79,6 +81,7 @@ class Flow(stateobject.StateObject):
self.intercepted = False # type: bool self.intercepted = False # type: bool
self._backup = None # type: Optional[Flow] self._backup = None # type: Optional[Flow]
self.reply = None self.reply = None
self.marked = False # type: bool
_stateobject_attributes = dict( _stateobject_attributes = dict(
id=str, id=str,
@ -86,7 +89,8 @@ class Flow(stateobject.StateObject):
client_conn=ClientConnection, client_conn=ClientConnection,
server_conn=ServerConnection, server_conn=ServerConnection,
type=str, type=str,
intercepted=bool intercepted=bool,
marked=bool,
) )
def get_state(self): def get_state(self):
@ -173,3 +177,21 @@ class Flow(stateobject.StateObject):
self.intercepted = False self.intercepted = False
self.reply.ack() self.reply.ack()
master.handle_accept_intercept(self) master.handle_accept_intercept(self)
def match(self, f):
"""
Match this flow against a compiled filter expression. Returns True
if matched, False if not.
If f is a string, it will be compiled as a filter expression. If
the expression is invalid, ValueError is raised.
"""
if isinstance(f, six.string_types):
from .. import filt
f = filt.parse(f)
if not f:
raise ValueError("Invalid filter expression.")
if f:
return f(self)
return True

View File

@ -2,7 +2,6 @@ from __future__ import absolute_import, print_function, division
import cgi import cgi
import warnings import warnings
import six
from mitmproxy.models.flow import Flow from mitmproxy.models.flow import Flow
from netlib import version from netlib import version
@ -211,24 +210,6 @@ class HTTPFlow(Flow):
f.response = self.response.copy() f.response = self.response.copy()
return f return f
def match(self, f):
"""
Match this flow against a compiled filter expression. Returns True
if matched, False if not.
If f is a string, it will be compiled as a filter expression. If
the expression is invalid, ValueError is raised.
"""
if isinstance(f, six.string_types):
from .. import filt
f = filt.parse(f)
if not f:
raise ValueError("Invalid filter expression.")
if f:
return f(self)
return True
def replace(self, pattern, repl, *args, **kwargs): def replace(self, pattern, repl, *args, **kwargs):
""" """
Replaces a regular expression pattern with repl in both request and Replaces a regular expression pattern with repl in both request and

View File

@ -7,8 +7,6 @@ from typing import List
import netlib.basetypes import netlib.basetypes
from mitmproxy.models.flow import Flow from mitmproxy.models.flow import Flow
import six
class TCPMessage(netlib.basetypes.Serializable): class TCPMessage(netlib.basetypes.Serializable):
@ -55,22 +53,3 @@ class TCPFlow(Flow):
def __repr__(self): def __repr__(self):
return "<TCPFlow ({} messages)>".format(len(self.messages)) return "<TCPFlow ({} messages)>".format(len(self.messages))
def match(self, f):
"""
Match this flow against a compiled filter expression. Returns True
if matched, False if not.
If f is a string, it will be compiled as a filter expression. If
the expression is invalid, ValueError is raised.
"""
if isinstance(f, six.string_types):
from .. import filt
f = filt.parse(f)
if not f:
raise ValueError("Invalid filter expression.")
if f:
return f(self)
return True

View File

@ -615,6 +615,7 @@ class TestSerialize:
def test_roundtrip(self): def test_roundtrip(self):
sio = io.BytesIO() sio = io.BytesIO()
f = tutils.tflow() f = tutils.tflow()
f.marked = True
f.request.content = bytes(bytearray(range(256))) f.request.content = bytes(bytearray(range(256)))
w = flow.FlowWriter(sio) w = flow.FlowWriter(sio)
w.add(f) w.add(f)
@ -627,6 +628,7 @@ class TestSerialize:
f2 = l[0] f2 = l[0]
assert f2.get_state() == f.get_state() assert f2.get_state() == f.get_state()
assert f2.request == f.request assert f2.request == f.request
assert f2.marked
def test_load_flows(self): def test_load_flows(self):
r = self._treader() r = self._treader()