mirror of
https://github.com/ChrisTitusTech/winutil
synced 2026-06-04 22:27:28 +00:00
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
This commit is contained in:
@@ -3,78 +3,134 @@ 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
|
||||
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)]
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$SearchString = ""
|
||||
)
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# Search through apps in this category
|
||||
$wrapPanel.Children | ForEach-Object {
|
||||
$_.Visibility = [Windows.Visibility]::Visible
|
||||
# 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
# 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 {
|
||||
$appEntry = $sync.configs.applicationsHashtable.$($_.Tag)
|
||||
if ($appEntry.Content -like "*$SearchString*" -or $appEntry.Description -like "*$SearchString*") {
|
||||
# Show the App and mark that this category has a match
|
||||
# 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
|
||||
$categoryHasMatch = $true
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user