url: make percentEncode stricter, expose and unit test it

This commit is contained in:
Yorick 2023-02-07 16:44:37 +01:00
parent 92611e6e4c
commit 0844856c84
No known key found for this signature in database
GPG key ID: A36E70F9DC014A15
3 changed files with 45 additions and 6 deletions

View file

@ -302,4 +302,37 @@ namespace nix {
ASSERT_EQ(d, s); ASSERT_EQ(d, s);
} }
/* ----------------------------------------------------------------------------
* percentEncode
* --------------------------------------------------------------------------*/
TEST(percentEncode, encodesUrlEncodedString) {
std::string s = percentEncode("==@==");
std::string d = "%3D%3D%40%3D%3D";
ASSERT_EQ(d, s);
}
TEST(percentEncode, keepArgument) {
std::string a = percentEncode("abd / def");
std::string b = percentEncode("abd / def", "/");
ASSERT_EQ(a, "abd%20%2F%20def");
ASSERT_EQ(b, "abd%20/%20def");
}
TEST(percentEncode, inverseOfDecode) {
std::string original = "%3D%3D%40%3D%3D";
std::string once = percentEncode(original);
std::string back = percentDecode(once);
ASSERT_EQ(back, original);
}
TEST(percentEncode, trailingPercent) {
std::string s = percentEncode("==@==%");
std::string d = "%3D%3D%40%3D%3D%25";
ASSERT_EQ(d, s);
}
} }

View file

@ -88,17 +88,22 @@ std::map<std::string, std::string> decodeQuery(const std::string & query)
return result; return result;
} }
std::string percentEncode(std::string_view s) const static std::string allowedInQuery = ":@/?";
const static std::string allowedInPath = ":@/";
std::string percentEncode(std::string_view s, std::string_view keep)
{ {
std::string res; std::string res;
for (auto & c : s) for (auto & c : s)
// unreserved + keep
if ((c >= 'a' && c <= 'z') if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z') || (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9') || (c >= '0' && c <= '9')
|| strchr("-._~!$&'()*+,;=:@", c)) || strchr("-._~", c)
|| keep.find(c) != std::string::npos)
res += c; res += c;
else else
res += fmt("%%%02x", (unsigned int) c); res += fmt("%%%02X", (unsigned int) c);
return res; return res;
} }
@ -109,9 +114,9 @@ std::string encodeQuery(const std::map<std::string, std::string> & ss)
for (auto & [name, value] : ss) { for (auto & [name, value] : ss) {
if (!first) res += '&'; if (!first) res += '&';
first = false; first = false;
res += percentEncode(name); res += percentEncode(name, allowedInQuery);
res += '='; res += '=';
res += percentEncode(value); res += percentEncode(value, allowedInQuery);
} }
return res; return res;
} }
@ -122,7 +127,7 @@ std::string ParsedURL::to_string() const
scheme scheme
+ ":" + ":"
+ (authority ? "//" + *authority : "") + (authority ? "//" + *authority : "")
+ path + percentEncode(path, allowedInPath)
+ (query.empty() ? "" : "?" + encodeQuery(query)) + (query.empty() ? "" : "?" + encodeQuery(query))
+ (fragment.empty() ? "" : "#" + percentEncode(fragment)); + (fragment.empty() ? "" : "#" + percentEncode(fragment));
} }

View file

@ -22,6 +22,7 @@ struct ParsedURL
MakeError(BadURL, Error); MakeError(BadURL, Error);
std::string percentDecode(std::string_view in); std::string percentDecode(std::string_view in);
std::string percentEncode(std::string_view s, std::string_view keep="");
std::map<std::string, std::string> decodeQuery(const std::string & query); std::map<std::string, std::string> decodeQuery(const std::string & query);