@@ -412,6 +412,49 @@ class AuthChallengeParserTest {
412412 )
413413 }
414414
415+ @Test
416+ fun `stray token after an unquoted param is rejected not parsed as a phantom challenge` () {
417+ // `Digest realm=value extra` — `realm=value` is a valid auth-param, but `extra`
418+ // is a bare token with no separating comma. RFC 7235 §2.1 permits only a comma
419+ // (or EOF) after an auth-param, so the trailing token is malformed. The parser
420+ // skips it (rather than silently dropping it and re-reading `extra` as the scheme
421+ // of a second challenge) and emits the single Digest challenge with `realm=value`.
422+ val challenges = AuthChallengeParser .parse(" Digest realm=value extra" )
423+ assertEquals(1 , challenges.size, " the stray token must not produce a second challenge" )
424+ assertEquals(" digest" , challenges[0 ].scheme)
425+ assertEquals(" value" , challenges[0 ].parameters[" realm" ])
426+ assertTrue(
427+ challenges.none { it.scheme == " extra" },
428+ " the stray trailing token must not be parsed as a phantom challenge scheme" ,
429+ )
430+ }
431+
432+ @Test
433+ fun `stray token after a quoted param is rejected not parsed as a phantom challenge` () {
434+ // Quoted-value variant of the stray-trailing-token case: `Digest realm="value" extra`.
435+ val challenges = AuthChallengeParser .parse(""" Digest realm="value" extra""" )
436+ assertEquals(1 , challenges.size, " the stray token must not produce a second challenge" )
437+ assertEquals(" digest" , challenges[0 ].scheme)
438+ assertEquals(" value" , challenges[0 ].parameters[" realm" ])
439+ assertTrue(
440+ challenges.none { it.scheme == " extra" },
441+ " the stray trailing token must not be parsed as a phantom challenge scheme" ,
442+ )
443+ }
444+
445+ @Test
446+ fun `stray token between a valid param and a comma-separated next challenge is dropped` () {
447+ // `Digest realm=value extra, Basic realm="x"` — the stray `extra` after the
448+ // first challenge's param is skipped up to the next top-level comma, and the
449+ // following Basic challenge is still picked up cleanly.
450+ val challenges = AuthChallengeParser .parse(""" Digest realm=value extra, Basic realm="x"""" )
451+ assertEquals(2 , challenges.size)
452+ assertEquals(" digest" , challenges[0 ].scheme)
453+ assertEquals(" value" , challenges[0 ].parameters[" realm" ])
454+ assertEquals(" basic" , challenges[1 ].scheme)
455+ assertEquals(" x" , challenges[1 ].parameters[" realm" ])
456+ }
457+
415458 @Test
416459 fun `quote with only a backslash inside before close quote` () {
417460 // `"\"` — opens, sees backslash, advances, sees `"` (the close), appends
0 commit comments