Skip to content

Commit a90a82d

Browse files
CHconnabyss
andauthored
Test duplicate Host headers that the mismatch check misses (#1343)
* Test duplicate Host headers that the mismatch check misses 292a408 rejects a request header block with more than one Host header (GHSA-6hr6-w5qg-qmwg), and 92b925e added a test case for it. That case puts the mismatching Host last, so the older :authority/Host mismatch check already rejects it: the test still passes with the new check taken out. Add two cases only the duplicate check catches: the matching Host header last, and two Host headers with no :authority. Both are accepted without the fix. Assisted-by: Claude Code * Assert the duplicate Host error message in the test Move the duplicate Host header cases into their own list and add a test that checks the ProtocolError says "Request header block has multiple Host headers", so it fails if any other header check rejects these blocks instead. The cases stay in the general invalid header list too. Assisted-by: Claude Code --------- Co-authored-by: connabyss <connabyss.code@proton.me>
1 parent 8380ac7 commit a90a82d

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

‎tests/test_invalid_headers.py‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class TestInvalidFrameSequences:
3333
(":method", "GET"),
3434
("user-agent", "someua/0.0.1"),
3535
]
36+
duplicate_host_header_blocks = [
37+
[*base_request_headers, ("host", "example.com"), ("host", "evil.example.com")],
38+
[*base_request_headers, ("host", "evil.example.com"), ("host", "example.com")],
39+
[*[header for header in base_request_headers if header[0] != ":authority"],
40+
("host", "example.com"), ("host", "evil.example.com")],
41+
]
3642
base_invalid_header_blocks = [
3743
[*base_request_headers, ("Uppercase", "name")],
3844
[*base_request_headers, (":late", "pseudo-header")],
@@ -43,7 +49,7 @@ class TestInvalidFrameSequences:
4349
[*base_request_headers, ("transfer-encoding", "gzip")],
4450
[*base_request_headers, ("upgrade", "super-protocol/1.1")],
4551
[*base_request_headers, ("te", "chunked")],
46-
[*base_request_headers, ("host", "example.com"), ("host", "evil.example.com")],
52+
*duplicate_host_header_blocks,
4753
[*base_request_headers, ("host", "notexample.com")],
4854
[*base_request_headers, (" name", "name with leading space")],
4955
[*base_request_headers, ("name ", "name with trailing space")],
@@ -88,6 +94,29 @@ def test_headers_event(self, frame_factory, headers) -> None:
8894
)
8995
assert c.data_to_send() == expected_frame.serialize()
9096

97+
@pytest.mark.parametrize("headers", duplicate_host_header_blocks + [
98+
h2.utilities.utf8_encode_headers(headers) for headers in duplicate_host_header_blocks
99+
])
100+
def test_duplicate_host_headers_rejected(self, frame_factory, headers) -> None:
101+
"""
102+
A request header block with more than one Host header is rejected
103+
by the duplicate Host check, not by any other header check.
104+
"""
105+
c = h2.connection.H2Connection(config=self.server_config)
106+
c.receive_data(frame_factory.preamble())
107+
c.clear_outbound_data_buffer()
108+
109+
f = frame_factory.build_headers_frame(headers)
110+
data = f.serialize()
111+
112+
with pytest.raises(h2.exceptions.ProtocolError, match="Request header block has multiple Host headers"):
113+
c.receive_data(data)
114+
115+
expected_frame = frame_factory.build_goaway_frame(
116+
last_stream_id=1, error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
117+
)
118+
assert c.data_to_send() == expected_frame.serialize()
119+
91120
@pytest.mark.parametrize("headers", invalid_header_blocks)
92121
def test_push_promise_event(self, frame_factory, headers) -> None:
93122
"""

0 commit comments

Comments
 (0)