Various file and path validation examples taken from the ASP.NET System.Web namespace source code.
private static int _maxPathLength = 259;
private static char[] _invalidPhysicalPathChars = { '/', '?', '*', '<', '>', '|', '"' };
private static char[] _invalidFileNameChars = new char[] { '/', '\\', '?', '*', ':' };
private static readonly char[] _invalidPathChars = Path.GetInvalidPathChars();
//
// VSWhidbey 609102 - Medium trust apps may hit this method, and if the physical path exists,
// Path.GetFullPath will seek PathDiscovery permissions and throw an exception.
//
// Source: System.Web.Util.FileUtil
[FileIOPermissionAttribute(SecurityAction.Assert, AllFiles=FileIOPermissionAccess.PathDiscovery)]
static internal bool IsSuspiciousPhysicalPath(string physicalPath, out bool pathTooLong) {
bool isSuspicious;
// DevDiv 340712: GetConfigPathData generates n^2 exceptions where n is number of incorrectly placed '/'
// Explicitly prevent frequent exception cases since this method is called a few times per url segment
if ((physicalPath != null) &&
(physicalPath.Length > _maxPathLength ||
physicalPath.IndexOfAny(_invalidPathChars) != -1 ||
// Contains ':' at any position other than 2nd char
(physicalPath.Length > 0 && physicalPath[0] == ':') ||
(physicalPath.Length > 2 && physicalPath.IndexOf(':', 2) > 0))) {
// see comment below
pathTooLong = true;
return true;
}
try {
isSuspicious = !String.IsNullOrEmpty(physicalPath) &&
String.Compare(physicalPath, Path.GetFullPath(physicalPath),
StringComparison.OrdinalIgnoreCase) != 0;
pathTooLong = false;
}
catch (PathTooLongException) {
isSuspicious = true;
pathTooLong = true;
}
catch (NotSupportedException) {
// see comment below -- we do the same for ':'
isSuspicious = true;
pathTooLong = true;
}
catch (ArgumentException) {
// DevDiv Bugs 152256: Illegal characters {",|} in path prevent configuration system from working.
// We need to catch this exception and conservatively assume that the path is suspicious in
// such a case.
// We also set pathTooLong to true because at this point we do not know if the path is too long
// or not. If we assume that pathTooLong is false, it means that our path length enforcement
// is bypassed by using URLs with illegal characters. We do not want that. Moreover, returning
// pathTooLong = true causes the current logic to peel of URL fragments, which can also find a
// path without illegal characters to retrieve the config.
isSuspicious = true;
pathTooLong = true;
}
return isSuspicious;
}
internal static bool IsValidPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return true;
if (originalResult.IndexOfAny(_invalidPhysicalPathChars) >= 0)
return true;
// Final check: do the full check to ensure it is valid
try {
bool pathTooLong;
if (FileUtil.IsSuspiciousPhysicalPath(originalResult, out pathTooLong) || pathTooLong)
return _DefaultPhysicalPathOnMapPathFailure;
} catch {
return _DefaultPhysicalPathOnMapPathFailure;
}
return false;
}
/*
* Attempt to delete a file, but don't throw if it can't be done
*/
internal static void DeleteFileNoException(string path) {
Debug.Assert(File.Exists(path), path);
try {
File.Delete(path);
}
catch { } // Ignore all exceptions
}
internal static void DeleteFileIfExistsNoException(string path) {
if (File.Exists(path))
DeleteFileNoException(path);
}
/*
* Return true if the directory exists and is not empty.
*/
internal static bool IsNonEmptyDirectory(string dir) {
// Does it exist
if (!Directory.Exists(dir))
return false;
// It exists, but maybe it's empty
try {
string[] entries = Directory.GetFileSystemEntries(dir);
return entries.Length > 0;
}
catch {
// If it throws, assume it's non-empty
return true;
}
}
/*
* Return true if string is a valid simple file name (with no path or wild cards)
*/
internal static bool IsValidFileName(string fileName)
{
// Check for the special names "." and ".."
if (fileName == "." || fileName == "..")
return false;
// Check for invalid characters
if (fileName.IndexOfAny(_invalidFileNameChars) >= 0)
return false;
return true;
}
/*
* Replace all invalid chars in a filename by underscores.
*/
internal static string MakeValidFileName(string fileName)
{
// If it's already valid, nothing to do
if (IsValidFileName(fileName))
return fileName;
// Replace all the invalid chars by '_'
for (int i = 0; i < _invalidFileNameChars.Length; ++i) {
fileName = fileName.Replace(_invalidFileNameChars[i], '_');
}
// Shoud always be valid now
Debug.Assert(IsValidFileName(fileName));
return fileName;
}
// Remove the final backslash from a directory path, unless it's something like c:\
internal static String RemoveTrailingDirectoryBackSlash(String path)
{
if (path == null)
return null;
int length = path.Length;
if (length > 3 && path[length - 1] == '\\')
path = path.Substring(0, length - 1);
return path;
}