fix(settings-ui): reject empty normalized search queries #50531

Open
niels9001 opened 1:21pm on September 11, 2026 wants to merge 201 Ξ” into microsoft/powertoys main from
niels9001-unicode-search-safety

Pull Request Overview

  • Opened on September 11, 2026
  • Status Open
  • Commit count 1 with first commit September 11, 2026

Total Delta

201 Total Diff Delta

Open Days

Open 6 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
0.0 days Authoring 1 commit before pull request opened for review
 
0.0 days Awaiting first review
 
5.0 days Revising work with 0 commits in response to 1 review that left 2 comments

Total time for pull request still awaiting merge: 5.0 business days

Author avatar

fix(settings-ui): reject empty normalized search queries

Summary of the Pull Request

Prevent Settings search from passing queries that normalize to empty or whitespace-only text into fuzzy matching, and make the shared matcher return its existing no-match result for whitespace-only queries.

PR Checklist


  • [x] Communication: Scoped to the requested atomic Settings search-safety audit follow-up; submitted as a draft.

  • [x] Tests: Added focused regression coverage; all 46 selected tests pass.

  • [x] Localization: No new user-facing strings; normalization and existing localized search behavior are unchanged.

Issue closure, development/user documentation, and new-binary checklist items are N/A. No new projects, dependencies, binaries, or persisted settings changes.

Detailed Description of the Pull Request / Additional comments

Trigger: A query consisting of U+00A8 (spacing diaeresis) passes the initial whitespace check in src\settings-ui\Settings.UI\Services\SearchIndexService.cs. FormKD normalization decomposes it into U+0020 plus a combining mark; removing nonspacing marks leaves a single space. Previously, src\common\Common.Search\FuzzSearch\StringMatcher.cs checked only for null/empty input before trimming, then indexed the first element of an empty token array. With a populated search index, the resulting IndexOutOfRangeException can propagate through Parallel.ForEach to the live search handler, which catches cancellation only. This is a source/unit-test finding, not a claim of a reproduced live application crash.

The Settings service now returns no results after normalization when no searchable text remains. The shared matcher rejects null/empty/whitespace queries before its per-start-index matching loop, preventing empty trimmed/tokenized input from reaching the indexed access. Both use existing no-match behavior rather than adding an exception catch.

Shared-caller risk: Common.Search is also used by Shortcut Guide and the Command Palette PowerToys extension. Their existing blank-query handling remains unchanged, as do the matching algorithm, separators, scoring, highlighting, and case options for nonempty queries. No public API signature changes. NormalizeString becomes internal solely for direct coverage through the existing Settings test friend assembly.

Tests are in the existing src\settings-ui\Settings.UI.UnitTests\Search\StringMatcherTests.cs and src\settings-ui\Settings.UI.UnitTests\Search\SearchIndexServiceTests.cs; there is no standalone Common.Search test project on current main. The service fixture supplies a nonempty in-memory index, restores it afterward, and does not load Settings resources or change user settings.

This PR deliberately excludes expander-result filtering and prebuilt search-index generation/embedding changes. It does not auto-close the different search-crash reports #41817 or #47118.

Validation Steps Performed


  • Ran the repository first-build prerequisite tools\build\build-essentials.cmd -Platform x64 -Configuration Release after the fresh worktree reported missing NuGet assets; restore and essentials completed successfully.

  • Built Settings.UI.UnitTests in Release x64 using tools\build\build.ps1 with the worktree SolutionDir; exit code 0.

  • Ran the existing Microsoft.Testing.Platform runner with --filter "FullyQualifiedName~Settings.UI.UnitTests.Search" --minimum-expected-tests 46 --report-trx: 46 passed, 0 failed, 0 skipped.

  • Covered null/empty/ASCII whitespace, Unicode spaces, U+00A8 compatibility normalization, combining-only queries, both Search overloads with a nonempty index, both case options, ordinary/padded/multiword queries, unchanged scores/highlights, exact-match ranking, and accented/fullwidth normalization.

  • Did not launch or modify the installed/running PowerToys instance or user settings.

Comments Threads Pending Resolution

ljharb reviewed on September 12, 2026
ljharb left a comment

### 🟒 Approval recommended

The focused guards address the failure path without changing nonempty-query behavior, with comprehensive regression coverage.

Pull request overview

Prevents normalized blank Settings queries from reaching fuzzy matching and hardens the shared matcher against whitespace-only input.

**Changes:**
- Rejects empty normalized queries before searching.
- Adds shared matcher validation for whitespace-only queries.
- Adds regression tests for normalization, scoring, highlighting, and case handling.

File summaries

| File | Description |
| ---- | ----------- |
| `SearchIndexService.cs` | Rejects blank normalized queries and exposes normalization internally for tests. |
| `StringMatcher.cs` | Returns no match for whitespace-only queries. |
| `StringMatcherTests.cs` | Covers matcher edge cases and unchanged matching behavior. |
| `SearchIndexServiceTests.cs` | Covers normalization and search regressions. |

Review details

- **Files reviewed:** 4/4 changed files
- **Comments generated:** 0
- **Review effort level:** Balanced

---

πŸ’‘ Add a `code-review` agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Resolved Comment Threads

No resolved comments have been left on this PR.