replacements addon: rename to ModifyBody

This commit is contained in:
Martin Plattner 2020-06-26 20:10:57 +02:00
parent 96e756ead0
commit b263b0dece
8 changed files with 68 additions and 62 deletions

View File

@ -11,9 +11,9 @@ from mitmproxy.addons import disable_h2c
from mitmproxy.addons import export from mitmproxy.addons import export
from mitmproxy.addons import onboarding from mitmproxy.addons import onboarding
from mitmproxy.addons import proxyauth from mitmproxy.addons import proxyauth
from mitmproxy.addons import replace
from mitmproxy.addons import script from mitmproxy.addons import script
from mitmproxy.addons import serverplayback from mitmproxy.addons import serverplayback
from mitmproxy.addons import modifybody
from mitmproxy.addons import modifyheaders from mitmproxy.addons import modifyheaders
from mitmproxy.addons import stickyauth from mitmproxy.addons import stickyauth
from mitmproxy.addons import stickycookie from mitmproxy.addons import stickycookie
@ -37,9 +37,9 @@ def default_addons():
export.Export(), export.Export(),
onboarding.Onboarding(), onboarding.Onboarding(),
proxyauth.ProxyAuth(), proxyauth.ProxyAuth(),
replace.Replace(),
script.ScriptLoader(), script.ScriptLoader(),
serverplayback.ServerPlayback(), serverplayback.ServerPlayback(),
modifybody.ModifyBody(),
modifyheaders.ModifyHeaders(), modifyheaders.ModifyHeaders(),
stickyauth.StickyAuth(), stickyauth.StickyAuth(),
stickycookie.StickyCookie(), stickycookie.StickyCookie(),

View File

@ -8,11 +8,11 @@ from mitmproxy import ctx
from mitmproxy.utils import strutils from mitmproxy.utils import strutils
def parse_replacements(s): def parse_modify_body(s):
""" """
Returns a (flow_filter, regex, replacement) tuple. Returns a (flow_filter, regex, replacement) tuple.
The general form for a replacements hook is as follows: The general form for a modify_body hook is as follows:
[/flow_filter]/regex/replacement [/flow_filter]/regex/replacement
@ -40,41 +40,42 @@ def parse_replacements(s):
flow_filter, regex, repl = parts flow_filter, regex, repl = parts
else: else:
raise exceptions.OptionsError( raise exceptions.OptionsError(
"Invalid replacements specifier: %s" % s "Invalid modify_body specifier: %s" % s
) )
return flow_filter, regex, repl return flow_filter, regex, repl
class Replace: class ModifyBody:
def __init__(self): def __init__(self):
self.lst = [] self.lst = []
def load(self, loader): def load(self, loader):
loader.add_option( loader.add_option(
"replacements", typing.Sequence[str], [], "modify_body", typing.Sequence[str], [],
""" """
Replacement pattern of the form "[/flow-filter]/regex/replacement", where Replacement pattern of the form "[/flow-filter]/regex/[@]replacement", where
the separator can be any character. the separator can be any character. The @ allows to provide a file path that
is used to read the replacement string.
""" """
) )
def configure(self, updated): def configure(self, updated):
""" """
.replacements is a list of tuples (flow_filter_pattern, regex, repl): .modify_body is a list of tuples (flow_filter_pattern, regex, repl):
flow_filter_pattern: a string specifying a flow filter pattern. flow_filter_pattern: a string specifying a flow filter pattern.
regex: a regular expression, as string. regex: a regular expression, as string.
repl: the replacement string repl: the replacement string
""" """
if "replacements" in updated: if "modify_body" in updated:
lst = [] lst = []
for rep in ctx.options.replacements: for rep in ctx.options.modify_body:
flow_filter_pattern, regex, repl = parse_replacements(rep) flow_filter_pattern, regex, repl = parse_modify_body(rep)
flow_filter = flowfilter.parse(flow_filter_pattern) flow_filter = flowfilter.parse(flow_filter_pattern)
if not flow_filter: if not flow_filter:
raise exceptions.OptionsError( raise exceptions.OptionsError(
"Invalid replacements flow filter: %s" % flow_filter_pattern "Invalid modify_body flow filter: %s" % flow_filter_pattern
) )
try: try:
# We should ideally escape here before trying to compile # We should ideally escape here before trying to compile

View File

@ -75,8 +75,7 @@ def run(
# To make migration from 2.x to 3.0 bearable. # To make migration from 2.x to 3.0 bearable.
if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"): if "-R" in sys.argv and sys.argv[sys.argv.index("-R") + 1].startswith("http"):
print("-R is used for specifying replacements.\n" print("To use mitmproxy in reverse mode please use --mode reverse:SPEC instead")
"To use mitmproxy in reverse mode please use --mode reverse:SPEC instead")
try: try:
args = parser.parse_args(arguments) args = parser.parse_args(arguments)

View File

@ -81,9 +81,9 @@ def common_options(parser, opts):
opts.make_parser(group, "server_replay_nopop") opts.make_parser(group, "server_replay_nopop")
opts.make_parser(group, "server_replay_refresh") opts.make_parser(group, "server_replay_refresh")
# Replacements # Modify Body
group = parser.add_argument_group("Replacements") group = parser.add_argument_group("Modify Body")
opts.make_parser(group, "replacements", metavar="PATTERN", short="R") opts.make_parser(group, "modify_body", metavar="PATTERN", short="B")
# Modify headers # Modify headers
group = parser.add_argument_group("Modify Headers") group = parser.add_argument_group("Modify Headers")

View File

@ -210,8 +210,8 @@ class StatusBar(urwid.WidgetWrap):
r.append("[") r.append("[")
r.append(("heading_key", "H")) r.append(("heading_key", "H"))
r.append("eaders]") r.append("eaders]")
if len(self.master.options.replacements): if len(self.master.options.modify_body):
r.append("[%d replacements]" % len(self.master.options.replacements)) r.append("[%d body modifications]" % len(self.master.options.modify_body))
if creplay: if creplay:
r.append("[") r.append("[")
r.append(("heading_key", "cplayback")) r.append(("heading_key", "cplayback"))

View File

@ -55,6 +55,7 @@ REPLACED = """
--insecure --insecure
-c -c
--replace --replace
--replacements
-i -i
-f -f
--filter --filter
@ -97,6 +98,7 @@ REPLACEMENTS = {
"--insecure": "--ssl-insecure", "--insecure": "--ssl-insecure",
"-c": "-C", "-c": "-C",
"--replace": "--replacements", "--replace": "--replacements",
"--replacements": ["--modify-body", "--modify-headers"],
"-i": "--intercept", "-i": "--intercept",
"-f": "--view-filter", "-f": "--view-filter",
"--filter": "--view-filter", "--filter": "--view-filter",
@ -129,11 +131,15 @@ def check():
for option in REPLACED.splitlines(): for option in REPLACED.splitlines():
if option in args: if option in args:
if isinstance(REPLACEMENTS.get(option), list):
new_options = REPLACEMENTS.get(option)
else:
new_options = [REPLACEMENTS.get(option)]
print( print(
"{} is deprecated.\n" "{} is deprecated.\n"
"Please use `{}` instead.".format( "Please use `{}` instead.".format(
option, option,
REPLACEMENTS.get(option) "` or `".join(new_options)
) )
) )

View File

@ -1,57 +1,57 @@
import pytest import pytest
from mitmproxy.addons import replace from mitmproxy.addons import modifybody
from mitmproxy.test import taddons from mitmproxy.test import taddons
from mitmproxy.test import tflow from mitmproxy.test import tflow
class TestReplace: class TestModifyBody:
def test_parse_replacements(self): def test_parse_modify_body(self):
x = replace.parse_replacements("/foo/bar/voing") x = modifybody.parse_modify_body("/foo/bar/voing")
assert x == ("foo", "bar", "voing") assert x == ("foo", "bar", "voing")
x = replace.parse_replacements("/foo/bar/vo/ing/") x = modifybody.parse_modify_body("/foo/bar/vo/ing/")
assert x == ("foo", "bar", "vo/ing/") assert x == ("foo", "bar", "vo/ing/")
x = replace.parse_replacements("/bar/voing") x = modifybody.parse_modify_body("/bar/voing")
assert x == (".*", "bar", "voing") assert x == (".*", "bar", "voing")
with pytest.raises(Exception, match="Invalid replacements"): with pytest.raises(Exception, match="Invalid modify_body specifier"):
replace.parse_replacements("/") modifybody.parse_modify_body("/")
def test_configure(self): def test_configure(self):
r = replace.Replace() mb = modifybody.ModifyBody()
with taddons.context(r) as tctx: with taddons.context(mb) as tctx:
tctx.configure(r, replacements=["one/two/three"]) tctx.configure(mb, modify_body=["one/two/three"])
with pytest.raises(Exception, match="Invalid replacements flow filter"): with pytest.raises(Exception, match="Invalid modify_body flow filter"):
tctx.configure(r, replacements=["/~b/two/three"]) tctx.configure(mb, modify_body=["/~b/two/three"])
with pytest.raises(Exception, match="Invalid regular expression"): with pytest.raises(Exception, match="Invalid regular expression"):
tctx.configure(r, replacements=["/foo/+/three"]) tctx.configure(mb, modify_body=["/foo/+/three"])
tctx.configure(r, replacements=["/a/b/c/"]) tctx.configure(mb, modify_body=["/a/b/c/"])
def test_simple(self): def test_simple(self):
r = replace.Replace() mb = modifybody.ModifyBody()
with taddons.context(r) as tctx: with taddons.context(mb) as tctx:
tctx.configure( tctx.configure(
r, mb,
replacements=[ modify_body=[
"/~q/foo/bar", "/~q/foo/bar",
"/~s/foo/bar", "/~s/foo/bar",
] ]
) )
f = tflow.tflow() f = tflow.tflow()
f.request.content = b"foo" f.request.content = b"foo"
r.request(f) mb.request(f)
assert f.request.content == b"bar" assert f.request.content == b"bar"
f = tflow.tflow(resp=True) f = tflow.tflow(resp=True)
f.response.content = b"foo" f.response.content = b"foo"
r.response(f) mb.response(f)
assert f.response.content == b"bar" assert f.response.content == b"bar"
def test_order(self): def test_order(self):
r = replace.Replace() mb = modifybody.ModifyBody()
with taddons.context(r) as tctx: with taddons.context(mb) as tctx:
tctx.configure( tctx.configure(
r, mb,
replacements=[ modify_body=[
"/foo/bar", "/foo/bar",
"/bar/baz", "/bar/baz",
"/foo/oh noes!", "/foo/oh noes!",
@ -60,43 +60,43 @@ class TestReplace:
) )
f = tflow.tflow() f = tflow.tflow()
f.request.content = b"foo" f.request.content = b"foo"
r.request(f) mb.request(f)
assert f.request.content == b"baz" assert f.request.content == b"baz"
class TestReplaceFile: class TestModifyBodyFile:
def test_simple(self, tmpdir): def test_simple(self, tmpdir):
r = replace.Replace() mb = modifybody.ModifyBody()
with taddons.context(r) as tctx: with taddons.context(mb) as tctx:
tmpfile = tmpdir.join("replacement") tmpfile = tmpdir.join("replacement")
tmpfile.write("bar") tmpfile.write("bar")
tctx.configure( tctx.configure(
r, mb,
replacements=["/~q/foo/@" + str(tmpfile)] modify_body=["/~q/foo/@" + str(tmpfile)]
) )
f = tflow.tflow() f = tflow.tflow()
f.request.content = b"foo" f.request.content = b"foo"
r.request(f) mb.request(f)
assert f.request.content == b"bar" assert f.request.content == b"bar"
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_nonexistent(self, tmpdir): async def test_nonexistent(self, tmpdir):
r = replace.Replace() mb = modifybody.ModifyBody()
with taddons.context(r) as tctx: with taddons.context(mb) as tctx:
with pytest.raises(Exception, match="Invalid file path"): with pytest.raises(Exception, match="Invalid file path"):
tctx.configure( tctx.configure(
r, mb,
replacements=["/~q/foo/@nonexistent"] modify_body=["/~q/foo/@nonexistent"]
) )
tmpfile = tmpdir.join("replacement") tmpfile = tmpdir.join("replacement")
tmpfile.write("bar") tmpfile.write("bar")
tctx.configure( tctx.configure(
r, mb,
replacements=["/~q/foo/@" + str(tmpfile)] modify_body=["/~q/foo/@" + str(tmpfile)]
) )
tmpfile.remove() tmpfile.remove()
f = tflow.tflow() f = tflow.tflow()
f.request.content = b"foo" f.request.content = b"foo"
r.request(f) mb.request(f)
assert await tctx.master.await_log("could not read") assert await tctx.master.await_log("could not read")

View File

@ -9,7 +9,7 @@ def test_statusbar(monkeypatch):
m = master.ConsoleMaster(o) m = master.ConsoleMaster(o)
m.options.update( m.options.update(
modify_headers=[":~q:foo:bar"], modify_headers=[":~q:foo:bar"],
replacements=[":~q:foo:bar"], modify_body=[":~q:foo:bar"],
ignore_hosts=["example.com", "example.org"], ignore_hosts=["example.com", "example.org"],
tcp_hosts=["example.tcp"], tcp_hosts=["example.tcp"],
intercept="~q", intercept="~q",