From c203e5ec809a3f87a5683c83a4b0ae426e16a4ba Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Tue, 3 May 2022 19:44:27 +0000 Subject: [PATCH] Bug 1746052, don't allow Windows reserved filenames when sanitizing filenames. Move MangleTextToValidFileName to nsLocalFileWin and rename it to CheckForReservedFileName, r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D138737 --- netwerk/mime/nsIMIMEService.idl | 2 + .../exthandler/nsExternalHelperAppService.cpp | 5 +++ widget/windows/nsDataObj.cpp | 37 ++----------------- xpcom/io/nsLocalFileWin.cpp | 20 ++++++++++ xpcom/io/nsLocalFileWin.h | 4 ++ 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/netwerk/mime/nsIMIMEService.idl b/netwerk/mime/nsIMIMEService.idl index 2d8d61fba0ad..c5d1efbd7575 100644 --- a/netwerk/mime/nsIMIMEService.idl +++ b/netwerk/mime/nsIMIMEService.idl @@ -168,6 +168,8 @@ interface nsIMIMEService : nsISupports { * character, either ' ' or an ideographic space 0x3000 if present. * - Unless VALIDATE_DONT_TRUNCATE is specified, the filename is truncated * to a maximum length, preserving the extension if possible. + * - Some filenames are invalid on certain platforms. These are replaced if + * possible. * * If the VALIDATE_NO_DEFAULT_FILENAME flag is not specified, and after the * rules above are applied, the resulting filename is empty, a default diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 15d212fed66d..808965442e2a 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -105,6 +105,7 @@ #ifdef XP_WIN # include "nsWindowsHelpers.h" +# include "nsLocalFile.h" #endif #include "mozilla/Components.h" @@ -3450,6 +3451,10 @@ nsExternalHelperAppService::ValidateFileNameForSaving( } } +#ifdef XP_WIN + nsLocalFile::CheckForReservedFileName(fileName); +#endif + // If no filename is present, use a default filename. if (!(aFlags & VALIDATE_NO_DEFAULT_FILENAME) && (fileName.Length() == 0 || fileName.RFind(".") == 0)) { diff --git a/widget/windows/nsDataObj.cpp b/widget/windows/nsDataObj.cpp index 9e507f62087e..553291ed2d9c 100644 --- a/widget/windows/nsDataObj.cpp +++ b/widget/windows/nsDataObj.cpp @@ -44,6 +44,7 @@ #include "imgIEncoder.h" #include "imgITools.h" #include "WinUtils.h" +#include "nsLocalFile.h" #include "mozilla/LazyIdleThread.h" #include @@ -1087,36 +1088,6 @@ nsDataObj ::GetFileContents(FORMATETC& aFE, STGMEDIUM& aSTG) { } // GetFileContents -// -// Given a unicode string, we ensure that it contains only characters which are -// valid within the file system. Remove all forbidden characters from the name, -// and completely disallow any title that starts with a forbidden name and -// extension (e.g. "nul" is invalid, but "nul." and "nul.txt" are also invalid -// and will cause problems). -// -// It would seem that this is more functionality suited to being in nsIFile. -// -static void MangleTextToValidFilename(nsString& aText) { - static const char* forbiddenNames[] = { - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", - "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", - "LPT8", "LPT9", "CON", "PRN", "AUX", "NUL", "CLOCK$"}; - - aText.StripChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS); - aText.CompressWhitespace(true, true); - uint32_t nameLen; - for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) { - nameLen = (uint32_t)strlen(forbiddenNames[n]); - if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) { - // invalid name is either the entire string, or a prefix with a period - if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) { - aText.Truncate(); - break; - } - } - } -} - // // Given a unicode string, convert it down to a valid local charset filename // with the supplied extension. This ensures that we do not cut MBCS characters @@ -1129,7 +1100,7 @@ static bool CreateFilenameFromTextA(nsString& aText, const char* aExtension, // ensure that the supplied name doesn't have invalid characters. If // a valid mangled filename couldn't be created then it will leave the // text empty. - MangleTextToValidFilename(aText); + nsLocalFile::CheckForReservedFileName(aText); if (aText.IsEmpty()) return false; // repeatably call WideCharToMultiByte as long as the title doesn't fit in the @@ -1161,7 +1132,7 @@ static bool CreateFilenameFromTextW(nsString& aText, const wchar_t* aExtension, // ensure that the supplied name doesn't have invalid characters. If // a valid mangled filename couldn't be created then it will leave the // text empty. - MangleTextToValidFilename(aText); + nsLocalFile::CheckForReservedFileName(aText); if (aText.IsEmpty()) return false; const int extensionLen = wcslen(aExtension); @@ -2183,7 +2154,7 @@ HRESULT nsDataObj::GetDownloadDetails(nsIURI** aSourceURI, if (srcFileName.IsEmpty()) return E_FAIL; // make the name safe for the filesystem - MangleTextToValidFilename(srcFileName); + nsLocalFile::CheckForReservedFileName(srcFileName); sourceURI.swap(*aSourceURI); aFilename = srcFileName; diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index 6da50ac8477b..3fcbab4bac2d 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -191,6 +191,26 @@ nsresult nsLocalFile::RevealFile(const nsString& aResolvedPath) { return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; } +// static +void nsLocalFile::CheckForReservedFileName(nsString& aFileName) { + static const char* forbiddenNames[] = { + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", + "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", + "LPT8", "LPT9", "CON", "PRN", "AUX", "NUL", "CLOCK$"}; + + uint32_t nameLen; + for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) { + nameLen = (uint32_t)strlen(forbiddenNames[n]); + if (aFileName.EqualsIgnoreCase(forbiddenNames[n], nameLen)) { + // invalid name is either the entire string, or a prefix with a period + if (aFileName.Length() == nameLen || + aFileName.CharAt(nameLen) == char16_t('.')) { + aFileName.Truncate(); + } + } + } +} + class nsDriveEnumerator : public nsSimpleEnumerator, public nsIDirectoryEnumerator { public: diff --git a/xpcom/io/nsLocalFileWin.h b/xpcom/io/nsLocalFileWin.h index 6b42964141af..f306c99bc7ce 100644 --- a/xpcom/io/nsLocalFileWin.h +++ b/xpcom/io/nsLocalFileWin.h @@ -49,6 +49,10 @@ class nsLocalFile final : public nsILocalFileWin { // Called off the main thread to open the window revealing the file static nsresult RevealFile(const nsString& aResolvedPath); + // Checks if the filename is one of the windows reserved filenames + // (com1, com2, etc...) and truncates the string if so. + static void CheckForReservedFileName(nsString& aFileName); + private: // CopyMove and CopySingleFile constants for |options| parameter: enum CopyFileOption {