From 6c478f739aa191afedae91174348d75d9fab4366 Mon Sep 17 00:00:00 2001 From: JSap0914 Date: Wed, 17 Jun 2026 15:19:20 +0900 Subject: [PATCH 1/2] Fix IndexError when nested JSON key ends with a trailing backslash tokenize() in httpie/cli/nested_json/parse.py guarded the backslash branch with `can_advance()`, which tests `cursor < len(source)`. Because we are already inside the `while can_advance()` loop that guard is always True, so when the input ends with a backslash the code immediately tries to read `source[cursor + 1]` and raises IndexError: string index out of range Fix: replace `can_advance()` with `cursor + 1 < len(source)` so the check actually tests for the existence of the *next* character. When no next character exists the backslash falls through to the else-branch and is appended to the buffer as a literal character, matching the behaviour of KeyValueArgType.tokenize() in argtypes.py. Adds a regression test that exercises both tokenize() and parse() with a key ending in a backslash without requiring a live server. --- httpie/cli/nested_json/parse.py | 2 +- tests/test_json.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) 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..d946794314 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -581,3 +581,21 @@ 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 From e16491a1e45e469016d7a5ba72b5864292c5cc90 Mon Sep 17 00:00:00 2001 From: JSap0914 Date: Wed, 17 Jun 2026 15:53:25 +0900 Subject: [PATCH 2/2] style: add missing blank line before test function (E302) --- tests/test_json.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_json.py b/tests/test_json.py index d946794314..4467ba723c 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -582,6 +582,7 @@ 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.