@@ -549,24 +549,35 @@ def test_header_value_sanitization_rfc_compliant(self):
549549 result = sanitize_header_value (input_val )
550550 assert result == expected
551551
552- def test_header_value_preserves_rfc_folding (self ):
553- """Test that legitimate CRLF sequences for header folding are preserved ."""
554- from google .adk .tools .mcp_tool ._internal import sanitize_header_value
552+ def test_header_value_crlf_stripped_in_provider (self ):
553+ """Test that CRLF in state values is stripped to prevent header injection ."""
554+ from google .adk .tools .mcp_tool .mcp_toolset import create_session_state_header_provider
555555
556- # Multi-line headers with proper folding should be preserved (RFC 7230 §3.2.4)
557- folding_cases = [
558- ("Authorization: Bearer token" , "Authorization: Bearer token" ),
559- ("X-Custom: value1\r \n \t value2" , "X-Custom: value1\r \n \t value2" ),
560- ("X-Header: line1\r \n line2" , "X-Header: line1\r \n line2" ),
561- (
562- "Content-Type: application/json\r \n \t charset=utf-8" ,
563- "Content-Type: application/json\r \n \t charset=utf-8" ,
564- ),
556+ # CRLF in the token value should be stripped from the final header
557+ malicious_cases = [
558+ ("tok\r \n X-Injected: evil" , "Bearer tokX-Injected: evil" ),
559+ ("tok\n Injected: bad" , "Bearer tokInjected: bad" ),
560+ ("tok\r Another: header" , "Bearer tokAnother: header" ),
561+ ("tok\r \n " , "Bearer tok" ),
562+ ("tok\n " , "Bearer tok" ),
563+ ("tok\r " , "Bearer tok" ),
565564 ]
566565
567- for folding_case in folding_cases :
568- result = sanitize_header_value (folding_case [0 ])
569- assert result == folding_case [1 ]
566+ for malicious_value , expected_header in malicious_cases :
567+ mock_context = Mock (spec = ReadonlyContext )
568+ mock_context .state = {"jwt_token" : malicious_value }
569+ provider = create_session_state_header_provider (state_key = "jwt_token" )
570+ headers = provider (mock_context )
571+ assert headers == {
572+ "Authorization" : expected_header
573+ }, f"Failed for value { malicious_value !r} "
574+
575+ # Clean values should be unchanged
576+ mock_context = Mock (spec = ReadonlyContext )
577+ mock_context .state = {"jwt_token" : "clean-token-123" }
578+ provider = create_session_state_header_provider (state_key = "jwt_token" )
579+ headers = provider (mock_context )
580+ assert headers == {"Authorization" : "Bearer clean-token-123" }
570581
571582 def test_header_value_validation_rfc_compliant (self ):
572583 """Test that header value validation is RFC 7230 compliant."""
@@ -659,8 +670,8 @@ def test_binary_data_handling(self):
659670
660671 def test_header_format_crlf_injection_protection (self ):
661672 """Test that header format strings with CRLF sequences are rejected."""
662- from google .adk .tools .mcp_tool .mcp_toolset import create_session_state_header_provider
663673 from google .adk .tools .mcp_tool ._internal import validate_header_format
674+ from google .adk .tools .mcp_tool .mcp_toolset import create_session_state_header_provider
664675
665676 # Valid format strings should be accepted
666677 valid_formats = [
@@ -691,5 +702,5 @@ def test_header_format_crlf_injection_protection(self):
691702 create_session_state_header_provider (
692703 state_key = "token" ,
693704 header_name = "Authorization" ,
694- header_format = "Bearer {value}\r \n X-Injected: evil"
705+ header_format = "Bearer {value}\r \n X-Injected: evil" ,
695706 )
0 commit comments