diff --git a/include/upa/url.h b/include/upa/url.h index 0dd5147..dc930fe 100644 --- a/include/upa/url.h +++ b/include/upa/url.h @@ -3090,6 +3090,10 @@ inline url url_from_file_path(StrT&& str, file_path_format format = file_path_fo } } + // Check for null characters + if (util::contains_null(pointer, last)) + throw url_error(validation_errc::null_character, "Path contains null character"); + // make URL detail::append_utf8_percent_encoded(pointer, last, *no_encode_set, str_url); return url(str_url); @@ -3164,6 +3168,11 @@ inline std::string path_from_file_url(const url& file_url, file_path_format form } } } + + // Check for null characters + if (util::contains_null(path.begin(), path.end())) + throw url_error(validation_errc::null_character, "Path contains null character"); + return path; } diff --git a/include/upa/url_result.h b/include/upa/url_result.h index a8adb98..43b5edf 100644 --- a/include/upa/url_result.h +++ b/include/upa/url_result.h @@ -84,6 +84,7 @@ enum class validation_errc { file_url_unsupported_host, ///< UNC path cannot have "." hostname file_url_invalid_unc, ///< Invalid UNC path in file URL file_url_not_windows_path, ///< Not a Windows path in file URL + null_character, ///< Path contains null character }; /// @brief Check validation error code indicates success diff --git a/include/upa/util.h b/include/upa/util.h index b9b1ea3..970e9aa 100644 --- a/include/upa/util.h +++ b/include/upa/util.h @@ -125,6 +125,11 @@ inline void append_ascii_lowercase(std::string& dest, const CharT* first, const // Finders +template +inline bool contains_null(InputIt first, InputIt last) { + return std::find(first, last, '\0') != last; +} + template UPA_CONSTEXPR_17 bool has_xn_label(const CharT* first, const CharT* last) { if (last - first >= 4) { diff --git a/test/test-url.cpp b/test/test-url.cpp index e027093..65aa40f 100644 --- a/test/test-url.cpp +++ b/test/test-url.cpp @@ -605,6 +605,8 @@ TEST_CASE("url_from_file_path") { // non absolute path CHECK_THROWS_AS(upa::url_from_file_path("path"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\h\\p", upa::file_path_format::posix), upa::url_error); + // null character + CHECK_THROWS_AS(upa::url_from_file_path(std::string{ "/p\0", 3 }, upa::file_path_format::posix), upa::url_error); } SUBCASE("Windows path") { // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file @@ -644,6 +646,8 @@ TEST_CASE("url_from_file_path") { // unsupported pathes CHECK_THROWS_AS(upa::url_from_file_path("\\\\?\\Volume{b75e2c83-0000-0000-0000-602f00000000}\\Test\\Foo.txt"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\.\\Volume{b75e2c83-0000-0000-0000-602f00000000}\\Test\\Foo.txt"), upa::url_error); + // null character + CHECK_THROWS_AS(upa::url_from_file_path(std::string{ "/C:/p\0", 6 }, upa::file_path_format::posix), upa::url_error); } } @@ -660,6 +664,8 @@ TEST_CASE("path_from_file_url") { CHECK(path_from_file_url("file:///path", upa::file_path_format::posix) == "/path"); // POSIX path cannot have host CHECK_THROWS_AS(path_from_file_url("file://host/path", upa::file_path_format::posix), upa::url_error); + // null character + CHECK_THROWS_AS(path_from_file_url("file:///p%00", upa::file_path_format::posix), upa::url_error); } SUBCASE("Windows path") { CHECK(path_from_file_url("file:///C:", upa::file_path_format::windows) == "C:\\"); @@ -685,6 +691,8 @@ TEST_CASE("path_from_file_url") { CHECK_THROWS_AS(path_from_file_url("file://///host/", upa::file_path_format::windows), upa::url_error); // Unsupported "." hostname CHECK_THROWS_AS(path_from_file_url("file://./name", upa::file_path_format::windows), upa::url_error); + // null character + CHECK_THROWS_AS(path_from_file_url("file:///C:/p%00", upa::file_path_format::posix), upa::url_error); } SUBCASE("Native path") { #ifdef _WIN32