mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.3 KiB
7.3 KiB
status: current
last-verified: 2026-10-20
owner: software-engineer
source: Handoff 5 (the coverage unknowns) and the rc8 fix: probes, red and green runs, and the independent reviews
Culture-sensitive comparison (
A swallowed initialization error (
Debugging insights
Recurring defect classes and how to find them. Evidence is in
Tests/Coverage/Quality-Gate-Unknowns-2026-10-10.md; a fix needs a test that
fails without it (Decision 16).
Culture-sensitive comparison (tr-TR)
- With
tr-TR,Iandiare different letters, so a comparison that takes the culture of the thread drops or misses items:ToLowerorToUpper,Compare,StartsWith,EndsWith, orIndexOf(string)without aStringComparison, a case-insensitiveRegex, aWildcardPatternwithoutCultureInvariant(string.Equals(a, b)and==are ordinal and safe). Handoff 5 found two defects of this class:IdentityReference2.Equals(object)called with a name, which lowercased both sides (nowOrdinalIgnoreCase), andGet-ChildItem2 -Filter(index*did not listIndex.txt). - A scan of the C# is not enough: AlphaFS 2.2 builds a culture-sensitive regex
from a search pattern.
Get-ChildItem2therefore enumerates underInvariantCultureScope(around the creation of the enumerator and eachMoveNext, never across aWriteObject) and its ownWildcardPatternhasCultureInvariant. Check any new library call that takes a user pattern. - PowerShell
-eq,-contains, and-indo not take the thread culture; only the .NET method call failed. Probe a claim before it enters the changelog or a report (the first wording named-eqand was wrong). - A test sets the culture of the thread, restores it in
finally, names the rows that differ (index*,*.INI), keeps a control (INDEX*), reads the culture in the next pipeline stage for each item and once after the call (a cmdlet that does not restore it, or writes an item inside the scope, fails both: two mutations proved it), and is proved red on the previous build and green on the new one in all four configurations (Tests/ItemCmdlets.Tests.ps1,Tests/ObjectApis.Tests.ps1).
A test oracle that is wrong on a domain member
Get-Acl -Auditreads the SACL with the other sections and reports an item without audit entries as protected (Windows Server 2019, 2022, and 2025 domain members, Windows 11); the module andGet-NTFSInheritanceread the SACL alone. Tests read the flag with .NETGetAccessControl(Audit)(Get-TestAuditAcl). A suite that is green on the host proves nothing about an oracle: run a new test on a domain member before calling it done.
A piped property and the parameter set
- An object with a property
AppliesTo, piped intoAdd-orRemove-NTFSAccessor-NTFSAuditwith a path, bound the defaultPathComplexset and lost the property. Fixed in93ed2c7: the four cmdlets declare no default set, so the property selectsPathSimple, an object withAppliesToandInheritanceFlagsfails withAmbiguousParameterSet, and the tests pipe an object of each shape (red on the previous build, green on the new one). A probe that tells the sets uses an entry of the default scope: removingThisFolderOnlyfrom it leaves theInheritOnlypart. - PowerShell restores only the parameters that the previous piped object
bound. A field that the cmdlet writes itself stays for the next object: the
scope that
ProcessRecordconverted fromAppliesTointo the field behind-InheritanceFlagswent to the next object without a scope.93ed2c7shipped that, the third review found it (rc7 had it for piped-SecurityDescriptorobjects), and9783d2akeeps the flags of a record in locals. A pipeline test pipes two or more objects of different shape in one call (Objects with different scopes in one pipeline, both ParameterSets files); one object per call cannot see it. - Probe the route that the documentation shows.
Import-Csvof a file gives an empty string for a blank cell,ConvertFrom-Csvof strings gives$nullfor a trailing blank cell, andImport-Csvgives$nullfor a column that a row lacks. The binder treats them differently:$nullfails to bind, and a value that it cannot convert to the enum (an empty string, a typo) is ignored, so the object is handled as one without the property. The first statement and the first tests used the convenient route, and the reviewer found the false claim; the corrected claim was then too narrow once more (a value that converts binds, whatever its form; a flag next to an unusable value wins). Write the claim from a probe of each form, not from the one that you tried. - A sentence that says what a test proves is a claim too: a title that says "and change nothing" needs an assertion that can see the change (the audit cmdlets write the SACL, not the DACL), and "cannot go red" needs a run.
- The cause was
DefaultParameterSetName = "PathComplex": when a piped object satisfies the mandatory parameters of the default set, PowerShell settles on it and never tries a parameter that belongs to another set. A stand-alone model with the four sets and no default bindsPathSimplefor an object withAppliesTo,PathComplexfor one without, and an error for one withAppliesToandInheritanceFlags(probes\p-parameterset-binding*.ps1in the Handoff 5 evidence; the model bindsPathComplexfor a piped descriptor where the real cmdlets bindSDSimple); the real cmdlets agreed for paths (probes\p-proto-appliesto.ps1). The default dated from 4.2.6, whenAppliesTowas optional;4b1abc9made it mandatory in the Simple sets, so the default no longer decided a named call. Build a model of the parameter sets before you change an attribute, and test the real cmdlet afterwards.
A swallowed initialization error (catch { })
Win32.GetEffectiveAccessran the start of the authorization manager inside an outercatch { }. A refusal there gave no exception and no rights, so the cmdlet wrote an entry withSynchronizeonly (.NET adds it to every allow rule) and no error: a false "no access". The security review of the matrix found it (LOW); rc8 fixed it (25fe775) with onetry/catchthat records every exception inauthzException.- A trigger without a test seam:
Get-NTFSEffectiveAccess -ServerName ('a' * 40000).AuthzInitializeRemoteResourceManagerfails with error 14 for a name of 32,718 characters or more, and with 1722 (RPC server unavailable, which keeps the documented fallback and its warning) for a shorter one. Server 2019, 2022, 2025, and Windows 11 26H1 agree (a probe in the rc8 record). Bisect the length of an input before you add a seam. - The test accepts both outcomes (an error, or the result of this computer) and
fails for no access without an error: the red run said
Expected FullControl, but got Synchronize. A system-dependent boundary must not fail the test where a system answers like for a short name. - The review found a second flaw in the same output: the warning "the result is
based on this computer" came before an error that left no result. A warning
that qualifies a result belongs with the result (
!FromRemote && !OperationFailed); the tests assert its absence next to the error. - A word in the CHANGELOG is a claim: "an overlong name" suggested 256 characters, where 1722 still falls back. The entry names the probed length.