From 419cfb3f6cfea50c4a5bed656f29a6a01dc242eb Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Sat, 10 Oct 2026 19:45:06 +0000 Subject: [PATCH] fix(identity): compare an identity with a name without the culture of the thread IdentityReference2.Equals(string) lowercased both names with ToLower(), which uses the culture of the thread. In tr-TR, I becomes U+0131, so an account whose name holds an uppercase I (NT AUTHORITY\SYSTEM, BUILTIN\Administrators) no longer matched the same name in another case, for example $entry.Account -eq 'nt authority\system'. Compare with StringComparison.OrdinalIgnoreCase. The test sets the culture of the thread to tr-TR and compares the system account with its name in three cases. Before the fix it fails in all four configurations (elevated and basic user, Windows PowerShell 5.1 and PowerShell 7); after it, all rows pass. The unit suite under tr-TR did not find it, because no test compares a name that holds an uppercase I. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 6 ++++++ Security2/IdentityReference2.cs | 3 ++- Tests/ObjectApis.Tests.ps1 | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e851e..07b28ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -439,5 +439,11 @@ 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 + (`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 [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Security2/IdentityReference2.cs b/Security2/IdentityReference2.cs index 9f01a96..2a72c3b 100644 --- a/Security2/IdentityReference2.cs +++ b/Security2/IdentityReference2.cs @@ -195,7 +195,8 @@ namespace Security2 if (this.ntAccount != null) { - if (this.ntAccount.Value.ToLower() == value.ToLower()) + // Not ToLower: with the culture of the thread, tr-TR lowercases I to U+0131 and misses names such as NT AUTHORITY\SYSTEM. + if (string.Equals(this.ntAccount.Value, value, StringComparison.OrdinalIgnoreCase)) { return true; } diff --git a/Tests/ObjectApis.Tests.ps1 b/Tests/ObjectApis.Tests.ps1 index d608a17..575e34c 100644 --- a/Tests/ObjectApis.Tests.ps1 +++ b/Tests/ObjectApis.Tests.ps1 @@ -159,6 +159,25 @@ Describe 'Identity comparisons and conversions' { $identity.Equals($other) | Should -Be $Expected } + # tr-TR lowercases I to U+0131, so a comparison that lowercases both names with the current culture misses an account name that holds an + # uppercase I (NT AUTHORITY, BUILTIN) when the other name has another case. The name must match in every culture. + It 'Should compare a name with the account ignoring case in Turkish, where I and i are different letters' { + $system = [Security2.IdentityReference2] 'S-1-5-18' + $system.AccountName | Should -MatchExactly 'I' -Because 'the test needs an account name with an uppercase I' + $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' + $system.Equals($system.AccountName) | Should -BeTrue + $system.Equals($system.AccountName.ToLowerInvariant()) | Should -BeTrue + $system.Equals($system.AccountName.ToUpperInvariant()) | Should -BeTrue + $system.Equals('NTFSSecurity-not-an-account') | Should -BeFalse + } + finally { + [System.Threading.Thread]::CurrentThread.CurrentCulture = $original + } + } + It 'Should compare null operands and distinct instances through both operators' { $same = [Security2.IdentityReference2] 'S-1-1-0' $different = [Security2.IdentityReference2] 'S-1-5-32-546'