Skip to main content

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;
}