Files
winutil/functions/private/Find-AppsByNameOrDescription.ps1
Nazrul Islam 743f9e3783 Refactor: Secure literal search and defensive scope handling (#4492)
* Refactor: Secure literal search and defensive scope handling

* Refactor: Secure literal search for Tweaks Tab

* Merge branch 'ChrisTitusTech:main' into fix-search-security
2026-05-19 12:56:58 -05:00

137 lines
5.7 KiB
PowerShell

function Find-AppsByNameOrDescription {
<#
.SYNOPSIS
Searches through the Apps on the Install Tab and hides all entries that do not match the string
.DESCRIPTION
Filters application entries by name or description using literal string matching.
Respects collapsed category state and handles null $sync gracefully.
.PARAMETER SearchString
The string to be searched for. Wildcards are treated as literal characters.
.NOTES
- Uses module-scope $sync (no parameter needed; inherits from caller's scope)
- Performs literal matching (no wildcard expansion)
- Safely handles missing hashtable keys and null UI elements
- Protected by try/catch to prevent UI thread crashes
#>
param(
[Parameter(Mandatory = $false)]
[string]$SearchString = ""
)
# Validate that $sync exists and has required structure
if ($null -eq $sync) {
Write-Warning "Find-AppsByNameOrDescription: Global `$sync not found. Aborting search."
return
}
if ($null -eq $sync.ItemsControl) {
Write-Warning "Find-AppsByNameOrDescription: `$sync.ItemsControl not initialized. Aborting search."
return
}
if ($null -eq $sync.configs -or $null -eq $sync.configs.applicationsHashtable) {
Write-Warning "Find-AppsByNameOrDescription: `$sync.configs.applicationsHashtable not initialized. Aborting search."
return
}
try {
# Reset the visibility if the search string is empty or the search is cleared
if ([string]::IsNullOrWhiteSpace($SearchString)) {
$sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container
$_.Visibility = [Windows.Visibility]::Visible
if ($_.Children.Count -ge 2) {
$categoryLabel = $_.Children[0]
$wrapPanel = $_.Children[1]
# Keep category label visible
$categoryLabel.Visibility = [Windows.Visibility]::Visible
# Respect the collapsed state of categories (indicated by + prefix)
if ($categoryLabel.Content -like "+*") {
$wrapPanel.Visibility = [Windows.Visibility]::Collapsed
}
else {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
}
# Show all apps within the category
$wrapPanel.Children | ForEach-Object {
$_.Visibility = [Windows.Visibility]::Visible
}
}
}
return
}
# Escape wildcard characters for literal matching
$escapedSearchString = [System.Management.Automation.WildcardPattern]::Escape($SearchString)
# Perform search
$sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container with Children[0] = label, Children[1] = WrapPanel
if ($_.Children.Count -ge 2) {
$categoryLabel = $_.Children[0]
$wrapPanel = $_.Children[1]
$categoryHasMatch = $false
# Keep category label visible
$categoryLabel.Visibility = [Windows.Visibility]::Visible
# Search through apps in this category
$wrapPanel.Children | ForEach-Object {
# Safely retrieve app entry from hashtable
$appTag = $_.Tag
$appEntry = $null
if (-not [string]::IsNullOrWhiteSpace($appTag) -and $sync.configs.applicationsHashtable.ContainsKey($appTag)) {
$appEntry = $sync.configs.applicationsHashtable[$appTag]
}
# Check if app matches search criteria
if ($null -ne $appEntry) {
$contentMatch = $appEntry.Content -like "*$escapedSearchString*"
$descriptionMatch = $appEntry.Description -like "*$escapedSearchString*"
if ($contentMatch -or $descriptionMatch) {
# Show the App and mark that this category has a match
$_.Visibility = [Windows.Visibility]::Visible
$categoryHasMatch = $true
}
else {
$_.Visibility = [Windows.Visibility]::Collapsed
}
}
else {
# Hide app if no entry found (data integrity issue)
$_.Visibility = [Windows.Visibility]::Collapsed
}
}
# If category has matches, show the WrapPanel and update the category label to expanded state
if ($categoryHasMatch) {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
$_.Visibility = [Windows.Visibility]::Visible
# Update category label to show expanded state (-)
if ($categoryLabel.Content -like "+*") {
$categoryLabel.Content = $categoryLabel.Content -replace "^\+ ", "- "
}
}
else {
# Hide the entire category container if no matches
$_.Visibility = [Windows.Visibility]::Collapsed
}
}
}
}
catch {
Write-Warning "Find-AppsByNameOrDescription: An error occurred during search: $_"
# Fail gracefully - do not crash the UI thread
return
}
}