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

Code before:
        private bool _lastEntryFound;
        private Queue<(IntPtr Handle, string Path, int RemainingDepth)>? _pending;
        private bool _isFirstGetData = true;

        /// <summary>
        /// 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)

Review comment (suggestion):
This line uses the field `_expression` instead of the parameter `expression`. At this point in the constructor, `_expression` has not been assigned yet (it's assigned on line 77), so it will be null. This should be `expression.AsSpan().ContainsAny(@"\""<>")` to check the parameter value before escaping it.
```suggestion
                else if (_options.MatchType == MatchType.Simple && expression.AsSpan().ContainsAny(@"\""<>"))
```

Code after:
        private bool _lastEntryFound;
        private Queue<(IntPtr Handle, string Path, int RemainingDepth)>? _pending;
        private bool _isFirstGetData = true;

        /// <summary>
        /// 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.