Browse Source

fix(items): match the filter of Get-ChildItem2 without the culture of the thread

With the culture of the thread tr-TR, the case-insensitive WildcardPattern that
compares each name with the filter treats I and i as different letters. The
enumeration had already matched the name without regard to the culture, so the
cmdlet dropped the items whose names differ from the filter in the case of an
I: -Filter 'index*' returned nothing for Index.txt, and -Filter '*.ini' did not
return SETTINGS.INI. Both editions. The independent review of the coverage
report found it; the scan of culture-sensitive calls had not listed
WildcardPattern.

The test sets the culture of the thread to tr-TR. On the previous build it
fails in the four configurations (3 of 134 cases of ItemCmdlets.Tests.ps1:
index*, *.ini, and *.INI; INDEX* is the control). The pattern is now
WildcardOptions.CultureInvariant.

The changelog entry of the identity comparison named the operator -eq, which
tr-TR never affected (probes on the previous build: -eq, -contains, and -in
return True for every case); it now names the method Equals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
ai/coverage-unknowns
Raimund Andree 1 day ago
parent
commit
0028ccd232
  1. 11
      CHANGELOG.md
  2. 4
      NTFSSecurity/ItemCmdlets/GetChildItem2.cs
  3. 30
      Tests/ItemCmdlets.Tests.ps1

11
CHANGELOG.md

@ -439,11 +439,16 @@ The format is based on
- Fix [Get-ChildItem2](Docs/Cmdlets/Get-ChildItem2.md) with `-Hidden`,
which omitted the first hidden item unless `-Force` was also supplied
- Fix the comparison of an identity with a name, such as
`$entry.Account -eq 'nt authority\system'`, in the Turkish culture
- Fix the comparison of an identity with a name by its `Equals` method, such
as `$entry.Account.Equals('nt authority\system')`, in the Turkish culture
(`tr-TR`): it changed the case of both names with the culture of the thread,
which turns `I` into a dotless `ı`, so an account whose name holds an
uppercase `I`, such as `NT AUTHORITY\SYSTEM` or `BUILTIN\Administrators`,
did not match the same name in another case
did not match the same name in another case. The operators `-eq`,
`-contains`, and `-in` were not affected
- Fix [Get-ChildItem2](Docs/Cmdlets/Get-ChildItem2.md) with `-Filter` in the
Turkish culture (`tr-TR`), which dropped the items whose names differ from
the filter in the case of an `I`, such as `Index.txt` for `-Filter 'index*'`
or `SETTINGS.INI` for `-Filter '*.ini'`
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

4
NTFSSecurity/ItemCmdlets/GetChildItem2.cs

@ -150,8 +150,10 @@ namespace NTFSSecurity
// for itself. Before 5.0.0, [1] was read as a character class, so a file with brackets in its name was not
// returned for its name. The enumeration returns every item for *.* as Windows does, so the comparison does
// too; with the dot as an ordinary character, it would drop the items without a dot, most folders.
// CultureInvariant: the enumeration ignores case without the culture of the thread, and with tr-TR the
// comparison would otherwise treat I and i as different letters and drop items that the enumeration matched.
var pattern = filter == "*.*" ? "*" : filter;
wildcard = new WildcardPattern(pattern.Replace("`", "``").Replace("[", "`[").Replace("]", "`]"), WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
wildcard = new WildcardPattern(pattern.Replace("`", "``").Replace("[", "`[").Replace("]", "`]"), WildcardOptions.Compiled | WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant);
modeMethodInfo = typeof(FileSystemCodeMembers).GetMethod("Mode");

30
Tests/ItemCmdlets.Tests.ps1

@ -159,6 +159,36 @@ Describe 'Get-ChildItem2' {
($result.Name | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',')
}
# With the culture of the thread tr-TR, a case-insensitive comparison that is not culture invariant treats I and i as
# different letters: the cmdlet dropped the items whose names differ in the case of an I from the pattern, which
# the enumeration had matched without regard to the culture. INDEX* is the control: both sides turn I into the
# dotless letter.
It 'Should match -Filter <Filter> ignoring case in Turkish, where I and i are different letters' -ForEach @(
@{ Filter = 'index*'; Expected = 'Index.txt' }
@{ Filter = 'INDEX*'; Expected = 'Index.txt' }
@{ Filter = '*.ini'; Expected = 'desktop.ini,SETTINGS.INI' }
@{ Filter = '*.INI'; Expected = 'desktop.ini,SETTINGS.INI' }
) {
$folder = New-TestSandboxItem -Sandbox $sandbox -Name 'FilterTurkish' -Directory
foreach ($name in 'Index.txt', 'desktop.ini', 'SETTINGS.INI', 'Other.txt') {
$file = Join-Path -Path $folder -ChildPath $name
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
Set-Content -LiteralPath $file -Value $name
}
$original = [System.Threading.Thread]::CurrentThread.CurrentCulture
try {
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]::GetCultureInfo('tr-TR')
'I'.ToLower() | Should -Be ([string][char]0x131) -Because 'the culture of the thread has to be Turkish for the test to mean anything'
$result = @(Get-ChildItem2 -Path $folder -Filter $Filter -ErrorAction Stop)
}
finally {
[System.Threading.Thread]::CurrentThread.CurrentCulture = $original
}
($result.Name | Sort-Object) -join ',' | Should -BeExactly $Expected
}
# Only * and ? are wildcards in -Filter. A bracket stands for itself, so a file with brackets in its name is found
# by its name, as Get-ChildItem finds it, and the file that the brackets would select as a character class is not.
# Before 5.0.0, the cmdlet read [1] as a character class and returned nothing.

Loading…
Cancel
Save