Write orphaned audit entries outside the read error handler, so that a
stopped pipeline isn't reported as a read error. Pass -RemoveSpecific
through the string path overload of RemoveFileSystemAuditRule, and state
on the Get-NTFSSimpleAccess page that the security descriptor of a file
is reported.
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>
Defect 17. Both cmdlets carried a removeSpecific field that no parameter
set, so they always took the rights away from matching entries; the
version history documents a -RemoveSpecific switch since 4.1. Both
cmdlets now have the switch, which removes only an entry that matches
exactly. The Security2 list overloads didn't pass the flag on, and the
audit item overload didn't support it; all overloads now do.
The applies-to field of both cmdlets is initialized; -AppliesTo is
mandatory in the Simple sets since defect 14, so it is always set when
used.
Tests: Access.Tests.ps1 3 tests, Audit.Tests.ps1 2 tests, on in-memory
security descriptors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 16. Get-NTFSOrphanedAccess, Get-NTFSOrphanedAudit, and
Get-NTFSSimpleAccess inherit -Account and -SecurityDescriptor from
Get-NTFSAccess or Get-NTFSAudit but override ProcessRecord without them:
- All three now filter by -Account and process security descriptors
(Get-NTFSOrphanedAudit writes an error for one read without its SACL,
like Get-NTFSAudit).
- Get-NTFSOrphanedAudit wrote the entries of each item as one collection;
it now writes one object per entry.
- Get-NTFSOrphanedAccess kept the entries of the previous item and wrote
them in a finally block, like Get-NTFSAccess before; each item now
starts empty.
- SimpleFileSystemAccessRule had no view; it gets a table with Account,
Access Rights, and Type, grouped by folder, with the grouping control
that existed for it but was unused.
Tests: Access.Tests.ps1 6 tests; Audit.Tests.ps1 2 tests, CI-only because
they add audit entries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 15, all three parts:
- -ExcludeNoneAccessEntries had no effect: the result was written in a
finally block, so "continue" didn't skip it, and the check compared the
rights with None although .NET adds Synchronize to every allow rule. A
result is now written only after the checks, and Synchronize alone
counts as no access.
- Without -Path, BeginProcessing tested the path list for null, which it
never is, so the cmdlet wrote nothing. It now uses the current location,
like the other cmdlets.
- ProcessRecord ignored the SecurityDescriptor parameter set. A new
EffectiveAccess overload computes the effective access from an
in-memory security descriptor; the item overload uses it.
The Security privilege state that selects the error message was read into
a local variable that hid the field; the field is now set.
Tests/Access.Tests.ps1: 4 tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 14. Add-NTFSAccess, Remove-NTFSAccess, Add-NTFSAudit, and
Remove-NTFSAudit have the sets PathSimple, PathComplex, SDSimple, and
SDComplex; the default PathComplex needs -Path. A command with
-SecurityDescriptor and without -AppliesTo, -InheritanceFlags, or
-PropagationFlags matched both SD sets and failed with "Parameter set
cannot be resolved". -AppliesTo is now mandatory in the Simple sets, so
such a command resolves to the Complex set and its default flags
(ContainerInherit, ObjectInherit / None, which is what AppliesTo
ThisFolderSubfoldersAndFiles means), as a command with -Path already did.
With -AppliesTo nothing changes.
The pages say "Required: True" for -AppliesTo and drop its never
reachable defaults; platyPS takes that metadata from the shipped help
file, so the help file was regenerated before the build.
Tests/Access.Tests.ps1: 6 tests on in-memory security descriptors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 13. The Inherits column of the Children2 view, which formats the
Get-ChildItem2 output, negated the IsInheritanceBlocked property. The
module defines that property for System.IO.FileInfo and DirectoryInfo
only, not for the AlphaFS objects that Get-ChildItem2 returns, so the
column showed !$null, that is True, for every item. The view now reads
the protection of the DACL from the item.
Tests/ItemCmdlets.Tests.ps1: 2 tests on the formatted output.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 12. Besides the base class, which enables the privileges only when
the module setting EnablePrivileges is $true and disables them again in
EndProcessing, the six inheritance cmdlets called
EnableFileSystemPrivileges in BeginProcessing unconditionally. With
EnablePrivileges = $false they enabled the privileges anyway and, because
EndProcessing disables them only when the setting is $true, left them
enabled. The extra calls are gone; the inheritance cmdlets now behave like
the other cmdlets.
Tests/Privileges.Tests.ps1: 6 tests, one per cmdlet, CI-only, because
they need a token that holds the privileges. Without the fix the Backup
privilege is enabled after each call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 11. DisableFileSystemPrivileges read the privileges of the token
into a local variable that hid the field, and DisablePrivilege read the
field. With the module setting EnablePrivileges = $false, BeginProcessing
never filled the field, so every DisablePrivilege call hit a null
reference, which TryDisablePrivilege turned into a warning, and the
privileges stayed enabled. The method now refreshes the field.
Tests/Privileges.Tests.ps1 (new): 1 test, CI-only, because it needs a
token that holds the privileges. Without the fix it fails on the warnings
and on the Backup privilege that stays enabled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Completes the documentation of defect 1: the descriptions of
-AccessInheritanceEnabled and -AuditInheritanceEnabled now say that an
omitted value leaves its section unchanged, instead of asking to always
supply the parameter to avoid "Nullable object must have a value".
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 10. AlphaFS 2.2 copies a folder only into an existing destination
folder; otherwise DirectoryInfo.CopyTo fails with a
DirectoryNotFoundException for the first file, so Copy-Item2 could not
copy a folder that contained files. The cmdlet now creates the
destination folder first; AlphaFS then copies the files and subfolders.
Tests/ItemCmdlets.Tests.ps1: 1 test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 9, both parts:
- Get-NTFSOwner wrote each owner inside a try block whose catch-all
turned every exception into a ReadSecurityError. When a command such as
Select-Object -First 1 stopped the pipeline, the cmdlet wrote "The
pipeline has been stopped" as an error for every path, and the stop
escaped into the caller's script. The owner is now written after the
try block.
- After access was denied, the retry called the same failing GetOwner
again before taking ownership, so it always failed and reported a
WriteError. Taking ownership would also replace the owner that the
cmdlet reports. The cmdlet now writes a ReadSecurityError with the
category PermissionDenied and continues.
Tests/Owner.Tests.ps1 (new): 2 tests; the access-denied test skips where
the Backup privilege may bypass the deny entry (CI).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 8. For a security descriptor that was read without its SACL (no
Security privilege), Get-NTFSInheritance -SecurityDescriptor reported
AuditInheritanceEnabled as $true, because the protection flag of a
section that was never read is not set. It now reports $null, like the
Path parameter set, using the sections that FileSystemSecurity2 records
since defect 4.
Set-NTFSInheritance -SecurityDescriptor reads the same state, so it no
longer skips a requested audit change on such a descriptor as "equal".
Tests/Inheritance.Tests.ps1: 2 tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 7. FileSystemAuditRule2.GetFileSystemAuditRules set the
InheritanceEnabled property of every audit entry from
AreAccessRulesProtected, the protection of the DACL. It now uses
AreAuditRulesProtected, so the property, and the "Inheritance enabled"
header of the audit view, describe the audit entries.
Tests/Audit.Tests.ps1: 1 test on an in-memory security descriptor whose
SACL is protected and whose DACL is not.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 6. With -PassThru, Add-NTFSAudit returned the access entries of a
security descriptor in its SecurityDescriptor sets, and Remove-NTFSAudit
returned the access entries of the item in its Path sets. Both now return
the audit entries, as in their other parameter sets.
Tests/Audit.Tests.ps1: 2 tests; the Remove-NTFSAudit test writes a SACL
and runs in CI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 5 (#4). -Account and -AccessRights of Add-NTFSAudit were both
declared at position 2, so a positional call failed with "Cannot bind
positional parameters because no names were given". -AccessRights is now
at position 3 in all four parameter sets, like in Remove-NTFSAudit.
The page's parameter metadata says position 3 as well. platyPS takes the
position from Get-Help, that is from the shipped help file, so
Update-MarkdownHelp kept the old value; the page, the regenerated help
file, and the build now agree.
Tests/Audit.Tests.ps1: 5 tests (positions in each parameter set and a
positional call against an in-memory security descriptor).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Found while fixing defect 4: Get-NTFSAccess has the same pattern as
Get-NTFSAudit. It kept the entries of the previous item and wrote them in
a finally block, so a path whose ACL failed to read returned the previous
item's entries again, next to the error. Each item now starts empty, and
entries are written only after a successful read.
Tests/Access.Tests.ps1 (new): 1 test; it failed before the fix with 6
entries instead of 3.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 4, both parts:
- Get-NTFSAudit kept the entries of the previous item and wrote them in a
finally block, so a path whose security descriptor failed to read
returned the previous item's entries again. Each item now starts empty,
and entries are written only after a successful read.
- Without the Security privilege, the cmdlet read the descriptor without
its SACL and returned nothing, like an item without audit entries. It
now reads the SACL alone, so a missing privilege is a ReadSecurityError
("A required privilege is not held by the client"). A descriptor from
Get-NTFSSecurityDescriptor that was read without the SACL gets the same
error; FileSystemSecurity2 now records which sections it read
(internal, visible to NTFSSecurity).
Tests/Audit.Tests.ps1 (new): 3 tests. The repeat test needs the Security
privilege to add an audit entry and runs in CI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 3. Get-FileHash2 left ProcessRecord at the first folder in -Path,
so the files that followed the folder in the same array were not hashed.
It now skips the folder, like Get-FileHash, and continues.
Tests/FileHash.Tests.ps1 (new): 1 test. It skips in PowerShell 7, where
every Get-FileHash2 call fails until the RIPEMD160 reference goes
(decision D5, later in this run).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 2. Get-ChildItem2 cast every -Path item to DirectoryInfo, so a file
path stopped the cmdlet with an InvalidCastException, a terminating error
that also skipped the remaining paths. Like Get-ChildItem, a file path now
returns the file itself, filtered like the other items; with -Directory it
returns nothing.
Tests/ItemCmdlets.Tests.ps1 (new): 3 tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
Defect 1. Set-NTFSInheritance compared the current state with an unset
Nullable<bool> and then read its value, so omitting
-AccessInheritanceEnabled always failed with "Nullable object must have a
value", and omitting -AuditInheritanceEnabled failed wherever the audit
section is readable. In the SecurityDescriptor set the error was
terminating.
An omitted parameter now leaves its section unchanged. The item, retry,
and security descriptor paths share one implementation instead of three
copies.
Tests/Inheritance.Tests.ps1 (new): 4 tests; the audit case needs the
Security privilege and runs in CI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
- Require Windows PowerShell 5.1 or PowerShell 7 (PowerShellVersion
5.1, which CompatiblePSEditions needs) and .NET Framework 4.5.2, and
use RootModule instead of the deprecated ModuleToProcess. Before,
Test-ModuleManifest, and with it Publish-Module, failed.
- Export exactly the 36 cmdlets: remove Show-NTFSSimpleAccess, which no
longer exists, and the duplicate inheritance cmdlets.
- Keep -PassThur, the name in 4.2.6 and earlier, as an alias of
Remove-Item2 -PassThru, deprecated in the changelog.
- Set version 5.0.0 in the manifest, in NTFSSecurity, Security2, and
PrivilegeControl, and in a new 5.0.0 section of CHANGELOG.md.
- Tests/Manifest.Tests.ps1 and Tests/Remove-Item2.Tests.ps1 guard the
manifest, the versions, and the alias.
BREAKING CHANGE: the module requires Windows PowerShell 5.1 or
PowerShell 7; the manifest no longer claims PowerShell 2.0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* fix(help): ship the generated help file so Get-Help works
Get-Help showed only the syntax of the cmdlets: the module shipped a
pre-4.x MAML file for the old command names under the wrong name
(NTFSSecurity-Help.xml), while PowerShell looks for
en-US\NTFSSecurity.dll-Help.xml.
- Generate en-US\NTFSSecurity.dll-Help.xml from Docs/Cmdlets with
New-ExternalHelp and commit it. The csproj copies it to the output,
so every build ships it, including the local Debug builds that
releases are published from.
- List all runtime files, including the help file, in FileList.
- Remove the stale NTFSSecurity-Help.xml and the unused help editor
project NTFSSecurity\Help\NTFSSecurity.Help.pshproj.
- Add Tests\Help.Tests.ps1 (Pester 5): Get-Help shows the synopsis,
parameters, examples, and online link of every page, and
Get-Help -Online resolves to the GitHub page.
- Reword six sentences in five cmdlet pages so that each link ends its
sentence: platyPS drops the space after a link in the help text.
- CI regenerates the help file and fails when it differs from the
committed file, then runs the Pester tests.
- Document the regeneration step and the link rule in the contributor
guide.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* ci: report each Pester test once on AppVeyor
AppVeyor build 54834154 passed all 218 Pester tests but listed 870 on
its Tests tab: the NUnit import files a Pester 5 test under every block
that contains it (Pester, test file, Describe, and Context).
Report the results through the build worker API instead
(POST api/tests/batch): one entry per test with its outcome, duration,
and error message. Outside AppVeyor, and when no test ran, the step
sends nothing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* chore: initialize the memory bank
Add the canonical .memory-bank base with evidence-based project context:
purpose and scope, workflows, stack and validation commands, architecture
map, decisions, and the open work found while documenting the cmdlets.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* docs: align documentation with the cmdlet source
- Fill all 36 platyPS cmdlet pages from the C# source: synopsis,
description, parameters, defaults, examples, inputs, outputs, and
notes, including documented limitations of the current code
- Check every example against live parameter metadata and run them in a
sandbox; fix examples that did not work (CSV restore, account filter,
recursive inheritance, -AccessRights typos)
- Rewrite the home, concepts, examples, README, and contributor pages;
add a grouped cmdlet overview, module settings, privileges, long paths,
and the platyPS workflow
- Document Remove-Item2 -PassThru as renamed after 4.2.6 (#64)
- Fix mkdocs.yml navigation, edit_uri, and copyright markup; add
build.os and a pinned MkDocs version for Read the Docs
- Point online help links to the pages on GitHub; add CHANGELOG.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* ci: build the module and check the docs against the build
The documentation check ran Update-MarkdownHelp against the NTFSSecurity
release from the PowerShell Gallery (4.2.6), so it failed for every
unreleased parameter change. PR #91 failed because 4.2.6 still has
Remove-Item2 -PassThur while the source and the docs have -PassThru.
- Build NTFSSecurity.csproj in Release on the Visual Studio 2022 image,
using the .NET Framework 4.5.2 reference assemblies package instead of
an installed targeting pack
- Check Docs/Cmdlets against the module built from source
- Pin platyPS 0.14.2 and MarkdownLinkCheck 0.2.0, and enable TLS 1.2 so
the NuGet provider bootstrap works
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
* chore: record the green PR 91 build in the memory bank
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>