Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
This document records all notable changes to [HTTPie](https://httpie.io).
This project adheres to [Semantic Versioning](https://semver.org/).

## [3.2.5-dev](https://github.com/httpie/cli/compare/3.2.4...master) (unreleased)

- Fixed nested JSON keys that look like numbers but are not in canonical form (e.g. `007`, `1_000`, `+5`) being silently normalized instead of kept verbatim.

## [3.2.4](https://github.com/httpie/cli/compare/3.2.3...3.2.4) (2024-11-01)

- Fix default certs loading and unpin `requests`. ([#1596](https://github.com/httpie/cli/issues/1596))
Expand Down
18 changes: 17 additions & 1 deletion httpie/cli/nested_json/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def send_buffer() -> Iterator[Token]:
kind = TokenKind.TEXT
if not backslashes:
for variation, kind in [
(int, TokenKind.NUMBER),
(parse_number, TokenKind.NUMBER),
(check_escaped_int, TokenKind.TEXT),
]:
try:
Expand Down Expand Up @@ -178,6 +178,22 @@ def can_advance() -> bool:
yield from send_buffer()


def parse_number(value: str) -> int:
"""Parse ``value`` as an integer, but only when it is written in its
canonical form.

Unlike the built-in ``int()``, this rejects representations such as
``007`` (leading zeros), ``1_000`` (digit-group separators), ``+5``
(explicit sign) or values padded with whitespace. Keeping those as plain
text preserves numeric-looking object keys verbatim instead of silently
normalizing them (e.g. ``007`` used to become ``7``).
"""
number = int(value)
if value != str(number):
raise ValueError('Not a canonical integer')
return number


def check_escaped_int(value: str) -> str:
if not value.startswith(BACKSLASH):
raise ValueError('Not an escaped int')
Expand Down
9 changes: 9 additions & 0 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ def test_complex_json_arguments_with_non_json(httpbin, request_type, value):
'2': {'3': {'4': 5}},
},
),
(
# Numeric-looking keys are only treated as array indexes when
# written in their canonical form. Non-canonical variants (leading
# zeros, digit separators, explicit signs, surrounding whitespace)
# are kept verbatim as object keys instead of being normalized by
# `int()` (e.g. `007` must not become `7`).
['007=1', '00=2', '1_000=3', '+5=4', 'x[007]=6'],
{'007': '1', '00': '2', '1_000': '3', '+5': '4', 'x': {'007': '6'}},
),
(
[':={"foo": {"bar": "baz"}}', 'top=val'],
{'': {'foo': {'bar': 'baz'}}, 'top': 'val'},
Expand Down
Loading