mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 02:10:59 +00:00
Add flow.comment
command and keybinding to add a comment to a flow. (#4608)
* Add `flow.comment` command and keybinding to add a comment to a flow. * Store comment in Flow().comment. Add ~comment flowfilter syntax. * resolve: Pythonic flow.comment * Be consistent and use comment variable.
This commit is contained in:
parent
4f60e52413
commit
6d2b823a54
@ -66,6 +66,7 @@ If you depend on these features, please raise your voice in
|
|||||||
* Fix parsing of certificate issuer/subject with escaped special characters (@Prinzhorn)
|
* Fix parsing of certificate issuer/subject with escaped special characters (@Prinzhorn)
|
||||||
* Customize markers with emoji, and filters: The `flow.mark` command may be used to mark a flow with either the default
|
* Customize markers with emoji, and filters: The `flow.mark` command may be used to mark a flow with either the default
|
||||||
"red ball" marker, a single character, or an emoji like `:grapes:`. Use the `~marker` filter to filter on marker characters. (@rbdixon)
|
"red ball" marker, a single character, or an emoji like `:grapes:`. Use the `~marker` filter to filter on marker characters. (@rbdixon)
|
||||||
|
* New `flow.comment` command to add a comment to the flow. Add `~comment <regex>` filter syntax to search flow comments. (@rbdixon)
|
||||||
* --- TODO: add new PRs above this line ---
|
* --- TODO: add new PRs above this line ---
|
||||||
* ... and various other fixes, documentation improvements, dependency version bumps, etc.
|
* ... and various other fixes, documentation improvements, dependency version bumps, etc.
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from mitmproxy.addons import blocklist
|
|||||||
from mitmproxy.addons import browser
|
from mitmproxy.addons import browser
|
||||||
from mitmproxy.addons import clientplayback
|
from mitmproxy.addons import clientplayback
|
||||||
from mitmproxy.addons import command_history
|
from mitmproxy.addons import command_history
|
||||||
|
from mitmproxy.addons import comment
|
||||||
from mitmproxy.addons import core
|
from mitmproxy.addons import core
|
||||||
from mitmproxy.addons import cut
|
from mitmproxy.addons import cut
|
||||||
from mitmproxy.addons import disable_h2c
|
from mitmproxy.addons import disable_h2c
|
||||||
@ -37,6 +38,7 @@ def default_addons():
|
|||||||
anticomp.AntiComp(),
|
anticomp.AntiComp(),
|
||||||
clientplayback.ClientPlayback(),
|
clientplayback.ClientPlayback(),
|
||||||
command_history.CommandHistory(),
|
command_history.CommandHistory(),
|
||||||
|
comment.Comment(),
|
||||||
cut.Cut(),
|
cut.Cut(),
|
||||||
disable_h2c.DisableH2C(),
|
disable_h2c.DisableH2C(),
|
||||||
export.Export(),
|
export.Export(),
|
||||||
|
16
mitmproxy/addons/comment.py
Normal file
16
mitmproxy/addons/comment.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import typing
|
||||||
|
from mitmproxy import command, flow, ctx
|
||||||
|
from mitmproxy.hooks import UpdateHook
|
||||||
|
|
||||||
|
|
||||||
|
class Comment:
|
||||||
|
@command.command("flow.comment")
|
||||||
|
def comment(self, flow: typing.Sequence[flow.Flow], comment: str) -> None:
|
||||||
|
"Add a comment to a flow"
|
||||||
|
|
||||||
|
updated = []
|
||||||
|
for f in flow:
|
||||||
|
f.comment = comment
|
||||||
|
updated.append(f)
|
||||||
|
|
||||||
|
ctx.master.addons.trigger(UpdateHook(updated))
|
@ -123,6 +123,7 @@ class Flow(stateobject.StateObject):
|
|||||||
self.marked: str = ""
|
self.marked: str = ""
|
||||||
self.is_replay: typing.Optional[str] = None
|
self.is_replay: typing.Optional[str] = None
|
||||||
self.metadata: typing.Dict[str, typing.Any] = dict()
|
self.metadata: typing.Dict[str, typing.Any] = dict()
|
||||||
|
self.comment: str = ""
|
||||||
|
|
||||||
_stateobject_attributes = dict(
|
_stateobject_attributes = dict(
|
||||||
id=str,
|
id=str,
|
||||||
@ -134,6 +135,7 @@ class Flow(stateobject.StateObject):
|
|||||||
is_replay=str,
|
is_replay=str,
|
||||||
marked=str,
|
marked=str,
|
||||||
metadata=typing.Dict[str, typing.Any],
|
metadata=typing.Dict[str, typing.Any],
|
||||||
|
comment=str,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
|
@ -425,6 +425,16 @@ class FMarker(_Rex):
|
|||||||
return self.re.search(f.marked)
|
return self.re.search(f.marked)
|
||||||
|
|
||||||
|
|
||||||
|
class FComment(_Rex):
|
||||||
|
code = "comment"
|
||||||
|
help = "Flow comment"
|
||||||
|
flags = re.MULTILINE
|
||||||
|
is_binary = False
|
||||||
|
|
||||||
|
def __call__(self, f):
|
||||||
|
return self.re.search(f.comment)
|
||||||
|
|
||||||
|
|
||||||
class _Int(_Action):
|
class _Int(_Action):
|
||||||
|
|
||||||
def __init__(self, num):
|
def __init__(self, num):
|
||||||
@ -512,6 +522,7 @@ filter_rex: Sequence[Type[_Rex]] = [
|
|||||||
FUrl,
|
FUrl,
|
||||||
FMeta,
|
FMeta,
|
||||||
FMarker,
|
FMarker,
|
||||||
|
FComment,
|
||||||
]
|
]
|
||||||
filter_int = [
|
filter_int = [
|
||||||
FCode
|
FCode
|
||||||
|
@ -309,6 +309,12 @@ def convert_12_13(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def convert_13_14(data):
|
||||||
|
data["version"] = 14
|
||||||
|
data["comment"] = ""
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def _convert_dict_keys(o: Any) -> Any:
|
def _convert_dict_keys(o: Any) -> Any:
|
||||||
if isinstance(o, dict):
|
if isinstance(o, dict):
|
||||||
return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()}
|
return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()}
|
||||||
@ -369,6 +375,7 @@ converters = {
|
|||||||
10: convert_10_11,
|
10: convert_10_11,
|
||||||
11: convert_11_12,
|
11: convert_11_12,
|
||||||
12: convert_12_13,
|
12: convert_12_13,
|
||||||
|
13: convert_13_14,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
def map(km):
|
def map(km):
|
||||||
km.add(":", "console.command ", ["commonkey", "global"], "Command prompt")
|
km.add(":", "console.command ", ["commonkey", "global"], "Command prompt")
|
||||||
|
km.add(";", "console.command flow.comment @focus ''", ["flowlist", "flowview"], "Add comment to flow")
|
||||||
km.add("?", "console.view.help", ["global"], "View help")
|
km.add("?", "console.view.help", ["global"], "View help")
|
||||||
km.add("B", "browser.start", ["global"], "Start an attached browser")
|
km.add("B", "browser.start", ["global"], "Start an attached browser")
|
||||||
km.add("C", "console.view.commands", ["global"], "View commands")
|
km.add("C", "console.view.commands", ["global"], "View commands")
|
||||||
|
@ -33,6 +33,10 @@ def flowdetails(state, flow: mitmproxy.flow.Flow):
|
|||||||
req = None
|
req = None
|
||||||
resp = None
|
resp = None
|
||||||
metadata = flow.metadata
|
metadata = flow.metadata
|
||||||
|
comment = flow.comment
|
||||||
|
|
||||||
|
if comment:
|
||||||
|
text.append(urwid.Text([("head", "Comment: "), ("text", comment)]))
|
||||||
|
|
||||||
if metadata is not None and len(metadata) > 0:
|
if metadata is not None and len(metadata) > 0:
|
||||||
parts = [(str(k), repr(v)) for k, v in metadata.items()]
|
parts = [(str(k), repr(v)) for k, v in metadata.items()]
|
||||||
|
@ -7,7 +7,7 @@ MITMPROXY = "mitmproxy " + VERSION
|
|||||||
|
|
||||||
# Serialization format version. This is displayed nowhere, it just needs to be incremented by one
|
# Serialization format version. This is displayed nowhere, it just needs to be incremented by one
|
||||||
# for each change in the file format.
|
# for each change in the file format.
|
||||||
FLOW_FORMAT_VERSION = 13
|
FLOW_FORMAT_VERSION = 14
|
||||||
|
|
||||||
|
|
||||||
def get_dev_version() -> str:
|
def get_dev_version() -> str:
|
||||||
|
12
test/mitmproxy/addons/test_comment.py
Normal file
12
test/mitmproxy/addons/test_comment.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from mitmproxy.test import tflow, taddons
|
||||||
|
from mitmproxy.addons.comment import Comment
|
||||||
|
|
||||||
|
|
||||||
|
def test_comment():
|
||||||
|
c = Comment()
|
||||||
|
f = tflow.tflow()
|
||||||
|
|
||||||
|
with taddons.context():
|
||||||
|
c.comment([f], "foo")
|
||||||
|
|
||||||
|
assert f.comment == "foo"
|
@ -35,6 +35,8 @@ class TestSerialize:
|
|||||||
sio = io.BytesIO()
|
sio = io.BytesIO()
|
||||||
f = tflow.tflow()
|
f = tflow.tflow()
|
||||||
f.marked = ":default:"
|
f.marked = ":default:"
|
||||||
|
f.marked = True
|
||||||
|
f.comment = "test comment"
|
||||||
f.request.content = bytes(range(256))
|
f.request.content = bytes(range(256))
|
||||||
w = mitmproxy.io.FlowWriter(sio)
|
w = mitmproxy.io.FlowWriter(sio)
|
||||||
w.add(f)
|
w.add(f)
|
||||||
|
@ -25,6 +25,7 @@ class TestParsing:
|
|||||||
assert flowfilter.parse("~replay")
|
assert flowfilter.parse("~replay")
|
||||||
assert flowfilter.parse("~replayq")
|
assert flowfilter.parse("~replayq")
|
||||||
assert flowfilter.parse("~replays")
|
assert flowfilter.parse("~replays")
|
||||||
|
assert flowfilter.parse("~comment .")
|
||||||
p = flowfilter.parse("~q ~c 10")
|
p = flowfilter.parse("~q ~c 10")
|
||||||
self._dump(p)
|
self._dump(p)
|
||||||
assert len(p.lst) == 2
|
assert len(p.lst) == 2
|
||||||
@ -615,6 +616,10 @@ class TestMatchingDummyFlow:
|
|||||||
|
|
||||||
assert not self.q("~q", f)
|
assert not self.q("~q", f)
|
||||||
|
|
||||||
|
assert not self.q("~comment .", f)
|
||||||
|
f.comment = "comment"
|
||||||
|
assert self.q("~comment .", f)
|
||||||
|
|
||||||
|
|
||||||
@patch('traceback.extract_tb')
|
@patch('traceback.extract_tb')
|
||||||
def test_pyparsing_bug(extract_tb):
|
def test_pyparsing_bug(extract_tb):
|
||||||
|
Loading…
Reference in New Issue
Block a user