From 2deec1761b903fc88eaff577f16917af9cc85db2 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Wed, 21 Oct 2026 14:05:51 +0000 Subject: [PATCH] fix(items): reject a -Filter of more than 32,767 characters An independent review found that a very long -Filter pattern (for example, a run of 120,000 question marks, or 45,000 asterisks) crashes the whole host process with an uncatchable StackOverflowException: the matcher recurses roughly once per character of the pattern, and a long enough pattern exhausts the stack. A pattern this long serves no purpose that a shorter one could not also serve, and the review confirmed that patterns of 32,767 characters or fewer, of any shape (literal, "?" repeated, "*" repeated, "*?" repeated, "*." repeated), do not come close to exhausting the stack. Added `[ValidateLength(0, 32767)]` next to the existing `[ValidateNotNull]` on -Filter, so that a pattern longer than 32,767 characters is rejected at parameter binding, before it ever reaches the matcher, with the same `ParameterArgumentValidationError` and `'Filter'` in the message as the existing null rejection; shorter values, including an empty one, are unaffected. Tests (Tests\ItemCmdlets.Tests.ps1, "Get-ChildItem2" > "Recursion, type filters, and depth"): a -Filter of 32,768 characters now gives ParameterArgumentValidationError, and one of exactly 32,767 characters ("?" repeated, in a folder with one file) returns the right result without crashing. The crash itself was confirmed in a disposable child process (not through the shared Pester harness, to avoid taking that process down too) with the reviewer's probe (C:\Temp\ntfs-rc9\review-probes-r3\p9-longpattern.ps1 -N 120000 -Kind qm): the process terminated with a StackOverflowException before this fix, and after it, the same probe at the exact boundaries (32,767 and 32,768 characters, every pattern kind the probe offers) neither crashes nor is accepted past the limit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- NTFSSecurity/ItemCmdlets/GetChildItem2.cs | 1 + Tests/ItemCmdlets.Tests.ps1 | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index 39d8b21..fd66987 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -44,6 +44,7 @@ namespace NTFSSecurity [Parameter(Position = 2)] [ValidateNotNull] + [ValidateLength(0, 32767)] public string Filter { get { return filter; } diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 9a15360..5c26da8 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -426,6 +426,28 @@ Describe 'Get-ChildItem2' { Should -Throw -ErrorId 'ParameterArgumentValidationError,NTFSSecurity.GetChildItem2' -ExpectedMessage "*'Filter'*" } + It 'Should reject a -Filter of more than 32,767 characters' { + $tooLong = '?' * 32768 + { Get-ChildItem2 -Path $tree -Filter $tooLong -ErrorAction Stop } | + Should -Throw -ErrorId 'ParameterArgumentValidationError,NTFSSecurity.GetChildItem2' -ExpectedMessage "*'Filter'*" + } + + # A pattern this long is not useful, but the cmdlet must not crash the host: before this test was added, a + # pattern of this length (or a long run of asterisks) recursed deep enough in the matcher to raise an + # uncatchable StackOverflowException and terminate the process. + It 'Should accept a -Filter of exactly 32,767 characters and return the right result' { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'FilterMaxLength' -Directory + $file = Join-Path -Path $folder -ChildPath 'a' + Assert-TestSandboxPath -Sandbox $sandbox -Path $file + Set-Content -LiteralPath $file -Value 'a' + $longest = '?' * 32767 + + $result = @(Get-ChildItem2 -Path $folder -Filter $longest -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0].Name | Should -Be 'a' + } + 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)