mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-01 18:27:18 +00:00
commands: only accept escaped quotes
instead of accepting quotation marks in strings, we now just accept \x22, which is then unescaped by the str type. This greatly simplifies the lexing and is more consistent from a user perspective.
This commit is contained in:
parent
e63437689e
commit
fbb7d3e4c1
6 changed files with 23 additions and 56 deletions
|
|
@ -9,13 +9,9 @@ import pyparsing
|
|||
PartialQuotedString = pyparsing.Regex(
|
||||
re.compile(
|
||||
r'''
|
||||
(["']) # start quote
|
||||
(?:
|
||||
(?:\\.) # escape sequence
|
||||
|
|
||||
(?!\1). # unescaped character that is not our quote nor the begin of an escape sequence. We can't use \1 in []
|
||||
)*
|
||||
(?:\1|$) # end quote
|
||||
"[^"]*(?:"|$) # double-quoted string that ends with double quote or EOF
|
||||
|
|
||||
'[^']*(?:'|$) # single-quoted string that ends with double quote or EOF
|
||||
''',
|
||||
re.VERBOSE
|
||||
)
|
||||
|
|
@ -35,17 +31,11 @@ def quote(val: str) -> str:
|
|||
return f'"{val}"'
|
||||
if "'" not in val:
|
||||
return f"'{val}'"
|
||||
return '"' + re.sub(r'(?<!\\)(\\\\)*"', lambda m: (m.group(1) or "") + '\\"', val) + '"'
|
||||
return '"' + val.replace('"', r"\x22") + '"'
|
||||
|
||||
|
||||
def unquote(x: str) -> str:
|
||||
quote_char = ""
|
||||
if len(x) > 1 and x.startswith('"') and x.endswith('"'):
|
||||
quote_char = '"'
|
||||
if len(x) > 1 and x.startswith("'") and x.endswith("'"):
|
||||
quote_char = "'"
|
||||
|
||||
if quote_char:
|
||||
return re.sub(r"(?<!\\)(\\\\)*\\" + quote_char, lambda m: (m.group(1) or "") + quote_char, x[1:-1])
|
||||
if len(x) > 1 and x[0] in "'\"" and x[0] == x[-1]:
|
||||
return x[1:-1]
|
||||
else:
|
||||
return x
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class _StrType(_BaseType):
|
|||
""", re.VERBOSE)
|
||||
|
||||
@staticmethod
|
||||
def _unescape(match: re.Match[str]) -> str:
|
||||
def _unescape(match: re.Match) -> str:
|
||||
return codecs.decode(match.group(0), "unicode-escape") # type: ignore
|
||||
|
||||
def completion(self, manager: "CommandManager", t: type, s: str) -> typing.Sequence[str]:
|
||||
|
|
|
|||
|
|
@ -367,24 +367,6 @@ class TestCommand:
|
|||
],
|
||||
[],
|
||||
],
|
||||
[
|
||||
r'cmd13 "a \"b\" c"',
|
||||
[
|
||||
command.ParseResult(value="cmd13", type=mitmproxy.types.Cmd, valid=False),
|
||||
command.ParseResult(value=" ", type=mitmproxy.types.Space, valid=True),
|
||||
command.ParseResult(value=r'"a \"b\" c"', type=mitmproxy.types.Unknown, valid=False),
|
||||
],
|
||||
[],
|
||||
],
|
||||
[
|
||||
r"cmd14 'a \'b\' c'",
|
||||
[
|
||||
command.ParseResult(value="cmd14", type=mitmproxy.types.Cmd, valid=False),
|
||||
command.ParseResult(value=" ", type=mitmproxy.types.Space, valid=True),
|
||||
command.ParseResult(value=r"'a \'b\' c'", type=mitmproxy.types.Unknown, valid=False),
|
||||
],
|
||||
[],
|
||||
],
|
||||
[
|
||||
" spaces_at_the_begining_are_not_stripped",
|
||||
[
|
||||
|
|
@ -436,12 +418,6 @@ def test_simple():
|
|||
c.call("nonexistent")
|
||||
with pytest.raises(exceptions.CommandError, match="Unknown"):
|
||||
c.execute("\\")
|
||||
with pytest.raises(exceptions.CommandError, match="Unknown"):
|
||||
c.execute(r"\'")
|
||||
with pytest.raises(exceptions.CommandError, match="Unknown"):
|
||||
c.execute(r"\"")
|
||||
with pytest.raises(exceptions.CommandError, match="Unknown"):
|
||||
c.execute(r"\"")
|
||||
|
||||
c.add("empty", a.empty)
|
||||
c.execute("empty")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ from mitmproxy import command_lexer
|
|||
("'foo'", True),
|
||||
('"foo"', True),
|
||||
("'foo' bar'", False),
|
||||
("'foo\\' bar'", True),
|
||||
("'foo' 'bar'", False),
|
||||
("'foo'x", False),
|
||||
('''"foo ''', True),
|
||||
|
|
@ -52,8 +51,10 @@ def test_expr(test_input, expected):
|
|||
@example("'foo\\\\'")
|
||||
@example("\"foo\\'\"")
|
||||
@example("\"foo\\\\'\"")
|
||||
@example('\'foo\\"\'')
|
||||
@example(r"\\\foo")
|
||||
def test_quote_unquote_cycle(s):
|
||||
assert command_lexer.unquote(command_lexer.quote(s)) == s
|
||||
assert command_lexer.unquote(command_lexer.quote(s)).replace(r"\x22", '"') == s
|
||||
|
||||
|
||||
@given(text())
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class TestWebSocketMessage:
|
|||
bin = websocket.WebSocketMessage(Opcode.BINARY, True, b"foo")
|
||||
|
||||
assert txt.is_text
|
||||
assert txt.text
|
||||
assert txt.text == "foo"
|
||||
txt.text = "bar"
|
||||
assert txt.content == b"bar"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,18 +20,18 @@ async def test_commands_exist():
|
|||
await m.load_flow(tflow())
|
||||
|
||||
for binding in km.bindings:
|
||||
parsed, _ = command_manager.parse_partial(binding.command.strip())
|
||||
|
||||
cmd = parsed[0].value
|
||||
args = [
|
||||
a.value for a in parsed[1:]
|
||||
if a.type != mitmproxy.types.Space
|
||||
]
|
||||
|
||||
assert cmd in m.commands.commands
|
||||
|
||||
cmd_obj = m.commands.commands[cmd]
|
||||
try:
|
||||
parsed, _ = command_manager.parse_partial(binding.command.strip())
|
||||
|
||||
cmd = parsed[0].value
|
||||
args = [
|
||||
a.value for a in parsed[1:]
|
||||
if a.type != mitmproxy.types.Space
|
||||
]
|
||||
|
||||
assert cmd in m.commands.commands
|
||||
|
||||
cmd_obj = m.commands.commands[cmd]
|
||||
cmd_obj.prepare_args(args)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid command: {binding.command}") from e
|
||||
raise ValueError(f"Invalid binding: {binding.command}") from e
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue