From 413a2bc7f9df4716d27ea92abb2b660d2aee4eb8 Mon Sep 17 00:00:00 2001
From: Oz <rik20@live.it>
Date: Thu, 23 Jan 2025 22:15:58 +0100
Subject: [PATCH] Fix path sanitization edge cases

---
 src/internal/fileextractcallback.cpp |   4 +-
 src/internal/fsutil.cpp              |  26 ++-
 src/internal/fsutil.hpp              |   2 +
 tests/src/test_fsutil.cpp            | 313 ++++++++++++++++++++++++++-
 4 files changed, 332 insertions(+), 13 deletions(-)

diff --git a/src/internal/fileextractcallback.cpp b/src/internal/fileextractcallback.cpp
index eeb861eb..a5d11dc1 100644
--- a/src/internal/fileextractcallback.cpp
+++ b/src/internal/fileextractcallback.cpp
@@ -87,11 +87,11 @@ auto FileExtractCallback::getOutStream( uint32_t index, ISequentialOutStream** o
         filePath = tstring_to_path( mRenameCallback( index, filePathString ) );
     }
 
-    if ( filePath.empty() ) {
+    if ( filePath.empty() || ( isItemFolder( index ) && filePath == L"/" ) ) {
         return S_OK;
     }
 #if defined( _WIN32 ) && defined( BIT7Z_PATH_SANITIZATION )
-    mFilePathOnDisk = mDirectoryPath / filesystem::fsutil::sanitize_path( filePath );
+    mFilePathOnDisk = filesystem::fsutil::sanitized_extraction_path( mDirectoryPath, filePath );
 #else
     mFilePathOnDisk = mDirectoryPath / filePath;
 #endif
diff --git a/src/internal/fsutil.cpp b/src/internal/fsutil.cpp
index b56e81bc..8e8ab2bc 100644
--- a/src/internal/fsutil.cpp
+++ b/src/internal/fsutil.cpp
@@ -359,7 +359,8 @@ void fsutil::increase_opened_files_limit() {
 }
 
 #if defined( _WIN32 ) && defined( BIT7Z_PATH_SANITIZATION )
-inline auto is_windows_reserved_name( const std::wstring& component ) -> bool {
+namespace {
+auto is_windows_reserved_name( const std::wstring& component ) -> bool {
     // Reserved file names that can't be used on Windows: CON, PRN, AUX, and NUL.
     if ( component == L"CON" || component == L"PRN" || component == L"AUX" || component == L"NUL" ) {
         return true;
@@ -373,7 +374,15 @@ inline auto is_windows_reserved_name( const std::wstring& component ) -> bool {
            std::iswdigit( component.back() ) != 0;
 }
 
-inline auto sanitize_path_component( std::wstring component ) -> std::wstring {
+auto sanitize_path_component( std::wstring component ) -> std::wstring {
+    const auto firstNonSlash = component.find_first_not_of( L"/\\" );
+    if ( firstNonSlash == std::wstring::npos ) {
+        return L"";
+    }
+    if ( firstNonSlash != 0 ) {
+        component.erase( 0, firstNonSlash );
+    }
+
     // If the component is a reserved name on Windows, we prepend it with a '_' character.
     if ( is_windows_reserved_name( component ) ) {
         component.insert( 0, 1, L'_' );
@@ -388,15 +397,24 @@ inline auto sanitize_path_component( std::wstring component ) -> std::wstring {
     }, L'_' );
     return component;
 }
+} // namespace
 
 auto fsutil::sanitize_path( const fs::path& path ) -> fs::path {
-    fs::path sanitizedPath = path.root_path().make_preferred();
-    for( const auto& pathComponent : path.relative_path() ) {
+    if ( path == L"/" ) {
+        return L"_";
+    }
+
+    fs::path sanitizedPath;
+    for( const auto& pathComponent : path ) {
         // cppcheck-suppress useStlAlgorithm
         sanitizedPath /= sanitize_path_component( pathComponent.wstring() );
     }
     return sanitizedPath;
 }
+
+auto fsutil::sanitized_extraction_path( const fs::path& outDir, const fs::path& itemPath ) -> fs::path {
+    return outDir / sanitize_path( itemPath );
+}
 #endif
 
 } // namespace filesystem
diff --git a/src/internal/fsutil.hpp b/src/internal/fsutil.hpp
index 9a21b058..628c4d3f 100644
--- a/src/internal/fsutil.hpp
+++ b/src/internal/fsutil.hpp
@@ -74,6 +74,8 @@ void increase_opened_files_limit();
  * @return the sanitized path, where illegal characters are replaced with the '_' character.
  */
 auto sanitize_path( const fs::path& path ) -> fs::path;
+
+auto sanitized_extraction_path( const fs::path& outDir, const fs::path& itemPath ) -> fs::path;
 #endif
 
 }  // namespace fsutil
diff --git a/tests/src/test_fsutil.cpp b/tests/src/test_fsutil.cpp
index 8e4b38e8..49145789 100644
--- a/tests/src/test_fsutil.cpp
+++ b/tests/src/test_fsutil.cpp
@@ -281,6 +281,8 @@ TEST_CASE( "fsutil: Format long Windows paths", "[fsutil][format_long_path]" ) {
 
 #if defined( _WIN32 ) && defined( BIT7Z_PATH_SANITIZATION )
 TEST_CASE( "fsutil: Sanitizing Windows paths", "[fsutil][sanitize_path]" ) {
+    REQUIRE( sanitize_path( L"" ) == L"" );
+
     REQUIRE( sanitize_path( L"hello world.txt" ) == L"hello world.txt" );
     REQUIRE( sanitize_path( L"hello?world<" ) == L"hello_world_" );
     REQUIRE( sanitize_path( L":hello world|" ) == L"_hello world_" );
@@ -310,11 +312,65 @@ TEST_CASE( "fsutil: Sanitizing Windows paths", "[fsutil][sanitize_path]" ) {
     REQUIRE( sanitize_path( L"AUXI" ) == L"AUXI" );
     REQUIRE( sanitize_path( L"NULL" ) == L"NULL" );
 
-    REQUIRE( sanitize_path( L"C:/abc/NUL/def" ) == L"C:\\abc\\_NUL\\def" );
-    REQUIRE( sanitize_path( L"C:\\abc\\NUL\\def" ) == L"C:\\abc\\_NUL\\def" );
-
-    REQUIRE( sanitize_path( L"C:/Test/COM0/hello?world<.txt" ) == L"C:\\Test\\_COM0\\hello_world_.txt" );
-    REQUIRE( sanitize_path( L"C:\\Test\\COM0\\hello?world<.txt" ) == L"C:\\Test\\_COM0\\hello_world_.txt" );
+    REQUIRE( sanitize_path( L"/" ) == L"_" );
+    REQUIRE( sanitize_path( L"//" ) == L"_" );
+    REQUIRE( sanitize_path( L"//////////////" ) == L"_" );
+    REQUIRE( sanitize_path( L"/\\" ) == L"_" );
+    REQUIRE( sanitize_path( L"////////\\\\\\\\" ) == L"_" );
+    REQUIRE( sanitize_path( L"\\" ) == L"_" );
+    REQUIRE( sanitize_path( L"\\\\" ) == L"_" );
+    REQUIRE( sanitize_path( L"\\\\\\\\\\\\" ) == L"_" );
+    REQUIRE( sanitize_path( L"\\/" ) == L"_" );
+    REQUIRE( sanitize_path( L"\\\\\\\\////////" ) == L"_" );
+    REQUIRE( sanitize_path( L"/abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"//abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"//////////abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"/\\abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"////////\\\\\\\\abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"\\abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"\\\\abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"\\\\\\\\\\\\\\\\\\abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"\\/abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"\\\\\\\\////////abc" ) == L"abc" );
+    REQUIRE( sanitize_path( L"/abc/" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc//" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc/\\" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc\\" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc\\\\" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc\\/" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"\\abc/" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"\\abc\\" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"\\\\abc\\" ) == L"abc\\" );
+    REQUIRE( sanitize_path( L"/abc/NUL/def" ) == L"abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"\\abc\\NUL\\def" ) == L"abc\\_NUL\\def" );
+
+    REQUIRE( sanitize_path( L"C:" ) == L"C_" );
+    REQUIRE( sanitize_path( L"C:/" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C://" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C:/\\" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C:\\" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C:\\\\" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C:\\/" ) == L"C_\\" );
+    REQUIRE( sanitize_path( L"C:/abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C://abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C:/\\abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C:\\abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C:\\\\abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C:\\/abc" ) == L"C_\\abc" );
+    REQUIRE( sanitize_path( L"C:/abc/" ) == L"C_\\abc\\" );
+    REQUIRE( sanitize_path( L"C:/abc\\" ) == L"C_\\abc\\" );
+    REQUIRE( sanitize_path( L"C:\\abc/" ) == L"C_\\abc\\" );
+    REQUIRE( sanitize_path( L"C:\\abc\\" ) == L"C_\\abc\\" );
+    REQUIRE( sanitize_path( L"C:/abc/NUL/def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:/abc//NUL/def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:/abc/\\NUL/def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:\\abc\\NUL\\def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:\\abc\\\\NUL\\def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:\\abc\\/NUL\\def" ) == L"C_\\abc\\_NUL\\def" );
+    REQUIRE( sanitize_path( L"C:\\abc\\NUL\\def" ) == L"C_\\abc\\_NUL\\def" );
+
+    REQUIRE( sanitize_path( L"C:/Test/COM0/hello?world<.txt" ) == L"C_\\Test\\_COM0\\hello_world_.txt" );
+    REQUIRE( sanitize_path( L"C:\\Test\\COM0\\hello?world<.txt" ) == L"C_\\Test\\_COM0\\hello_world_.txt" );
     REQUIRE( sanitize_path( L"Test/COM0/hello?world<.txt" ) == L"Test\\_COM0\\hello_world_.txt" );
     REQUIRE( sanitize_path( L"Test\\COM0\\hello?world<.txt" ) == L"Test\\_COM0\\hello_world_.txt" );
     REQUIRE( sanitize_path( L"../COM0/hello?world<.txt" ) == L"..\\_COM0\\hello_world_.txt" );
@@ -324,8 +380,8 @@ TEST_CASE( "fsutil: Sanitizing Windows paths", "[fsutil][sanitize_path]" ) {
     REQUIRE( sanitize_path( L"COM0/hello?world<.txt" ) == L"_COM0\\hello_world_.txt" );
     REQUIRE( sanitize_path( L"COM0\\hello?world<.txt" ) == L"_COM0\\hello_world_.txt" );
 
-    REQUIRE( sanitize_path( L"C:/Test/:hello world|/LPT5" ) == L"C:\\Test\\_hello world_\\_LPT5" );
-    REQUIRE( sanitize_path( L"C:\\Test\\:hello world|\\LPT5" ) == L"C:\\Test\\_hello world_\\_LPT5" );
+    REQUIRE( sanitize_path( L"C:/Test/:hello world|/LPT5" ) == L"C_\\Test\\_hello world_\\_LPT5" );
+    REQUIRE( sanitize_path( L"C:\\Test\\:hello world|\\LPT5" ) == L"C_\\Test\\_hello world_\\_LPT5" );
     REQUIRE( sanitize_path( L"Test/:hello world|/LPT5" ) == L"Test\\_hello world_\\_LPT5" );
     REQUIRE( sanitize_path( L"Test\\:hello world|\\LPT5" ) == L"Test\\_hello world_\\_LPT5" );
     REQUIRE( sanitize_path( L"../:hello world|/LPT5" ) == L"..\\_hello world_\\_LPT5" );
@@ -335,4 +391,247 @@ TEST_CASE( "fsutil: Sanitizing Windows paths", "[fsutil][sanitize_path]" ) {
     REQUIRE( sanitize_path( L":hello world|/LPT5" ) == L"_hello world_\\_LPT5" );
     REQUIRE( sanitize_path( L":hello world|\\LPT5" ) == L"_hello world_\\_LPT5" );
 }
+
+TEST_CASE( "fsutil: Sanitizing and concatenate Windows paths", "[fsutil][sanitized_extraction_path]" ) {
+    REQUIRE( sanitized_extraction_path( L"", L"abc" ) == L"abc" );
+    REQUIRE( sanitized_extraction_path( L"", L"/" ) == L"_" );
+    REQUIRE( sanitized_extraction_path( L"", L"/abc" ) == L"abc" );
+    REQUIRE( sanitized_extraction_path( L"", L"\\\\abc" ) == L"abc" );
+    REQUIRE( sanitized_extraction_path( L"", L"C:/abc" ) == L"C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"", L"\\" ) == L"_" );
+    REQUIRE( sanitized_extraction_path( L"", L"\\abc" ) == L"abc" );
+    REQUIRE( sanitized_extraction_path( L"", L"C:\\abc" ) == L"C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"/", L"abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/", L"/" ) == L"\\_" );
+    REQUIRE( sanitized_extraction_path( L"/", L"/abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/", L"C:/abc" ) == L"\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/", L"\\" ) == L"\\_" );
+    REQUIRE( sanitized_extraction_path( L"/", L"\\abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/", L"C:\\abc" ) == L"\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"/def", L"abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"/" ) == L"\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"/abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"C:/abc" ) == L"\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"\\" ) == L"\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"\\abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"/def", L"C:\\abc" ) == L"\\def\\C_\\abc" );
+
+    // GHC library is a bit buggy in these edge cases.
+#ifndef GHC_FILESYSTEM_VERSION
+    REQUIRE( sanitized_extraction_path( L"//", L"abc" ) == L"//abc" );
+    REQUIRE( sanitized_extraction_path( L"//", L"/" ) == L"//_" );
+    REQUIRE( sanitized_extraction_path( L"//", L"/abc" ) == L"//abc" );
+    REQUIRE( sanitized_extraction_path( L"//", L"C:/abc" ) == L"//C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"//", L"\\" ) == L"//_" );
+    REQUIRE( sanitized_extraction_path( L"//", L"\\abc" ) == L"//abc" );
+    REQUIRE( sanitized_extraction_path( L"//", L"C:\\abc" ) == L"//C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"//server", L"abc" ) == L"//server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"/" ) == L"//server\\_" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"/abc" ) == L"//server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"C:/abc" ) == L"//server\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"\\" ) == L"//server\\_" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"\\abc" ) == L"//server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"//server", L"C:\\abc" ) == L"//server\\C_\\abc" );
+#endif
+
+    REQUIRE( sanitized_extraction_path( L"\\", L"abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"/" ) == L"\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"/abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"C:/abc" ) == L"\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"\\" ) == L"\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"\\abc" ) == L"\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\", L"C:\\abc" ) == L"\\C_\\abc" );
+
+
+    REQUIRE( sanitized_extraction_path( L"\\def", L"abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"/" ) == L"\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"/abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"C:/abc" ) == L"\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"\\" ) == L"\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"\\abc" ) == L"\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\def", L"C:\\abc" ) == L"\\def\\C_\\abc" );
+
+    // GHC library is a bit buggy in these edge cases.
+#ifndef GHC_FILESYSTEM_VERSION
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"abc" ) == L"\\\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"/" ) == L"\\\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"/abc" ) == L"\\\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"C:/abc" ) == L"\\\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"\\" ) == L"\\\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"\\abc" ) == L"\\\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\", L"C:\\abc" ) == L"\\\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"abc" ) == L"\\\\server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"/" ) == L"\\\\server\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"/abc" ) == L"\\\\server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"C:/abc" ) == L"\\\\server\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"\\" ) == L"\\\\server\\_" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"\\abc" ) == L"\\\\server\\abc" );
+    REQUIRE( sanitized_extraction_path( L"\\\\server", L"C:\\abc" ) == L"\\\\server\\C_\\abc" );
+#endif
+
+    REQUIRE( sanitized_extraction_path( L"out", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"out/", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out/\\", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"out\\", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\/", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"/" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"/abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"C:/abc" ) == L"out\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"\\" ) == L"out\\_" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"\\abc" ) == L"out\\abc" );
+    REQUIRE( sanitized_extraction_path( L"out\\\\", L"C:\\abc" ) == L"out\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:", L"abc" ) == L"C:abc" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"/" ) == L"C:_" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"/abc" ) == L"C:abc" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"C:/abc" ) == L"C:C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"\\" ) == L"C:_" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"\\abc" ) == L"C:abc" );
+    REQUIRE( sanitized_extraction_path( L"C:", L"C:\\abc" ) == L"C:C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:/", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C://", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C://", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/\\", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\\\", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"/" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"/abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"C:/abc" ) == L"C:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"\\" ) == L"C:\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"\\abc" ) == L"C:\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\/", L"C:\\abc" ) == L"C:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def/", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:/def\\", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def/", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"/" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"/abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"C:/abc" ) == L"C:\\def\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"\\" ) == L"C:\\def\\_" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"\\abc" ) == L"C:\\def\\abc" );
+    REQUIRE( sanitized_extraction_path( L"C:\\def\\", L"C:\\abc" ) == L"C:\\def\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"D:", L"C:/abc" ) == L"D:C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"D:", L"C:\\abc" ) == L"D:C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"D:/", L"C:/abc" ) == L"D:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"D:/", L"C:\\abc" ) == L"D:\\C_\\abc" );
+
+    REQUIRE( sanitized_extraction_path( L"D:\\", L"C:/abc" ) == L"D:\\C_\\abc" );
+    REQUIRE( sanitized_extraction_path( L"D:\\", L"C:\\abc" ) == L"D:\\C_\\abc" );
+}
 #endif
\ No newline at end of file