diff --git a/include/upa/url.h b/include/upa/url.h index 4bac797..bb4f31e 100644 --- a/include/upa/url.h +++ b/include/upa/url.h @@ -2967,8 +2967,9 @@ inline bool is_unc_path(const CharT* first, const CharT* last) ++path_components_count; - // Check the first UNC path component (hostname) - if (path_components_count == 1) { + switch (path_components_count) { + case 1: + // Check the first UNC path component (hostname) switch (pcend - start) { case 1: // Do not allow "?" and "." hostnames, because "\\?\" means Win32 file @@ -2982,6 +2983,23 @@ inline bool is_unc_path(const CharT* first, const CharT* last) return false; break; } + break; + case 2: + // Check the second UNC path component (share name). + // Do not allow "." and ".." as share names, because they have + // a special meaning and are removed by the URL parser. + switch (pcend - start) { + case 1: + if (start[0] == '.') + return false; + break; + case 2: + if (start[0] == '.' && start[1] == '.') + return false; + break; + } + break; + default:; } if (pcend == last) break; start = pcend + 1; // skip '\' diff --git a/test/test-url.cpp b/test/test-url.cpp index 230d19c..ba291fb 100644 --- a/test/test-url.cpp +++ b/test/test-url.cpp @@ -622,10 +622,11 @@ TEST_CASE("url_from_file_path") { CHECK(upa::url_from_file_path("\\\\h\\a/b").href() == "file://h/a/b"); CHECK(upa::url_from_file_path("\\\\a/b\\path").href() == "file://a/b/path"); CHECK(upa::url_from_file_path("//h/path", upa::file_path_format::windows).href() == "file://h/path"); - // UNC: two-character hostname - CHECK(upa::url_from_file_path("\\\\ab\\path").href() == "file://ab/path"); - // UNC: three-character hostname - CHECK(upa::url_from_file_path("\\\\abc\\path").href() == "file://abc/path"); + // UNC: two-character hostname and share name + CHECK(upa::url_from_file_path("\\\\ab\\xy").href() == "file://ab/xy"); + // UNC: three-character hostname and share name + CHECK(upa::url_from_file_path("\\\\abc\\xyz").href() == "file://abc/xyz"); + CHECK(upa::url_from_file_path("\\\\abc\\...").href() == "file://abc/..."); // Win32 file and device namespaces // https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats // https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation @@ -647,11 +648,15 @@ TEST_CASE("url_from_file_path") { CHECK_THROWS_AS(upa::url_from_file_path("\\\\h"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\h\\"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\h\\\\"), upa::url_error); + CHECK_THROWS_AS(upa::url_from_file_path("\\\\h\\."), upa::url_error); + CHECK_THROWS_AS(upa::url_from_file_path("\\\\h\\.."), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path(std::string{ '\\', '\\', 'h', '\\', 'a', '\0', 'b' }), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\C:\\path"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\C|\\path"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\?\\UNC\\?\\name"), upa::url_error); CHECK_THROWS_AS(upa::url_from_file_path("\\\\?\\UNC\\.\\name"), upa::url_error); + CHECK_THROWS_AS(upa::url_from_file_path("\\\\?\\UNC\\h\\."), upa::url_error); + CHECK_THROWS_AS(upa::url_from_file_path("\\\\?\\UNC\\h\\.."), upa::url_error); // invalid hostname CHECK_THROWS_AS(upa::url_from_file_path("\\\\a b\\path"), upa::url_error); // unsupported pathes