LibURL: Advance past the '@' delimiter after batch-parsing userinfo

After batch-processing the userinfo, the authority state advanced the
parse pointer onto the '@' and relied on the end-of-loop increment to
step past it. That increment uses the byte length of the authority's
first code point, not the delimiter's. When the first code point was
multi-byte, the pointer overshot into the middle of a code point and
the next iteration sliced the string at a non-char boundary causing the
process to panic. We now step past the delimiter explicitly so we don't
need to rely on the end of loop increment to do so.
This commit is contained in:
Tim Ledbetter 2026-06-05 14:12:31 +01:00 committed by Tim Ledbetter
parent d13d29ee38
commit 3771bd5d4a
2 changed files with 23 additions and 2 deletions

View file

@ -671,8 +671,10 @@ pub(crate) fn basic_parse_into(
username_builder.push_str(&encoded_authority);
}
// NB: Since we have batch processed the username/password, we need to move the pointer past those code points.
pointer += authority_length;
// NB: Since we have batch processed the username/password, we need to move the pointer past those
// code points and the U+0040 (@) delimiter.
pointer += authority_length + '@'.len_utf8();
continue;
}
// 2. Otherwise, if one of the following is true:
// * c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)

View file

@ -575,6 +575,25 @@ TEST_CASE(username_and_password)
}
}
TEST_CASE(non_ascii_userinfo)
{
{
auto url = URL::Parser::basic_parse("http://é@é"sv);
EXPECT(url.has_value());
EXPECT_EQ(url->username(), "%C3%A9"sv);
EXPECT(url->password().is_empty());
EXPECT_EQ(url->serialized_host(), "xn--9ca"sv);
}
{
auto url = URL::Parser::basic_parse("http://é@example.com"sv);
EXPECT(url.has_value());
EXPECT_EQ(url->username(), "%C3%A9"sv);
EXPECT(url->password().is_empty());
EXPECT_EQ(url->serialized_host(), "example.com"sv);
}
}
TEST_CASE(ascii_only_url)
{
{