@@ -184,6 +184,128 @@ async def test_invalidate_resource_cache_clears_entries():
184184 assert main .resource_cache .get ("/resource2" ) is None
185185
186186
187+ def test_validate_http_headers_valid ():
188+ """Test _validate_http_headers with valid headers."""
189+ headers = {
190+ "Content-Type" : "application/json" ,
191+ "Authorization" : "Bearer token123" ,
192+ "X-Custom-Header" : "value with spaces" ,
193+ }
194+ result = main ._validate_http_headers (headers )
195+ assert result == headers
196+
197+
198+ def test_validate_http_headers_invalid_name ():
199+ """Test _validate_http_headers rejects invalid header names (line 1435-1436)."""
200+ # Invalid header name with space
201+ headers = {"Invalid Name" : "value" }
202+ result = main ._validate_http_headers (headers )
203+ assert result is None
204+
205+ # Invalid header name with special characters not in RFC 9110 token
206+ headers = {"Invalid@Header" : "value" }
207+ result = main ._validate_http_headers (headers )
208+ assert result is None
209+
210+ # Mix of valid and invalid headers
211+ headers = {
212+ "Valid-Header" : "value1" ,
213+ "Invalid Name" : "value2" ,
214+ "Another-Valid" : "value3" ,
215+ }
216+ result = main ._validate_http_headers (headers )
217+ assert result == {"Valid-Header" : "value1" , "Another-Valid" : "value3" }
218+
219+
220+ def test_validate_http_headers_crlf_in_value ():
221+ """Test _validate_http_headers rejects CRLF in header values (line 1439-1440)."""
222+ # Header value with carriage return
223+ headers = {"Content-Type" : "application/json\r injection" }
224+ result = main ._validate_http_headers (headers )
225+ assert result is None
226+
227+ # Header value with newline
228+ headers = {"Authorization" : "Bearer token\n injection" }
229+ result = main ._validate_http_headers (headers )
230+ assert result is None
231+
232+ # Header value with both CRLF
233+ headers = {"X-Custom" : "value\r \n injection" }
234+ result = main ._validate_http_headers (headers )
235+ assert result is None
236+
237+ # Mix of valid and invalid headers
238+ headers = {
239+ "Valid-Header" : "clean value" ,
240+ "Invalid-Header" : "value\r \n injection" ,
241+ "Another-Valid" : "another clean value" ,
242+ }
243+ result = main ._validate_http_headers (headers )
244+ assert result == {"Valid-Header" : "clean value" , "Another-Valid" : "another clean value" }
245+
246+
247+ def test_validate_http_headers_ctl_characters ():
248+ """Test _validate_http_headers rejects CTL characters in values (line 1447-1448, 1450)."""
249+ # Header value with null byte (0x00)
250+ headers = {"Content-Type" : "application/json\x00 " }
251+ result = main ._validate_http_headers (headers )
252+ assert result is None
253+
254+ # Header value with control character (0x01)
255+ headers = {"Authorization" : "Bearer\x01 token" }
256+ result = main ._validate_http_headers (headers )
257+ assert result is None
258+
259+ # Header value with DEL character (0x7F)
260+ headers = {"X-Custom" : "value\x7f " }
261+ result = main ._validate_http_headers (headers )
262+ assert result is None
263+
264+ # Header value with various CTL characters (0x00-0x1F except tab and space)
265+ for code in range (0 , 32 ):
266+ if code in (9 , 32 ): # Skip tab and space (allowed)
267+ continue
268+ headers = {"Test-Header" : f"value{ chr (code )} end" }
269+ result = main ._validate_http_headers (headers )
270+ assert result is None , f"Should reject CTL character 0x{ code :02x} "
271+
272+ # Header value with tab (0x09) - should be allowed
273+ headers = {"Content-Type" : "application/json\t charset=utf-8" }
274+ result = main ._validate_http_headers (headers )
275+ assert result == headers
276+
277+ # Header value with space (0x20) - should be allowed
278+ headers = {"Authorization" : "Bearer token with spaces" }
279+ result = main ._validate_http_headers (headers )
280+ assert result == headers
281+
282+ # Mix of valid and invalid headers
283+ headers = {
284+ "Valid-Header" : "clean value" ,
285+ "Invalid-Header" : "value\x01 injection" ,
286+ "Another-Valid" : "another clean value" ,
287+ }
288+ result = main ._validate_http_headers (headers )
289+ assert result == {"Valid-Header" : "clean value" , "Another-Valid" : "another clean value" }
290+
291+
292+ def test_validate_http_headers_empty_dict ():
293+ """Test _validate_http_headers with empty dictionary."""
294+ result = main ._validate_http_headers ({})
295+ assert result is None
296+
297+
298+ def test_validate_http_headers_all_invalid ():
299+ """Test _validate_http_headers when all headers are invalid."""
300+ headers = {
301+ "Invalid Name" : "value1" ,
302+ "Valid-But-Bad-Value" : "value\r \n injection" ,
303+ "Another-Invalid" : "value\x00 " ,
304+ }
305+ result = main ._validate_http_headers (headers )
306+ assert result is None
307+
308+
187309# ---------------------------------------------------------------------------
188310# tojson_attr filter tests
189311# ---------------------------------------------------------------------------
0 commit comments