From ae3078f99ae854c4a4faa3fae61eefb6b6a9569f Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Fri, 9 Oct 2026 16:08:10 +0000 Subject: [PATCH] fix: reject a null -Filter of Get-ChildItem2 and document the dot of the pattern The escape of brackets in the pattern read the filter before the pattern could reject it, so a null filter ended in a NullReferenceException. The parameter now rejects null with a validation error that names it; an empty filter still matches no item. The cmdlet compares each name that the enumeration returns with the pattern again. The two disagree for *.*: the enumeration returns every item, as Get-ChildItem does, and the comparison drops the names without a dot, files and folders alike. A test pins this and reaches the branch that drops an item; the help says that a dot is an ordinary character. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 5 ++++- Docs/Cmdlets/Get-ChildItem2.md | 2 +- NTFSSecurity/ItemCmdlets/GetChildItem2.cs | 1 + NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml | 4 ++-- Tests/ItemCmdlets.Tests.ps1 | 21 ++++++++++++++++++++ 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b494b2f..c0c2148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,7 +132,10 @@ The format is based on removed every item. They now stop and write no error - Fix `Get-ChildItem2 -Filter`, which read a bracket as the start of a character class, so that it did not return a file with brackets in its name, - such as `Report[1].txt`, for that name; only `*` and `?` are wildcards + such as `Report[1].txt`, for that name; only `*` and `?` are wildcards. A dot + is an ordinary character, so `*.*` returns only the names that contain a dot + (unlike `Get-ChildItem`), and a null `-Filter` is rejected as a parameter + error - Fix `Get-Help`, which showed only the syntax: ship the help file `en-US\NTFSSecurity.dll-Help.xml` generated from the cmdlet documentation, including the links that `Get-Help -Online` opens, instead of the outdated diff --git a/Docs/Cmdlets/Get-ChildItem2.md b/Docs/Cmdlets/Get-ChildItem2.md index c39bbaf..c26ec60 100644 --- a/Docs/Cmdlets/Get-ChildItem2.md +++ b/Docs/Cmdlets/Get-ChildItem2.md @@ -134,7 +134,7 @@ Accept wildcard characters: False ### -Filter -Specifies a name pattern that an item must match to be returned. The pattern supports the `*` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is `*`, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. +Specifies a name pattern that an item must match to be returned. The pattern supports the `*` and `?` wildcard characters, and the match ignores case; any other character stands for itself. A bracket is an ordinary character, so `Report[1].txt` returns the file of that name, and so is a dot, so `*.*` returns only the items whose names contain a dot, not every item as it does for `Get-ChildItem`. The default value is `*`, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. ```yaml Type: String diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index 58b126d..e77f67a 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -45,6 +45,7 @@ namespace NTFSSecurity } [Parameter(Position = 2)] + [ValidateNotNull] public string Filter { get { return filter; } diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index 276123f..7e5b100 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -3520,7 +3520,7 @@ PS C:\> Disable-Privileges Filter - Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. + Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character stands for itself. A bracket is an ordinary character, so `Report[1].txt` returns the file of that name, and so is a dot, so ` . ` returns only the items whose names contain a dot, not every item as it does for `Get-ChildItem`. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. String @@ -3724,7 +3724,7 @@ PS C:\> Disable-Privileges Filter - Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. + Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character stands for itself. A bracket is an ordinary character, so `Report[1].txt` returns the file of that name, and so is a dot, so ` . ` returns only the items whose names contain a dot, not every item as it does for `Get-ChildItem`. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. String diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 9e22b86..cb7e4bf 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -176,6 +176,27 @@ Describe 'Get-ChildItem2' { $result[0].Name | Should -BeExactly 'Report[1].txt' } + # The dot is an ordinary character of the pattern, so *.* selects the names that contain a dot. The enumeration + # alone would return every item for it, as Get-ChildItem and cmd.exe do; the cmdlet then compares each name with + # the pattern and drops the items whose names have no dot, files and folders alike. + It 'Should return only the items with a dot in their names for -Filter *.*' { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'FilterDot' -Directory + $paths = @('Page.htm', 'NoExtension', 'NoExtensionFolder') | ForEach-Object -Process { Join-Path -Path $folder -ChildPath $_ } + Assert-TestSandboxPath -Sandbox $sandbox -Path $paths + Set-Content -LiteralPath $paths[0] -Value 'Page' + Set-Content -LiteralPath $paths[1] -Value 'NoExtension' + New-Item -ItemType Directory -Path $paths[2] | Out-Null + + $result = @(Get-ChildItem2 -Path $folder -Filter '*.*' -ErrorAction Stop) + + ($result.Name -join ',') | Should -BeExactly 'Page.htm' + } + + It 'Should reject a null -Filter' { + { Get-ChildItem2 -Path $tree -Filter $null -ErrorAction Stop } | + Should -Throw -ErrorId 'ParameterArgumentValidationError,NTFSSecurity.GetChildItem2' -ExpectedMessage "*'Filter'*" + } + It 'Should stop a recursive pipeline without recording an enumeration error' { $result = @(Get-ChildItem2 -Path $tree -Recurse -ErrorVariable childErrors -ErrorAction SilentlyContinue | Select-Object -First 1)