The independent review of the delta found the tests below weak. The verbose and debug rows of PipelineControl ran their command without an error action; the CI runner sets Stop, under which the handler that reports a later command's exception as an item error ended the pipeline with it, so the mutations that stop recording the verbose or the debug exception escaped (M19 in two configurations, M20 in all four). The rows now say -ErrorAction SilentlyContinue. The Enable-Privileges tests for a script named NTFSSecurity.Init.ps1 could not fail for the branch they name when the module setting was true, because the module enables the privileges itself before the cmdlet runs; they now also assert the verbose message that only the cmdlet writes, and they disable the privileges before each test so that the child process inherits none.
The first-nested-folder test asserts the first folder, not either. Set-TestNullDacl refuses an item that is a link, which the native call would follow out of the sandbox. A table pins the three outcomes that the filter documentation lists for a dot (Report.*, a trailing dot, an empty value), so a change of those rules is a decision.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Add-NTFSAccess, Remove-NTFSAccess, and Add-NTFSAudit report an AddAceError or RemoveAceError for each item when .NET refuses an entry without rights, change nothing, and return nothing with -PassThru. Get-NTFSAccess returns the one entry that .NET reports for a NULL DACL without a source; the new helper Set-TestNullDacl writes it through SetNamedSecurityInfo inside the sandbox guard. Get-NTFSHardLink lists the names of a file whose read rights are denied, which is why its UnauthorizedAccessException handler has no trigger. The public Extensions.ForEach and GetParent helpers, which a static scan listed as unused although cmdlets call them, are tested directly.
All of these characterize behavior that was already correct, so none was red before; the mutations that prove their detection run against the frozen measurement commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
New guards cover the default AllAccess token handle, token handles that lack the right to query or to adjust privileges, and the finalizer of an abandoned PrivilegeEnabler, which closes the handle that it owns; writes to the root of a drive that subst maps to a sandbox folder, which skip for the restricted token of the basic-user runner that cannot define a drive letter; Get-ChildItem2 -Filter against a match by short name; retargeting a descriptor with Item; the path overloads of the inheritance helpers for a missing path and of the audit rule reader for folders; removing a generic right while another account holds an exact entry; and an empty -ServerName.
Follow-ups of the independent review: dispose the enabler in the finally block before its handle, don't pin that the iterator overloads write only when enumerated, arrange the previous owner of the RestoreOwnerError test, and describe what ObjectApis.Tests.ps1 does.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
From the security-reviewer pass over be04cb7..4ee01e5 (Nit 8): the
probe for the administrative share and the conversion of a sandbox path
to \\localhost\C$\... were copied into ItemCmdlets.Tests.ps1 and
Links.Tests.ps1, and only the local path passed Assert-TestSandboxPath.
TestHelpers.psm1 now has Test-AdminShareAvailable and
ConvertTo-TestAdminSharePath, which checks the local path against the
sandbox before it returns the share path, with tests of their own.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Set-Acl compares AreAuditRulesProtected of the new descriptor with
AreAccessRulesProtected of the item (FileSystemSecurity.cs in PowerShell),
so it also writes the audit section of an item whose DACL is protected.
Without the Security privilege that fails with PrivilegeNotHeldException:
after Disable-NTFSAccessInheritance, Add-TestDenyRule added nothing, and
the take-ownership test of PathErrors.Tests.ps1 failed as a basic user.
With the privilege, Set-Acl wrote all sections and dropped the audit
entries of the item.
Add-TestDenyRule now writes only the DACL with SetAccessControl. Two
regression tests cover both effects; each failed before the change in its
configuration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
- Remove-NTFSAccess and Remove-NTFSAudit with -RemoveSpecific are tested
also with -Path, not only with -SecurityDescriptor.
- Copy-Item2 and Move-Item2 name the destination of a folder in their
verbose message, not only of a file.
- The Enable-Privileges count compares with the privileges of the token,
so that it passes as a basic user, whose token holds one; the tests of
-PassThru restore the privilege states that they found.
- The type name comparison of Get-FileHash2 is exact, the inherited-entry
counts must be greater than zero, and the braces test (#3) checks the
verbose message that raised the FormatException.
- Remove-TestSandbox removes paths longer than 260 characters in Windows
PowerShell, through the \\?\ prefix and rd, so the long-path test of
Test-Path2 no longer cleans up itself; after a failed setup, it returns
instead of stopping AfterAll with a binding error.
Not reachable by a test: a second path whose SACL read fails while the
Security privilege is enabled; a declined -Confirm takes the code path of
-WhatIf. The tests that need a session without privileges get the CI run
as a basic user.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
- #41: for the root of a drive, the cmdlets read and changed the security
descriptor of the drive, a device object. FileSystemSecurity2 now routes
drive roots through the path-based AlphaFS methods, which keep the
trailing backslash; the removal and inheritance helpers use it too.
- #109: Add-, Remove-, and Clear-NTFSAudit report a security descriptor
without the audit entries like Get-NTFSAudit, through one helper, and
Get-NTFSEffectiveAccess names the cause that Windows reported instead
of a missing Security privilege.
- #108: Copy-Item2 and Move-Item2 check the destination only for an
operation that runs; with -WhatIf, a verbose message names the conflict.
- #111: Disable-Privileges skips the privileges that the token doesn't
hold, the privilege messages are spelled right, and Get-FileHash2
declares the type name of its objects; 05-Releasing.md documents the
release metadata tests.
- rc3 review leftovers: Remove-NTFSAudit writes nothing for an item
without a SACL, the owner retry of Set-NTFSSecurityDescriptor restores
the previous owner in a finally block and keeps an owner that the
descriptor sets, and FileSystemSecurity2.Write with another item writes
only the sections that were read.
Each fix has a test that failed first, in Windows PowerShell 5.1 and
PowerShell 7; writing a drive root was checked once on a temporary VHD.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
- Set-NTFSSecurityDescriptor -Verbose names the sections that it writes,
or says that it writes nothing for an unchanged descriptor; its page
says "since it was read or last written" (review F-02).
- The pages of Enable-NTFSAccessInheritance, Disable-NTFSAccessInheritance,
and Set-NTFSInheritance get the #34 note, like the other fixed cmdlets
(review F-06).
- Set-TestOwner throws its own error when icacls fails, also when the
caller uses -ErrorAction Stop in Windows PowerShell (review F-07).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The access and audit cmdlets wrote the owner of an item back with the
entries they changed. For a DACL without the auto-inherit flag, Windows
returns the owner and the group even when only the DACL is read, and the
cmdlets wrote every section that the descriptor held. Without the Restore
privilege, or on a file server that refuses the owner, the write failed
with error 1307 (#34).
- Add-NTFSAccess, Clear-NTFSAccess, Add-NTFSAudit, and Clear-NTFSAudit
read only the DACL or the SACL. FileSystemSecurity2.Write(),
Remove-NTFSAccess, and Remove-NTFSAudit write only the sections they
read, which also fixes the access inheritance cmdlets.
- Read together with the SACL, the inherited entries of such a DACL lose
their inherited flag when the parent folder has no SACL, and the
cmdlets stored them as explicit copies. Get-NTFSSecurityDescriptor now
reads the DACL in a separate call.
- Set-NTFSSecurityDescriptor writes only the sections that changed since
they were read; an unchanged descriptor writes nothing (maintainer
decision of 2026-10-06).
- Clear-NTFSAudit writes nothing for an item without a SACL, and reports
an error without the Security privilege (maintainer decision).
The regression tests failed before and pass after the fix in Windows
PowerShell 5.1 and PowerShell 7, elevated and as a basic user.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
The cmdlets that take ownership of an item to repeat a denied operation
left the account that ran them as the owner when the second attempt failed
as well. BaseCmdlet.InvokeAsOwner now restores the previous owner on every
exit path and reports a failed restore as RestoreOwnerError.
Add-NTFSAccess, Add-NTFSAudit, Remove-NTFSAccess, and Remove-NTFSAudit
wrote the unchanged entries of an item with -PassThru after a failed
change; they now continue with the next path.
The inheritance tests assert the error identity and cover a missing path
on every runner.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Security review of this branch, Major findings:
- M4: Get-NTFSAudit took ownership of an item whose SACL it couldn't
read. Ownership grants no access to the SACL, so the retry always
failed, and it left the owner changed. The cmdlet now writes a
ReadSecurityError with the category PermissionDenied, as Get-NTFSOwner
does since defect 9; the page says so.
- M1: Remove-TestSandbox reset the ACLs recursively before it removed the
links. Measured: icacls /reset /T did not follow the junction (the
target's explicit entry stayed), but the links now go first anyway; a
folder that denies listing gets a reset without /T. New test: the ACL
of a junction target stays unchanged.
- m4: Assert-TestSandboxPath now rejects a path below a link, which can
point outside the sandbox (new test, failed before).
- M5: the Inherits column reads one ACL per displayed item; the
Get-ChildItem2 page names the cost and how to avoid it.
- M2: the comment of the CI-only Get-NTFSAudit repeat test states what it
guards; Access.Tests.ps1 guards the same loop fix without elevation.
- M3, the stale hash of Get-FileHash2, is fixed on ai/defects-c.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
- New-TestSandboxItem creates a uniquely named file or folder in a sandbox
after the path guard, replacing per-file copies.
- Block-TestReadPermission denies OWNER RIGHTS the right to read the
security descriptor of a sandbox item, so that reading it fails without
elevation.
- Remove-TestSandbox no longer stops when icacls cannot reset such an
item: Windows PowerShell turned the icacls message on stderr into a
terminating error; the item is still deleted through its folder.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Tests/TestHelpers.psm1 lets a test that changes files, links, ACLs,
owners, audit entries, or inheritance work in its own sandbox below
$env:TEMP\NTFSSecurity.Tests:
- New-TestSandbox creates the folder; Assert-TestSandboxPath throws
unless every target, relative ones resolved against the location, is
inside it.
- Remove-TestSandbox removes the links first without following them
(Windows PowerShell 5.1 follows directory links when it removes a
folder), resets ACL changes, and deletes the folder.
- Test-IsElevated and Test-PrivilegeHeld decide which tests can run; CI
runners are elevated, the workstation isn't.
Tests/TestHelpers.Tests.ps1 (14 tests) covers the helpers. CI runs every
*.Tests.ps1 file in Tests, so new test files need no workflow change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>