From 413a2bc7f9df4716d27ea92abb2b660d2aee4eb8 Mon Sep 17 00:00:00 2001 From: Oz 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