@@ -290,3 +290,59 @@ fn verify_hs256_rfc7517_appendix_a1() {
290290 let c = decode :: < C > ( token, & key, & validation) . unwrap ( ) ;
291291 assert_eq ! ( c. claims. iss, "joe" ) ;
292292}
293+
294+ // Regression tests for type confusion vulnerability where malformed claims
295+ // (eg nbf or exp as strings) were silently treated as "not present" when not required
296+ #[ derive( Debug , Serialize ) ]
297+ struct ClaimsWithStringNbf {
298+ sub : String ,
299+ nbf : String , // should be a number
300+ }
301+
302+ #[ derive( Debug , Serialize ) ]
303+ struct ClaimsWithStringExp {
304+ sub : String ,
305+ exp : String , // should be a number
306+ }
307+
308+ #[ test]
309+ #[ wasm_bindgen_test]
310+ fn test_string_nbf_rejected_when_validate_nbf_enabled ( ) {
311+ // Create token with nbf as string (malformed)
312+ let claims = ClaimsWithStringNbf {
313+ sub : "test" . to_string ( ) ,
314+ nbf : "99999999999" . to_string ( ) , // Far future as string
315+ } ;
316+ let token = encode ( & Header :: default ( ) , & claims, & EncodingKey :: from_secret ( b"secret" ) ) . unwrap ( ) ;
317+
318+ let mut validation = Validation :: new ( Algorithm :: HS256 ) ;
319+ validation. validate_nbf = true ;
320+ validation. required_spec_claims = std:: collections:: HashSet :: new ( ) ;
321+
322+ let result =
323+ decode :: < serde_json:: Value > ( & token, & DecodingKey :: from_secret ( b"secret" ) , & validation) ;
324+
325+ assert ! ( result. is_err( ) ) ;
326+ assert_eq ! ( result. unwrap_err( ) . into_kind( ) , ErrorKind :: InvalidClaimFormat ( "nbf" . to_string( ) ) ) ;
327+ }
328+
329+ #[ test]
330+ #[ wasm_bindgen_test]
331+ fn test_string_exp_rejected_when_validate_exp_enabled ( ) {
332+ // Create token with exp as string (malformed)
333+ let claims = ClaimsWithStringExp {
334+ sub : "test" . to_string ( ) ,
335+ exp : "99999999999" . to_string ( ) , // Far future as string
336+ } ;
337+ let token = encode ( & Header :: default ( ) , & claims, & EncodingKey :: from_secret ( b"secret" ) ) . unwrap ( ) ;
338+
339+ let mut validation = Validation :: new ( Algorithm :: HS256 ) ;
340+ validation. validate_exp = true ;
341+ validation. required_spec_claims = std:: collections:: HashSet :: new ( ) ;
342+
343+ let result =
344+ decode :: < serde_json:: Value > ( & token, & DecodingKey :: from_secret ( b"secret" ) , & validation) ;
345+
346+ assert ! ( result. is_err( ) ) ;
347+ assert_eq ! ( result. unwrap_err( ) . into_kind( ) , ErrorKind :: InvalidClaimFormat ( "exp" . to_string( ) ) ) ;
348+ }
0 commit comments