From c7ffc228194574d1cff2c2fbb05fc3419c4805f4 Mon Sep 17 00:00:00 2001 From: Henrique Date: Tue, 12 Nov 2019 18:57:39 -0500 Subject: Fix for issues when using \ and " on the commander bar --- mitmproxy/command.py | 58 ++++++++++++++++++++++++-- mitmproxy/flowfilter.py | 1 + mitmproxy/tools/console/commander/commander.py | 13 ++---- test/mitmproxy/test_command.py | 8 ++-- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/mitmproxy/command.py b/mitmproxy/command.py index 27f0921d..b287c740 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -13,6 +13,46 @@ import sys from mitmproxy import exceptions import mitmproxy.types +def maybequote(value): + """ + This function takes the output from the lexer and puts it between quotes + in the following cases: + * There is a space in the string: The only way a token from the lexer can have a space in it is if it was between quotes + * There is one or more quotes in the middle of the string: The only way for a token to have a quote in it that is not escaped is if it was escaped prior to being processed by the lexer. For example, the string `"s1 \" s2"` would come back from the lexer as `s1 " s2`. + + Any quotes that are in the middle of the string and that are not escaped will also be escaped (by placing a \ in front of it). + This function only deals with double quotes and they are the only ones that should be used. + """ + + new_value = "" + last_pos = len(value) - 1 + + for pos, char in enumerate(value): + if pos == 0: + new_value += char + continue + + # if pos == last_pos: + # new_value += char + # break + + if char in " \n\r\t": + new_value += char + continue + + if char == '"': + if value[pos-1] != '\\': + new_value += '\\' + + new_value += char + + value = new_value + + if ((" " in value) or ('"' in value)) and not (value.startswith("\"") or value.startswith("'")): + return "\"%s\"" % value + + return value + def verify_arg_signature(f: typing.Callable, args: list, kwargs: dict) -> None: sig = inspect.signature(f) @@ -201,9 +241,11 @@ class CommandManager(mitmproxy.types._CommandBase): else: valid = True + # if ctx.log: + # ctx.log.info('[gilga] before parse.append. value = %s' % parts[i]) parse.append( ParseResult( - value=parts[i], + value=maybequote(parts[i]), type=typ, valid=valid, ) @@ -236,13 +278,21 @@ class CommandManager(mitmproxy.types._CommandBase): """ Execute a command string. May raise CommandError. """ + if cmdstr == '': + raise exceptions.CommandError("Invalid command: %s" % cmdstr) + try: - parts = list(lexer(cmdstr)) + parts, _ = self.parse_partial(cmdstr) except ValueError as e: raise exceptions.CommandError("Command error: %s" % e) - if not len(parts) >= 1: + if len(parts) == 0: raise exceptions.CommandError("Invalid command: %s" % cmdstr) - return self.call_strings(parts[0], parts[1:]) + + params = [] + for p in parts: + params.append(p.value) + + return self.call_strings(params[0], params[1:]) def dump(self, out=sys.stdout) -> None: cmds = list(self.commands.values()) diff --git a/mitmproxy/flowfilter.py b/mitmproxy/flowfilter.py index 0d8f1062..b3f143af 100644 --- a/mitmproxy/flowfilter.py +++ b/mitmproxy/flowfilter.py @@ -501,6 +501,7 @@ def _make(): pp.Word(alphlatinB) |\ pp.QuotedString("\"", escChar='\\') |\ pp.QuotedString("'", escChar='\\') + for klass in filter_rex: f = pp.Literal("~%s" % klass.code) + pp.WordEnd() + rex.copy() f.setParseAction(klass.make) diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index e8550f86..0c043081 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -70,16 +70,11 @@ class CommandBuffer: else: self._cursor = x - def maybequote(self, value): - if " " in value and not value.startswith("\""): - return "\"%s\"" % value - return value - def parse_quoted(self, txt): parts, remhelp = self.master.commands.parse_partial(txt) for i, p in enumerate(parts): parts[i] = mitmproxy.command.ParseResult( - value = self.maybequote(p.value), + value = p.value, type = p.type, valid = p.valid ) @@ -145,7 +140,7 @@ class CommandBuffer: def backspace(self) -> None: if self.cursor == 0: return - self.text = self.flatten(self.text[:self.cursor - 1] + self.text[self.cursor:]) + self.text = self.text[:self.cursor - 1] + self.text[self.cursor:] self.cursor = self.cursor - 1 self.completion = None @@ -153,8 +148,8 @@ class CommandBuffer: """ Inserts text at the cursor. """ - self.text = self.flatten(self.text[:self.cursor] + k + self.text[self.cursor:]) - self.cursor += 1 + self.text = self.text[:self.cursor] + k + self.text[self.cursor:] + self.cursor += len(k) self.completion = None diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index d9dcf5f9..2785e28f 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -249,10 +249,10 @@ class TestCommand: ["str"] ], [ - "flow \"one two\"", + "flow \"three four\"", [ command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), - command.ParseResult(value = "one two", type = flow.Flow, valid = False), + command.ParseResult(value = '"three four"', type = flow.Flow, valid = False), ], ["str"] ], @@ -270,7 +270,7 @@ def test_simple(): c = command.CommandManager(tctx.master) a = TAddon() c.add("one.two", a.cmd1) - assert c.commands["one.two"].help == "cmd1 help" + assert(c.commands["one.two"].help == "cmd1 help") assert(c.execute("one.two foo") == "ret foo") assert(c.call("one.two", "foo") == "ret foo") with pytest.raises(exceptions.CommandError, match="Unknown"): @@ -281,7 +281,7 @@ def test_simple(): c.execute("one.two too many args") with pytest.raises(exceptions.CommandError, match="Unknown"): c.call("nonexistent") - with pytest.raises(exceptions.CommandError, match="No escaped"): + with pytest.raises(exceptions.CommandError, match="Unknown"): c.execute("\\") c.add("empty", a.empty) -- cgit v1.2.3