Skip to content

Commit 1d27698

Browse files
Reject control characters in header names and values
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 986f826 commit 1d27698

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

hyperbrowser/header_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def normalize_headers(
3131
or "\r" in value
3232
):
3333
raise HyperbrowserError("headers must not contain newline characters")
34+
if any(
35+
ord(character) < 32 or ord(character) == 127
36+
for character in f"{normalized_key}{value}"
37+
):
38+
raise HyperbrowserError("headers must not contain control characters")
3439
canonical_header_name = normalized_key.lower()
3540
if canonical_header_name in seen_header_names:
3641
raise HyperbrowserError(

tests/test_config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@ def test_client_config_rejects_newline_header_values():
254254
ClientConfig(api_key="test-key", headers={"X-Correlation-Id": "bad\nvalue"})
255255

256256

257+
def test_client_config_rejects_control_character_header_values():
258+
with pytest.raises(
259+
HyperbrowserError, match="headers must not contain control characters"
260+
):
261+
ClientConfig(api_key="test-key", headers={"X-Correlation-Id": "bad\tvalue"})
262+
263+
257264
def test_client_config_normalizes_header_name_whitespace():
258265
config = ClientConfig(api_key="test-key", headers={" X-Correlation-Id ": "value"})
259266

tests/test_custom_headers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def test_sync_transport_rejects_header_newline_values():
4747
SyncTransport(api_key="test-key", headers={"X-Correlation-Id": "bad\nvalue"})
4848

4949

50+
def test_sync_transport_rejects_header_control_character_values():
51+
with pytest.raises(
52+
HyperbrowserError, match="headers must not contain control characters"
53+
):
54+
SyncTransport(api_key="test-key", headers={"X-Correlation-Id": "bad\tvalue"})
55+
56+
5057
def test_async_transport_accepts_custom_headers():
5158
async def run() -> None:
5259
transport = AsyncTransport(
@@ -87,6 +94,13 @@ def test_async_transport_rejects_header_newline_values():
8794
AsyncTransport(api_key="test-key", headers={"X-Correlation-Id": "bad\nvalue"})
8895

8996

97+
def test_async_transport_rejects_header_control_character_values():
98+
with pytest.raises(
99+
HyperbrowserError, match="headers must not contain control characters"
100+
):
101+
AsyncTransport(api_key="test-key", headers={"X-Correlation-Id": "bad\tvalue"})
102+
103+
90104
def test_sync_client_config_headers_are_applied_to_transport():
91105
client = Hyperbrowser(
92106
config=ClientConfig(api_key="test-key", headers={"X-Team-Trace": "team-1"})

tests/test_header_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ def test_parse_headers_env_json_rejects_non_mapping_payload():
7373
parse_headers_env_json('["bad"]')
7474

7575

76+
def test_normalize_headers_rejects_control_characters():
77+
with pytest.raises(
78+
HyperbrowserError, match="headers must not contain control characters"
79+
):
80+
normalize_headers(
81+
{"X-Trace-Id": "value\twith-tab"},
82+
mapping_error_message="headers must be a mapping of string pairs",
83+
)
84+
85+
86+
def test_parse_headers_env_json_rejects_control_characters():
87+
with pytest.raises(
88+
HyperbrowserError, match="headers must not contain control characters"
89+
):
90+
parse_headers_env_json('{"X-Trace-Id":"bad\\u0000value"}')
91+
92+
7693
def test_merge_headers_replaces_existing_headers_case_insensitively():
7794
merged = merge_headers(
7895
{"User-Agent": "default-sdk", "x-api-key": "test-key"},

0 commit comments

Comments
 (0)