Repository: dotnet/runtime
File: src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs (C#)

Code before:
        /// Encapsulates a find operation.
        /// </summary>
        /// <param name="directory">The directory to search in.</param>
        /// <param name="isNormalized">Whether the directory path is already normalized or not.</param>
        /// <param name="options">Enumeration options to use.</param>
        /// <param name="expression">The search expression to potentially use for OS-level filtering (Windows only).</param>
        internal FileSystemEnumerator(string directory, bool isNormalized, EnumerationOptions? options, string? expression) :
            this(directory, isNormalized, options)
        {
            // We need to ensure that the expression is prepped to be used as an NtQueryDirectoryFile filter.
            // If the match type is simple, we need to escape the special DOS wildcard characters. If it is
            // DOS style, the characters have already been escaped in the FileSystemEnumerableFactory.

            if (expression is not null && !_options.RecurseSubdirectories)
            {
                if (expression is "." or "..")
                {
                    // Special directories are not valid expressions for OS-level filtering
                    expression = null;
                }
                else if (_options.MatchType == MatchType.Simple && _expression.AsSpan().ContainsAny(@"\""<>"))
                {
                    // Escape the escaping literal first
                    expression = expression.Replace("\\", "\\\\");

                    // Also need to escape the other special wild characters ('"', '<', and '>')
                    expression = expression.Replace("\"", "\\\"");
                    expression = expression.Replace(">", "\\>");
                    expression = expression.Replace("<", "\\<");
                }
            }

            _expression = expression;
        }

        private void Init()
        {
            // We'll only suppress the media insertion prompt on the topmost directory as that is the
            // most likely scenario and we don't want to take the perf hit for large enumerations.
            // (We weren't consistent with how we handled this historically.)
            using (default(DisableMediaInsertionPrompt))
            {
                // We need to initialize the directory handle up front to ensure
                // we immediately throw IO exceptions for missing directory/etc.
                _directoryHandle = CreateDirectoryHandle(_rootDirectory);
                if (_directoryHandle == IntPtr.Zero)
                    _lastEntryFound = true;
            }

            _currentPath = _rootDirectory;


Review comment (security):
The comment refers to these as "special wild characters", but they are not wildcards in the traditional sense. These are literal characters (quote, less-than, greater-than) that have special meaning in NT pattern matching (RtlIsNameInExpression) and need to be escaped when they should be matched literally. Consider clarifying the comment to say "special DOS pattern matching characters" or "characters with special meaning in NT pattern matching" for better accuracy.
```suggestion
                    // Also need to escape the other characters with special meaning in DOS/NT pattern matching ('"', '<', and '>')
```

Code after:
        /// Encapsulates a find operation.
        /// </summary>
        /// <param name="directory">The directory to search in.</param>
        /// <param name="isNormalized">Whether the directory path is already normalized or not.</param>
        /// <param name="options">Enumeration options to use.</param>
        /// <param name="expression">The search expression to potentially use for OS-level filtering (Windows only).</param>
        internal FileSystemEnumerator(string directory, bool isNormalized, EnumerationOptions? options, string? expression) :
            this(directory, isNormalized, options)
        {
            if (_options.RecurseSubdirectories)
            {
                // We can never filter when recursing, as we need to find all subdirectories to recurse into them.
                expression = null;
            }
            else if (expression is not null)
            {
                // We need to ensure that the expression is prepped to be used as an NtQueryDirectoryFile filter.
                // If the match type is simple, we need to escape the special DOS wildcard characters. If it is
                // DOS style, the characters have already been escaped in the FileSystemEnumerableFactory.

                if (expression.Length > 255 || expression is "*" or "." or "..")
                {
                    // Don't allow expressions longer than the maximum filename length. This comes back as
                    // STATUS_INVALID_PARAMETER from NtQueryDirectoryFile. We could catch that there, but
                    // don't want to mask other bad parameter issues.
                    //
                    // Also, an expression of "*" is pointless as a filter- it matches everything. "*.*"
                    // should have been converted to "*" already in the case of MatchType.Win32.
                    //
                    // Finally, "." and ".." are invalid filter names on their own, but we allow you to see
                    // these entries if you specifically ask via EnumerationOptions.ReturnSpecialDirectories.
                    expression = null;
                }
                else if (_options.MatchType == MatchType.Simple)
                {
                    expression = FileSystemName.EscapeExpression(expression);
                }
            }

            _expression = expression;
        }

        private void Init()
        {
            // We'll only suppress the media insertion prompt on the topmost directory as that is the
            // most likely scenario and we don't want to take the perf hit for large enumerations.
            // (We weren't consistent with how we handled this historically.)
            using (default(DisableMediaInsertionPrompt))
            {
                // We need to initialize the directory handle up front to ensure
                // we immediately throw IO exceptions for missing directory/etc.