@@ -54,6 +54,28 @@ fn format_expiration(ts: DateTime<Utc>) -> String {
5454 ts. format ( "%Y-%m-%dT%H:%M:%SZ" ) . to_string ( )
5555}
5656
57+ /// Enforce the AWS `RoleSessionName` pattern `[\w+=,.@-]*`. Without this, a
58+ /// name containing `<`, `>` or `&` is interpolated raw into the AssumedRoleId /
59+ /// assumed-role ARN in the XML response, producing malformed (and, for the
60+ /// attacker-controlled SAML session name, injectable) XML that SDK parsers
61+ /// reject. AWS rejects such input up front with a ValidationError.
62+ fn validate_session_name ( name : & str ) -> Result < ( ) , AwsServiceError > {
63+ if name
64+ . chars ( )
65+ . all ( |c| c. is_ascii_alphanumeric ( ) || matches ! ( c, '_' | '+' | '=' | ',' | '.' | '@' | '-' ) )
66+ {
67+ return Ok ( ( ) ) ;
68+ }
69+ Err ( AwsServiceError :: aws_error (
70+ StatusCode :: BAD_REQUEST ,
71+ "ValidationError" ,
72+ format ! (
73+ "1 validation error detected: Value '{name}' at 'roleSessionName' failed to satisfy \
74+ constraint: Member must satisfy regular expression pattern: [\\ w+=,.@-]*"
75+ ) ,
76+ ) )
77+ }
78+
5779/// Test-only wrapper around [`compute_expiration_at`] used by the existing
5880/// duration unit tests.
5981#[ cfg( test) ]
@@ -589,6 +611,20 @@ fn extract_saml_session_name(saml_b64: &str) -> Option<String> {
589611mod tests {
590612 use super :: * ;
591613
614+ #[ test]
615+ fn session_name_pattern_accepts_valid_and_rejects_xml_metacharacters ( ) {
616+ // AWS-legal characters pass.
617+ for ok in [ "testuser" , "role.session-1" , "a+b=c,d.e@f" , "user_123" ] {
618+ assert ! ( validate_session_name( ok) . is_ok( ) , "should accept {ok}" ) ;
619+ }
620+ // XML metacharacters (and anything else off-pattern) are rejected with
621+ // ValidationError rather than injected raw into the response XML.
622+ for bad in [ "a<b" , "a>b" , "a&b" , "a\" b" , "a b" , "a/b" ] {
623+ let err = validate_session_name ( bad) . unwrap_err ( ) ;
624+ assert_eq ! ( err. code( ) , "ValidationError" , "should reject {bad:?}" ) ;
625+ }
626+ }
627+
592628 #[ test]
593629 fn test_partition_for_region ( ) {
594630 assert_eq ! ( partition_for_region( "us-east-1" ) , "aws" ) ;
0 commit comments