commit 95115695179fe1ad1f0c3d09c8cf400d77970d2d
parent be5e0d5a705ef7314dadc4795d3ec5f0be443c5f
Author: Alexey Yerin <yyp@disroot.org>
Date: Wed, 22 Feb 2023 15:36:09 +0300
net::uri: disallow '@' after seeing userinfo
A boolean is used instead of len(userinfo)>0 to also catch a provided,
but empty userinfo part like this:
https://@example
Adding another '@' here should be still invalid.
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/net/uri/+test.ha b/net/uri/+test.ha
@@ -103,6 +103,10 @@ use net::ip;
// Something other than IPv6 address inside [ ... ]
assert(parse("https://[1.2.3.4]") is invalid);
assert(parse("https://[example]") is invalid);
+
+ // '@' in userinfo
+ assert(parse("https://a@b@example") is invalid);
+ assert(parse("https://@@example") is invalid);
};
@test fn percent_encoding() void = {
diff --git a/net/uri/parse.ha b/net/uri/parse.ha
@@ -137,6 +137,7 @@ fn parse_authority(
let host: (str | ip::addr6) = "";
let port = 0u16;
let userinfo = "";
+ let has_userinfo = false;
for (true) {
const r = match (strings::next(in)) {
@@ -173,14 +174,15 @@ fn parse_authority(
return invalid;
};
} else if (r == ':' || !is_userinfo(r) && !is_host(r)) {
- if (len(userinfo) > 0 && is_userinfo(r)) {
- return invalid;
- };
switch (r) {
case '@' =>
+ if (has_userinfo) {
+ return invalid;
+ };
// This was userinfo+host[+port]
userinfo = percent_decode(strio::string(&buf))?;
strio::reset(&buf);
+ has_userinfo = true;
case '/' =>
// This was just host
strings::prev(in);