Merge pull request #7776 from yorickvP/fix-path-escapes-7707

Properly escape local paths into URLs in fetchTree
This commit is contained in:
Théophane Hufschmitt 2023-02-27 21:10:25 +01:00 committed by GitHub
commit eae89aca1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 7 deletions

View file

@ -4,6 +4,7 @@
#include "fetchers.hh" #include "fetchers.hh"
#include "filetransfer.hh" #include "filetransfer.hh"
#include "registry.hh" #include "registry.hh"
#include "url.hh"
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
@ -68,7 +69,16 @@ void emitTreeAttrs(
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file") std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
{ {
state.checkURI(uri); state.checkURI(uri);
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri; if (uri.find("://") == std::string::npos) {
const auto p = ParsedURL {
.scheme = defaultScheme,
.authority = "",
.path = uri
};
return p.to_string();
} else {
return uri;
}
} }
std::string fixURIForGit(std::string uri, EvalState & state) std::string fixURIForGit(std::string uri, EvalState & state)

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);

View file

@ -237,3 +237,17 @@ rm -rf $repo/.git
# should succeed for a repo without commits # should succeed for a repo without commits
git init $repo git init $repo
path10=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath") path10=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath")
# should succeed for a path with a space
# regression test for #7707
repo="$TEST_ROOT/a b"
git init "$repo"
git -C "$repo" config user.email "foobar@example.com"
git -C "$repo" config user.name "Foobar"
echo utrecht > "$repo/hello"
touch "$repo/.gitignore"
git -C "$repo" add hello .gitignore
git -C "$repo" commit -m 'Bla1'
cd "$repo"
path11=$(nix eval --impure --raw --expr "(builtins.fetchGit ./.).outPath")