diff --git a/httpie/cli/nested_json/parse.py b/httpie/cli/nested_json/parse.py index 323a22eef1..5e5a8bb185 100644 --- a/httpie/cli/nested_json/parse.py +++ b/httpie/cli/nested_json/parse.py @@ -164,7 +164,7 @@ def can_advance() -> bool: if index in OPERATORS: yield from send_buffer() yield Token(OPERATORS[index], index, cursor, cursor + 1) - elif index == BACKSLASH and can_advance(): + elif index == BACKSLASH and cursor + 1 < len(source): if source[cursor + 1] in SPECIAL_CHARS: backslashes += 1 else: diff --git a/tests/test_json.py b/tests/test_json.py index e758ebe7f4..4467ba723c 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -581,3 +581,22 @@ def test_nested_json_errors(input_json, expected_error, httpbin): def test_nested_json_sparse_array(httpbin_both): r = http(httpbin_both + '/post', 'test[0]:=1', 'test[100]:=1') assert len(r.json['json']['test']) == 101 + + +def test_nested_json_trailing_backslash_no_crash(): + """A backslash at the very end of a key must not crash with IndexError. + + Regression test for: tokenize() accessed source[cursor + 1] without + first verifying cursor + 1 < len(source). + """ + from httpie.cli.nested_json.parse import tokenize, parse + + trailing_bs = 'key' + chr(92) # 'key\' + + # tokenize must not raise IndexError + tokens = list(tokenize(trailing_bs)) + assert tokens # produced at least one token + + # parse must also be safe + paths = list(parse(trailing_bs)) + assert paths