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.
144 lines
14 KiB
144 lines
14 KiB
|
2 weeks ago
|
---
|
||
|
|
status: accepted
|
||
|
|
date: 2026-10-21
|
||
|
|
last-verified: 2026-10-21
|
||
|
|
owner: shared
|
||
|
|
source: maintainer answers of 2026-10-21 (fix scope B, items 3 to 7 change, review on); probes of 2026-10-21 on the published rc8
|
||
|
|
---
|
||
|
|
|
||
|
|
# Decision 27: The open items before 5.0.0, and the piped optional parameters
|
||
|
|
|
||
|
|
- Context: the tests-only round left no plain test gap (#129). On 2026-10-21
|
||
|
|
the maintainer chose to fix the silent default scope of a piped `AppliesTo`
|
||
|
|
before 5.0.0 (rc9) and to go through the other open items one by one. rc9
|
||
|
|
is the last prerelease: a change after its acceptance needs rc10, and after
|
||
|
|
5.0.0 the repository is archived (Decision 18). So this is the last moment
|
||
|
|
to change any of the items below, and unchanged means accepted.
|
||
|
|
- Status: accepted at the level of scope (see "The maintainer's choices"). The
|
||
|
|
designs of the items 3 to 7 are still to be proposed and chosen. Every
|
||
|
|
recommendation in the tables is the agent's; the last column of the table of
|
||
|
|
the other items stays empty, because the choices are listed below.
|
||
|
|
|
||
|
|
## New finding: the flaw is a family
|
||
|
|
|
||
|
|
PowerShell skips a property of a piped object whose value it cannot convert to
|
||
|
|
the type of an optional parameter, and the parameter keeps its default. A named
|
||
|
|
call fails loudly; the same value on a piped object does not. Probe
|
||
|
|
`p-unconvertible-optional-params.ps1`, the published rc8, both editions (the
|
||
|
|
only difference is how `ReadData` prints):
|
||
|
|
|
||
|
|
| Piped property | What rc8 does |
|
||
|
|
| --- | --- |
|
||
|
|
| `AccessType = 'Deny'` | A Deny entry, as intended |
|
||
|
|
| `AccessType = 'Denyy'` (typo), or a blank CSV cell | **An Allow entry**, no message |
|
||
|
|
| `AuditFlags = 'Failurre'` | Success and Failure are audited (the default), no message |
|
||
|
|
| `InheritanceFlags` or `PropagationFlags = 'Typo'` | Ignored; the default flags (`ContainerInherit`, `ObjectInherit`) |
|
||
|
|
| `AppliesTo = 'Typo'`, a blank cell, or `99` | Ignored; the broader default scope |
|
||
|
|
| `Remove-NTFSAccess` with `AccessType = 'Denyy'` | Looks for an Allow entry: the Deny entry stays, no message |
|
||
|
|
| `Get-FileHash2` with `Algorithm = 'SHA999'` | SHA256, no message |
|
||
|
|
| `-AccessType Denyy` as a named parameter | A binding error, as it should be |
|
||
|
|
|
||
|
|
The decision of 2026-10-21 covers `AppliesTo` only. A typo for `Deny` that
|
||
|
|
creates an Allow entry is at least as serious, and it has the same cause.
|
||
|
|
|
||
|
|
## Options for the fix
|
||
|
|
|
||
|
|
Prototypes (`p-proto-appliesto-shapes.ps1`, `p-proto-object3.ps1`), both
|
||
|
|
editions, same attribute shape as `Add-NTFSAccess`:
|
||
|
|
|
||
|
|
| Shape | Result |
|
||
|
|
| --- | --- |
|
||
|
|
| One parameter set with optional parameters | Does not help: the binder skips the unconvertible value there too |
|
||
|
|
| `AppliesTo` as `object` alone | Loud for a bad scope, but a CSV row with `AppliesTo` and `InheritanceFlags` is no longer `AmbiguousParameterSet`: the scope silently wins |
|
||
|
|
| `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, and `AccessType` or `AuditFlags` as `object`, converted in the cmdlet with `LanguagePrimitives.ConvertTo` | Loud for every bad value (a non-terminating error for that object, the next object goes on, nothing carries over); `AppliesTo` with flags is `AmbiguousParameterSet` for every mix of strings and enums; valid values unchanged |
|
||
|
|
|
||
|
|
| Option | What it is | Cost |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| A | `AppliesTo` only (the decision as asked) | Leaves the Deny-to-Allow flip |
|
||
|
|
| B | The family in the four cmdlets (recommended): `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType` (access), `AuditFlags` (audit) | See below |
|
||
|
|
| C | B and `Get-FileHash2 -Algorithm` | B plus one cmdlet of low severity |
|
||
|
|
| D | No code change: pages and CHANGELOG say that piped values are not checked | Weak for a permissions tool |
|
||
|
|
|
||
|
|
Consequences of B: the help shows `<Object>` for these parameters (the pages
|
||
|
|
list the values; an argument completer keeps tab completion); a named call
|
||
|
|
with a bad value is an error record of the cmdlet instead of a terminating
|
||
|
|
binding error; a blank cell for any of them is an error, not the default; a
|
||
|
|
`$null` `AccessType` stays the default (Allow); the pipeline conflict check is
|
||
|
|
now uniform. The work: tests first (the pins of the silent behavior change),
|
||
|
|
four cmdlets, four pages, the help file, the CHANGELOG, rc9, and the
|
||
|
|
acceptance (about 70 minutes with `Chain-Published.ps1`). An independent
|
||
|
|
review is recommended: public behavior and the permission scope.
|
||
|
|
|
||
|
|
## The other open items
|
||
|
|
|
||
|
|
| # | Item | What a user can notice | If changed | Recommended | Choice |
|
||
|
|
| --- | --- | --- | --- | --- | --- |
|
||
|
|
| 2 | The help says that a local standard user who asks about a domain account still gets "Access is denied" | Nothing changes in behavior | Docs only; rides with rc9 | Do it in rc9 | |
|
||
|
|
| 3 | Library-only items of the DLL that no cmdlet reaches: the `FileSecurity` conversions always throw; the `RemoveAll` filters ignore the account list (#113); the lazy path overloads; a `PrivilegeEnabler` that is not disposed stays enabled; the unused registry model and helper classes; `PipelineControl.IsEnd`; one unvisited catch in `Enable-Privileges` | Only .NET callers of the DLL | Code change and rc9 | Accept; list as known limits of the .NET API | |
|
||
|
|
| 4 | `Get-ChildItem2 -Filter` dot patterns differ from `Get-ChildItem` (`Report.*` misses `Report`; `Rep*.` returns nothing) | Documented, pinned by tests | New matching code and rc9 | Accept as documented | |
|
||
|
|
| 5 | The `-SecurityDescriptor` sets of the four add and remove cmdlets end with a terminating error when the change throws; the `-Path` sets write an error per item and go on (7 test rows stay unpinned) | A failing change to a descriptor in memory stops the cmdlet | Per-item handlers and rc9 | Accept | |
|
||
|
|
| 6 | Three small inconsistencies: the error category of `Get-NTFSAccess` after a failed ownership retry (`WriteError` there, `OpenError` elsewhere); `Get-NTFSOrphanedAccess` warns where its siblings write an error; `Set-NTFSSecurityDescriptor` takes `-Path` at position 2, its siblings at 1 | Scripts that check a category or pass a path by position | A breaking change for such scripts, and rc9 | Accept | |
|
||
|
|
| 7 | Seventeen owner-restore handlers do not pass on what a later command raises, in the rare case that it throws only for the `RestoreOwnerError` record | Rare | One check per handler, an elevated-only test, rc9 | Accept | |
|
||
|
|
| 8 | The ownership retry and the read denial of an audit write over SMB have no trigger on a Windows file server | None known | A seam (a design change), or the answer of a non-Windows server (#34) | Accept; #34 decides on 2026-11-02 | |
|
||
|
|
| 9 | Eight getters of piped parameters that no test binds by property name | None (tests only) | Tests only, no rc9 | Do it after the code of rc9 is final; the fix tests cover `AccessType`, `AuditFlags`, and `Algorithm` | |
|
||
|
|
| 10 | Lab and process only: the ACL of the lab stage folders under `C:\`, rollback checkpoints that report Standard, a publication recovery that never ran against the Gallery, test helpers that accept a link, the cleanup list (AppVeyor, Read the Docs, wiki, `test/transfer`, old checkpoints) | Not shipped | n/a | Accept; the cleanup before the archive is the maintainer's | |
|
||
|
|
| 11 | The deferred reviews of rc3 to rc7 (`progress.md`, open work 3) | Not new | n/a | Accept | |
|
||
|
|
|
||
|
|
Decided before: the hard-link names under another root stay a documented known
|
||
|
|
limit (Decision 21); Decisions 22 and 24 are accepted; #34 is decided on
|
||
|
|
2026-11-02 (Decision 23).
|
||
|
|
|
||
|
|
## The maintainer's choices, 2026-10-21
|
||
|
|
|
||
|
|
| Question | Answer |
|
||
|
|
| --- | --- |
|
||
|
|
| Scope of the fix of the piped optional parameters | B: the family in the four cmdlets (`AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType`, `AuditFlags`) |
|
||
|
|
| Items to change in rc9 | 3 (library-only items), 4 (`Get-ChildItem2 -Filter` dot patterns), 5 (descriptor sets report per descriptor), 6 (three inconsistencies), 7 (17 owner-restore handlers) |
|
||
|
|
| Items accepted as recommended | 8 (audit retry and read denial over SMB; #34 decides on 2026-11-02), 10, 11. Items 2 and 9 need no decision: the help sentence goes into rc9, and the tests for the eight getters follow when the code of rc9 is final |
|
||
|
|
| Independent review of the rc9 change | Yes (`review: on`) |
|
||
|
|
|
||
|
|
What follows: the agent proposes the design of each of the items 3 to 7
|
||
|
|
(options, risk, tests, a recommended default); the maintainer chooses; the work
|
||
|
|
goes test-first into one branch, one commit per item, so that any item can be
|
||
|
|
dropped. A defect that the review finds is fixed before rc9 is tagged; a defect
|
||
|
|
that the acceptance finds needs rc10. One acceptance (about 70 minutes with
|
||
|
|
`Chain-Published.ps1`) covers rc9 as a whole.
|
||
|
|
|
||
|
|
## Designs proposed, 2026-10-21
|
||
|
|
|
||
|
|
Five read-only research threads read the code, the tests, and the docs. The
|
||
|
|
sheet below is their result, verified in part by the probes above. Fix B is
|
||
|
|
decided; the rest is to be chosen. Recommended defaults are the agent's.
|
||
|
|
|
||
|
|
| # | Design | Recommended | Alternatives and risks |
|
||
|
|
| --- | --- | --- | --- |
|
||
|
|
| B | `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType` (access cmdlets), `AuditFlags` (audit cmdlets) become `object` parameters; ProcessRecord converts them with `LanguagePrimitives.ConvertTo` into locals. One shared error: id `InvalidPipedParameterValue`, category `InvalidData`, target the piped object, message "The value '{0}' for parameter '-{1}' could not be converted to '{2}'. The piped object was skipped." (blank: "The value for parameter '-{0}' is blank. The piped object was skipped."). A null `AccessType` or `AuditFlags` stays the default. Argument completers (`IArgumentCompleter`, available in the referenced Windows PowerShell 5.0 assembly) keep tab completion. Docs: `Type: Object`, help from `New-ExternalHelp`, the CHANGELOG paragraph at lines 247 to 258 | As described | `Get-FileHash2 -Algorithm` has the same flaw (option C, not chosen). Named calls change from a terminating binding error to the error record of the cmdlet |
|
||
|
|
| 5 | In the four `-SecurityDescriptor` branches, a `try/catch (Exception)` around the change of each descriptor writes `AddAceError` or `RemoveAceError`, category `WriteError`, target the descriptor, message of the exception, and goes on; the later-command check of item 7 goes into the same catch. Lines: `AddAccess.cs` 176 to 184, `RemoveAccess.cs` 177 to 185, `AddAudit.cs` 175 to 185, `RemoveAudit.cs` 176 to 186 | As described | Tests: a failing descriptor followed by a valid one; the 7 unpinned rows |
|
||
|
|
| 7 | One `IsFromLaterCommand(ex)` check, rethrowing, in each of the 17 outer handlers (the pattern of `SetSecurityDescriptor.cs` 72 to 101): `AddAccess`, `RemoveAccess`, `ClearAccess`, `GetAccess`, `GetOrphanedAccess`, `GetEffectiveAccess`, `AddAudit`, `RemoveAudit`, `ClearAudit`, `GetFileHash2`, the six inheritance cmdlets, and `SetSecurityDescriptor` | As described | Tests: one parameterized elevated-only test (a later command that throws on the `RestoreOwnerError` record), like the existing `restoreOwnerCases` |
|
||
|
|
| 3.1 | `FileSystemSecurity2` conversions from `FileSecurity` and `DirectorySecurity` keep the given descriptor | Fix | Remove the operators: breaking public API. Medium risk |
|
||
|
|
| 3.2 | `RemoveFileSystemAccessRuleAll` and `RemoveFileSystemAuditRuleAll` honor the account list | Fix | High risk for external callers (they remove entries); the cmdlets pass no list. Tests for both rule types |
|
||
|
|
| 3.3 | The path and several accounts overloads of the two add methods write at once, like their siblings | Fix | Medium risk (the moment of an exception changes) |
|
||
|
|
| 3.4 | `PrivilegeEnabler` no longer keeps an undisposed instance reachable | Defer unless the maintainer insists | High risk: token state in every cmdlet; a finalizer test is not deterministic; every cmdlet disposes its enabler today |
|
||
|
|
| 3.6 | The registry model (about 500 lines), `FileSystemFlags`, `FileSystemEffectivePermissionEntry`, and two raw descriptor readers | Keep and document (Decisions 21 and 22) | Removal breaks unknown .NET callers of the public DLL |
|
||
|
|
| 3.9 | `PipelineControl.IsEnd` | Keep as defense in depth | A change touches the pipeline semantics of all cmdlets for no known defect |
|
||
|
|
| 3.10 | The malformed `EnablePrivileges` setting: one validation in the base class, the dead catch removed | Fix | Medium risk (base class) |
|
||
|
|
| 4 | `Get-ChildItem2 -Filter`. A: pass the pattern unchanged to AlphaFS (the Windows PowerShell 5.1 rules; brings back the 8.3 short-name matches that a past fix removed). B: one cmdlet-side matcher with the Win32 dot rules on the real names and a broad enumeration (about 40 lines; costs a larger enumeration over SMB). D: keep as documented | B | Even `Get-ChildItem` differs between the editions here, so "aligned" needs a stated policy: the Windows PowerShell 5.1 dot rules on real names, in both editions |
|
||
|
|
| 6a | `Get-NTFSAccess`, the category of `ReadSecurityError` after a failed ownership retry: `WriteError` in `Get-NTFSAudit`, `Get-NTFSEffectiveAccess`, `Get-NTFSInheritance`, and `Get-NTFSSecurityDescriptor` too, so it is the majority convention | Keep, and pin it with a test | `OpenError` would break scripts that check the category, and make the module inconsistent unless six cmdlets change |
|
||
|
|
| 6b | `Get-NTFSOrphanedAccess` warns for a read failure other than access denied | An error: `ReadSecurityError`, `OpenError`, target the path | Breaks `-WarningAction` users; `-ErrorAction Stop` newly stops |
|
||
|
|
| 6c | `Set-NTFSSecurityDescriptor` has `-SecurityDescriptor` at position 2 and no other positional parameter | Position 1 | Cosmetic: a lone positional parameter binds first whatever its number |
|
||
|
|
|
||
|
|
## Assumption flagged for the maintainer's review
|
||
|
|
|
||
|
|
- The lab suite: he answered "not sure, you pick". The agent took the
|
||
|
|
recommendation: run the full suite (1,615 cases per configuration) on the
|
||
|
|
lab machine classes now, against the published rc8, so that an OS-specific
|
||
|
|
finding can still go into rc9 instead of forcing rc10. It changes nothing in
|
||
|
|
the repository or the product. Evidence: `suite-newtests` in the session
|
||
|
|
folder. Result (2026-10-21, 09:10 to 09:33 UTC): 24 runs on six machine
|
||
|
|
classes (Windows Server 2019, 2022, and 2025, Windows 11 22H2 and 26H1, and
|
||
|
|
the host), 0 failures. Every run gained exactly 121 cases against the rc8
|
||
|
|
acceptance, and the four new files give the counts of the CI runner (elevated
|
||
|
|
Desktop: Access 74/0, Audit 55/2, Inheritance 72/1, Items 90/0, executed and
|
||
|
|
skipped). No OS-specific finding. The machines are saved again, and
|
||
|
|
`OSWin11E` is off.
|