From f2c551e17abc4d788a2a2e4bb4bea20fc6457b1d Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Mon, 5 Oct 2026 01:45:16 +0200 Subject: [PATCH] fix: declare and write the right output types Defect 22: - [OutputType]: Test-Path2 writes System.Boolean, not file objects; Get-FileHash2 writes the file object with Hash and Algorithm, not access rules; Add-NTFSAudit and Remove-NTFSAudit write audit entries since defect 6; Copy-Item2, Move-Item2, Remove-Item2, and the five inheritance cmdlets with -PassThru declared no type. - Enable-Privileges and Disable-Privileges with -PassThru wrote the privileges as one collection; they now enumerate it. - New-NTFSSymbolicLink -PassThru returned a FileInfo for a link to a folder; it now returns a DirectoryInfo. The OUTPUTS sections of the pages name the same types. Tests/OutputTypes.Tests.ps1 (new): 15 tests; Disable-Privileges and the symbolic link need privileges and run in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 8 ++ Docs/Cmdlets/Copy-Item2.md | 8 +- Docs/Cmdlets/Disable-NTFSAccessInheritance.md | 2 +- Docs/Cmdlets/Disable-NTFSAuditInheritance.md | 2 +- Docs/Cmdlets/Disable-Privileges.md | 2 +- Docs/Cmdlets/Enable-NTFSAccessInheritance.md | 2 +- Docs/Cmdlets/Enable-NTFSAuditInheritance.md | 2 +- Docs/Cmdlets/Enable-Privileges.md | 2 +- Docs/Cmdlets/Get-FileHash2.md | 4 +- Docs/Cmdlets/Move-Item2.md | 8 +- Docs/Cmdlets/New-NTFSSymbolicLink.md | 4 +- Docs/Cmdlets/Remove-Item2.md | 8 +- Docs/Cmdlets/Set-NTFSInheritance.md | 2 +- Docs/Cmdlets/Test-Path2.md | 8 +- NTFSSecurity/AuditCmdlets/AddAudit.cs | 2 +- NTFSSecurity/AuditCmdlets/RemoveAudit.cs | 2 +- .../DisableAccessInheritance.cs | 1 + .../DisableAuditInheritance.cs | 1 + .../EnableAccessInheritance.cs | 1 + .../EnableAuditInheritance.cs | 1 + .../InheritanceCmdlets/SetInheritance.cs | 1 + NTFSSecurity/ItemCmdlets/CopyItem2.cs | 1 + NTFSSecurity/ItemCmdlets/MoveItem2.cs | 1 + NTFSSecurity/ItemCmdlets/RemoveItem2.cs | 1 + NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs | 5 +- NTFSSecurity/MiscCmdlets/GetFileHash2.cs | 2 +- NTFSSecurity/OtherCmdlets.cs | 4 +- NTFSSecurity/PathCmdlets/TestPath2.cs | 2 +- NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml | 70 +++++++++------ Tests/OutputTypes.Tests.ps1 | 87 +++++++++++++++++++ 30 files changed, 187 insertions(+), 57 deletions(-) create mode 100644 Tests/OutputTypes.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c73265..a523644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,5 +134,13 @@ The format is based on - Fix the cmdlets that take ownership of an item to repeat an operation that was denied: when the second attempt failed as well, the account that ran the cmdlet stayed the owner of the item; now the previous owner is restored +- Fix `-PassThru` of `Enable-Privileges` and `Disable-Privileges`, which + wrote the privileges as one collection instead of one object per + privilege, and of `New-NTFSSymbolicLink`, which returned a file object for + a link to a folder +- Declare the output types of `Test-Path2`, `Get-FileHash2`, + `Add-NTFSAudit`, `Remove-NTFSAudit`, `Copy-Item2`, `Move-Item2`, + `Remove-Item2`, and the inheritance cmdlets correctly, so that + `Get-Command` and tab completion report the objects they write [Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD diff --git a/Docs/Cmdlets/Copy-Item2.md b/Docs/Cmdlets/Copy-Item2.md index 9bd30c6..f43b357 100644 --- a/Docs/Cmdlets/Copy-Item2.md +++ b/Docs/Cmdlets/Copy-Item2.md @@ -176,9 +176,13 @@ You can pipe an object that has a `Destination` property to supply the target of ## OUTPUTS -### System.Object +### Alphaleonis.Win32.Filesystem.FileInfo -By default this cmdlet returns nothing. With `-PassThru $true` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it copied. +By default this cmdlet returns nothing. With `-PassThru $true` it returns a file object for each file that it copied. + +### Alphaleonis.Win32.Filesystem.DirectoryInfo + +With `-PassThru $true` the cmdlet returns a folder object for each folder that it copied. ## NOTES diff --git a/Docs/Cmdlets/Disable-NTFSAccessInheritance.md b/Docs/Cmdlets/Disable-NTFSAccessInheritance.md index fc19225..0a8ca64 100644 --- a/Docs/Cmdlets/Disable-NTFSAccessInheritance.md +++ b/Docs/Cmdlets/Disable-NTFSAccessInheritance.md @@ -156,7 +156,7 @@ You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns ## OUTPUTS -### System.Object +### Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. diff --git a/Docs/Cmdlets/Disable-NTFSAuditInheritance.md b/Docs/Cmdlets/Disable-NTFSAuditInheritance.md index 18ad002..4fca4bc 100644 --- a/Docs/Cmdlets/Disable-NTFSAuditInheritance.md +++ b/Docs/Cmdlets/Disable-NTFSAuditInheritance.md @@ -156,7 +156,7 @@ You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns ## OUTPUTS -### System.Object +### Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. diff --git a/Docs/Cmdlets/Disable-Privileges.md b/Docs/Cmdlets/Disable-Privileges.md index 0d48b8f..223adff 100644 --- a/Docs/Cmdlets/Disable-Privileges.md +++ b/Docs/Cmdlets/Disable-Privileges.md @@ -93,7 +93,7 @@ This cmdlet does not accept pipeline input. ### ProcessPrivileges.PrivilegeAndAttributes -With `-PassThru`, the cmdlet writes the privilege collection of the current process. The pipeline enumerates it into one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. +With `-PassThru`, the cmdlet writes one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege of the current process, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. Before 5.0.0, it wrote the privileges as one collection. ## NOTES diff --git a/Docs/Cmdlets/Enable-NTFSAccessInheritance.md b/Docs/Cmdlets/Enable-NTFSAccessInheritance.md index 0a8b6da..6f38c39 100644 --- a/Docs/Cmdlets/Enable-NTFSAccessInheritance.md +++ b/Docs/Cmdlets/Enable-NTFSAccessInheritance.md @@ -155,7 +155,7 @@ You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns ## OUTPUTS -### System.Object +### Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. diff --git a/Docs/Cmdlets/Enable-NTFSAuditInheritance.md b/Docs/Cmdlets/Enable-NTFSAuditInheritance.md index 4765a1b..36fd685 100644 --- a/Docs/Cmdlets/Enable-NTFSAuditInheritance.md +++ b/Docs/Cmdlets/Enable-NTFSAuditInheritance.md @@ -155,7 +155,7 @@ You can pipe the security descriptors that `Get-NTFSSecurityDescriptor` returns ## OUTPUTS -### System.Object +### Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. diff --git a/Docs/Cmdlets/Enable-Privileges.md b/Docs/Cmdlets/Enable-Privileges.md index a8c7fa3..cc0c479 100644 --- a/Docs/Cmdlets/Enable-Privileges.md +++ b/Docs/Cmdlets/Enable-Privileges.md @@ -95,7 +95,7 @@ This cmdlet does not accept pipeline input. ### ProcessPrivileges.PrivilegeAndAttributes -With `-PassThru`, the cmdlet writes the privilege collection of the current process. The pipeline enumerates it into one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. +With `-PassThru`, the cmdlet writes one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege of the current process, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. Before 5.0.0, it wrote the privileges as one collection. ## NOTES diff --git a/Docs/Cmdlets/Get-FileHash2.md b/Docs/Cmdlets/Get-FileHash2.md index 6dd281e..783aa4d 100644 --- a/Docs/Cmdlets/Get-FileHash2.md +++ b/Docs/Cmdlets/Get-FileHash2.md @@ -111,9 +111,9 @@ You can supply the `-Algorithm` value through a pipeline object that has an `Alg ## OUTPUTS -### Security2.FileSystemAccessRule2 +### Alphaleonis.Win32.Filesystem.FileInfo -The cmdlet does not return access rules. For every hashed file it writes the file object of that file, decorated with the type name `Alphaleonis.Win32.Filesystem.FileInfo+Hash` and extended with the `Hash` and `Algorithm` note properties, so all regular file properties such as `FullName`, `Name`, and `Length` remain available. +For every hashed file, the cmdlet writes the file object of that file, decorated with the type name `Alphaleonis.Win32.Filesystem.FileInfo+Hash` and extended with the `Hash` and `Algorithm` note properties, so all regular file properties such as `FullName`, `Name`, and `Length` remain available. ## NOTES diff --git a/Docs/Cmdlets/Move-Item2.md b/Docs/Cmdlets/Move-Item2.md index b4f3eff..0f49d4d 100644 --- a/Docs/Cmdlets/Move-Item2.md +++ b/Docs/Cmdlets/Move-Item2.md @@ -176,9 +176,13 @@ You can pipe an object that has a `Destination` property to supply the target of ## OUTPUTS -### System.Object +### Alphaleonis.Win32.Filesystem.FileInfo -By default this cmdlet returns nothing. With `-PassThru $true` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it moved, pointing at the new location. +By default this cmdlet returns nothing. With `-PassThru $true` it returns a file object for each file that it moved, pointing at the new location. + +### Alphaleonis.Win32.Filesystem.DirectoryInfo + +With `-PassThru $true` the cmdlet returns a folder object for each folder that it moved, pointing at the new location. ## NOTES diff --git a/Docs/Cmdlets/New-NTFSSymbolicLink.md b/Docs/Cmdlets/New-NTFSSymbolicLink.md index 2be53db..b1757ff 100644 --- a/Docs/Cmdlets/New-NTFSSymbolicLink.md +++ b/Docs/Cmdlets/New-NTFSSymbolicLink.md @@ -124,11 +124,11 @@ You can pass the path of the new link and the path of the target as strings. ### Alphaleonis.Win32.Filesystem.FileInfo -With `-PassThru`, the cmdlet writes a file object for the new link. The cmdlet writes that object type for a link to a folder as well. Without `-PassThru`, the cmdlet writes nothing. +With `-PassThru`, the cmdlet writes a file object for a new link to a file. Without `-PassThru`, the cmdlet writes nothing. ### Alphaleonis.Win32.Filesystem.DirectoryInfo -The cmdlet does not write folder objects. A directory symbolic link is also returned as a file object. +With `-PassThru`, the cmdlet writes a folder object for a new link to a folder. Before 5.0.0, it wrote a file object for those links as well. ## NOTES diff --git a/Docs/Cmdlets/Remove-Item2.md b/Docs/Cmdlets/Remove-Item2.md index 0669f84..9b28de8 100644 --- a/Docs/Cmdlets/Remove-Item2.md +++ b/Docs/Cmdlets/Remove-Item2.md @@ -169,9 +169,13 @@ You can pipe one or more paths to this cmdlet, either as strings or as objects t ## OUTPUTS -### System.Object +### Alphaleonis.Win32.Filesystem.FileInfo -By default this cmdlet returns nothing. With `-PassThru` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it deleted. +By default this cmdlet returns nothing. With `-PassThru` it returns a file object for each file that it deleted. + +### Alphaleonis.Win32.Filesystem.DirectoryInfo + +With `-PassThru` the cmdlet returns a folder object for each folder that it deleted. ## NOTES diff --git a/Docs/Cmdlets/Set-NTFSInheritance.md b/Docs/Cmdlets/Set-NTFSInheritance.md index 34c141f..be5a2d4 100644 --- a/Docs/Cmdlets/Set-NTFSInheritance.md +++ b/Docs/Cmdlets/Set-NTFSInheritance.md @@ -178,7 +178,7 @@ You can supply `-AccessInheritanceEnabled` and `-AuditInheritanceEnabled` throug ## OUTPUTS -### System.Object +### Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. diff --git a/Docs/Cmdlets/Test-Path2.md b/Docs/Cmdlets/Test-Path2.md index ee925f0..41e14d6 100644 --- a/Docs/Cmdlets/Test-Path2.md +++ b/Docs/Cmdlets/Test-Path2.md @@ -109,13 +109,9 @@ You can supply the `-PathType` value through a pipeline object that has a `PathT ## OUTPUTS -### Alphaleonis.Win32.Filesystem.FileInfo +### System.Boolean -`Test-Path2` does not write file objects. For each path it writes a single `System.Boolean` value that is `$true` when the item exists and matches `-PathType`, and `$false` otherwise. - -### Alphaleonis.Win32.Filesystem.DirectoryInfo - -`Test-Path2` does not write folder objects either. A folder is reported through the same `System.Boolean` result as a file. +For each path, the cmdlet writes `$true` when the item exists and matches `-PathType`, and `$false` otherwise. ## NOTES diff --git a/NTFSSecurity/AuditCmdlets/AddAudit.cs b/NTFSSecurity/AuditCmdlets/AddAudit.cs index 4ce3edb..d5a11f2 100644 --- a/NTFSSecurity/AuditCmdlets/AddAudit.cs +++ b/NTFSSecurity/AuditCmdlets/AddAudit.cs @@ -8,7 +8,7 @@ using System.Security.AccessControl; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Add, "NTFSAudit", DefaultParameterSetName = "PathComplex")] - [OutputType(typeof(FileSystemAccessRule2))] + [OutputType(typeof(FileSystemAuditRule2))] public class AddAudit : BaseCmdletWithPrivControl { private IdentityReference2[] account; diff --git a/NTFSSecurity/AuditCmdlets/RemoveAudit.cs b/NTFSSecurity/AuditCmdlets/RemoveAudit.cs index f075b30..c9b618b 100644 --- a/NTFSSecurity/AuditCmdlets/RemoveAudit.cs +++ b/NTFSSecurity/AuditCmdlets/RemoveAudit.cs @@ -8,7 +8,7 @@ using System.Security.AccessControl; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Remove, "NTFSAudit", DefaultParameterSetName = "PathComplex")] - [OutputType(typeof(FileSystemAccessRule2))] + [OutputType(typeof(FileSystemAuditRule2))] public class RemoveAudit : BaseCmdletWithPrivControl { private IdentityReference2[] account; diff --git a/NTFSSecurity/InheritanceCmdlets/DisableAccessInheritance.cs b/NTFSSecurity/InheritanceCmdlets/DisableAccessInheritance.cs index f459e61..8204ba6 100644 --- a/NTFSSecurity/InheritanceCmdlets/DisableAccessInheritance.cs +++ b/NTFSSecurity/InheritanceCmdlets/DisableAccessInheritance.cs @@ -6,6 +6,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsLifecycle.Disable, "NTFSAccessInheritance", DefaultParameterSetName = "Path")] + [OutputType(typeof(FileSystemInheritanceInfo))] public class DisableAccessInheritance : BaseCmdletWithPrivControl { private bool removeInheritedAccessRules; diff --git a/NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs b/NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs index bf51018..c64168e 100644 --- a/NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs +++ b/NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs @@ -7,6 +7,7 @@ namespace NTFSSecurity { [Cmdlet(VerbsLifecycle.Disable, "NTFSAuditInheritance", DefaultParameterSetName = "Path")] + [OutputType(typeof(FileSystemInheritanceInfo))] public class DisableAuditInheritance : BaseCmdletWithPrivControl { private bool removeInheritedAccessRules; diff --git a/NTFSSecurity/InheritanceCmdlets/EnableAccessInheritance.cs b/NTFSSecurity/InheritanceCmdlets/EnableAccessInheritance.cs index 1821e49..8db7bc6 100644 --- a/NTFSSecurity/InheritanceCmdlets/EnableAccessInheritance.cs +++ b/NTFSSecurity/InheritanceCmdlets/EnableAccessInheritance.cs @@ -6,6 +6,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsLifecycle.Enable, "NTFSAccessInheritance", DefaultParameterSetName = "Path")] + [OutputType(typeof(FileSystemInheritanceInfo))] public class EnableAccessInheritance : BaseCmdletWithPrivControl { private bool removeExplicitAccessRules; diff --git a/NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs b/NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs index d3c753c..5a19158 100644 --- a/NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs +++ b/NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs @@ -6,6 +6,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsLifecycle.Enable, "NTFSAuditInheritance", DefaultParameterSetName = "Path")] + [OutputType(typeof(FileSystemInheritanceInfo))] public class EnableAuditInheritance : BaseCmdletWithPrivControl { private bool removeExplicitAccessRules; diff --git a/NTFSSecurity/InheritanceCmdlets/SetInheritance.cs b/NTFSSecurity/InheritanceCmdlets/SetInheritance.cs index 3dcc8b3..ec3333b 100644 --- a/NTFSSecurity/InheritanceCmdlets/SetInheritance.cs +++ b/NTFSSecurity/InheritanceCmdlets/SetInheritance.cs @@ -6,6 +6,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Set, "NTFSInheritance", DefaultParameterSetName = "Path")] + [OutputType(typeof(FileSystemInheritanceInfo))] public class SetInheritance : BaseCmdletWithPrivControl { private bool? accessInheritanceEnabled; diff --git a/NTFSSecurity/ItemCmdlets/CopyItem2.cs b/NTFSSecurity/ItemCmdlets/CopyItem2.cs index 3e391d9..ed373da 100644 --- a/NTFSSecurity/ItemCmdlets/CopyItem2.cs +++ b/NTFSSecurity/ItemCmdlets/CopyItem2.cs @@ -5,6 +5,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Copy, "Item2", SupportsShouldProcess = true)] + [OutputType(typeof(FileInfo), typeof(DirectoryInfo))] public class CopyItem2 : BaseCmdlet { private string destination; diff --git a/NTFSSecurity/ItemCmdlets/MoveItem2.cs b/NTFSSecurity/ItemCmdlets/MoveItem2.cs index 2a360e1..33d7369 100644 --- a/NTFSSecurity/ItemCmdlets/MoveItem2.cs +++ b/NTFSSecurity/ItemCmdlets/MoveItem2.cs @@ -5,6 +5,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Move, "Item2", SupportsShouldProcess = true)] + [OutputType(typeof(FileInfo), typeof(DirectoryInfo))] public class MoveItem2 : BaseCmdlet { private string destination; diff --git a/NTFSSecurity/ItemCmdlets/RemoveItem2.cs b/NTFSSecurity/ItemCmdlets/RemoveItem2.cs index de34ca0..252d80b 100644 --- a/NTFSSecurity/ItemCmdlets/RemoveItem2.cs +++ b/NTFSSecurity/ItemCmdlets/RemoveItem2.cs @@ -5,6 +5,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Remove, "Item2", SupportsShouldProcess = true)] + [OutputType(typeof(FileInfo), typeof(DirectoryInfo))] public class RemoveItem2 : BaseCmdlet { private SwitchParameter force; diff --git a/NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs b/NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs index 81d85ca..64ee2a1 100644 --- a/NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs +++ b/NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs @@ -70,7 +70,10 @@ namespace NTFSSecurity if (passThru) { - WriteObject(new FileInfo(path)); + if (targetItem is FileInfo) + WriteObject(new FileInfo(path)); + else + WriteObject(new DirectoryInfo(path)); } } catch (System.IO.FileNotFoundException ex) diff --git a/NTFSSecurity/MiscCmdlets/GetFileHash2.cs b/NTFSSecurity/MiscCmdlets/GetFileHash2.cs index 2e46f78..91ef656 100644 --- a/NTFSSecurity/MiscCmdlets/GetFileHash2.cs +++ b/NTFSSecurity/MiscCmdlets/GetFileHash2.cs @@ -7,7 +7,7 @@ using Security2.FileSystem.FileInfo; namespace NTFSSecurity { [Cmdlet(VerbsCommon.Get, "FileHash2")] - [OutputType(typeof(FileSystemAccessRule2))] + [OutputType(typeof(FileInfo))] public class GetFileHash2 : BaseCmdlet { private HashAlgorithms algorithm = HashAlgorithms.SHA256; diff --git a/NTFSSecurity/OtherCmdlets.cs b/NTFSSecurity/OtherCmdlets.cs index 83edb5d..b34aa17 100644 --- a/NTFSSecurity/OtherCmdlets.cs +++ b/NTFSSecurity/OtherCmdlets.cs @@ -54,7 +54,7 @@ namespace NTFSSecurity if (passThru) { - this.WriteObject(this.privControl.GetPrivileges()); + this.WriteObject(this.privControl.GetPrivileges(), true); } } @@ -104,7 +104,7 @@ namespace NTFSSecurity if (passThru) { - this.WriteObject(this.privControl.GetPrivileges()); + this.WriteObject(this.privControl.GetPrivileges(), true); } } diff --git a/NTFSSecurity/PathCmdlets/TestPath2.cs b/NTFSSecurity/PathCmdlets/TestPath2.cs index 220f7da..8bb4dfd 100644 --- a/NTFSSecurity/PathCmdlets/TestPath2.cs +++ b/NTFSSecurity/PathCmdlets/TestPath2.cs @@ -5,7 +5,7 @@ using System.Management.Automation; namespace NTFSSecurity { [Cmdlet(VerbsDiagnostic.Test, "Path2")] - [OutputType(typeof(FileInfo), typeof(DirectoryInfo))] + [OutputType(typeof(bool))] public class TestPath2 : BaseCmdlet { private TestPathType pathType = TestPathType.Any; diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index 9425ccf..d9e5d0f 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -2163,10 +2163,18 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - System.Object + Alphaleonis.Win32.Filesystem.FileInfo + + + By default this cmdlet returns nothing. With `-PassThru $true` it returns a file object for each file that it copied. + + + + + Alphaleonis.Win32.Filesystem.DirectoryInfo - By default this cmdlet returns nothing. With `-PassThru $true` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it copied. + With `-PassThru $true` the cmdlet returns a folder object for each folder that it copied. @@ -2400,7 +2408,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - System.Object + Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. @@ -2645,7 +2653,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - System.Object + Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. @@ -2785,7 +2793,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd ProcessPrivileges.PrivilegeAndAttributes - With `-PassThru`, the cmdlet writes the privilege collection of the current process. The pipeline enumerates it into one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. + With `-PassThru`, the cmdlet writes one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege of the current process, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. Before 5.0.0, it wrote the privileges as one collection. @@ -3017,7 +3025,7 @@ PS C:\> Get-Privileges | Where-Object { $_.Privilege -in 'Backup', 'Restore', - System.Object + Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. @@ -3262,7 +3270,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - System.Object + Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. @@ -3403,7 +3411,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd ProcessPrivileges.PrivilegeAndAttributes - With `-PassThru`, the cmdlet writes the privilege collection of the current process. The pipeline enumerates it into one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. + With `-PassThru`, the cmdlet writes one `ProcessPrivileges.PrivilegeAndAttributes` object per privilege of the current process, each with a `Privilege`, a `PrivilegeAttributes`, and a `PrivilegeState` property. Without `-PassThru`, the cmdlet writes nothing. Before 5.0.0, it wrote the privileges as one collection. @@ -4140,10 +4148,10 @@ PS C:\> Disable-Privileges - Security2.FileSystemAccessRule2 + Alphaleonis.Win32.Filesystem.FileInfo - The cmdlet does not return access rules. For every hashed file it writes the file object of that file, decorated with the type name `Alphaleonis.Win32.Filesystem.FileInfo+Hash` and extended with the `Hash` and `Algorithm` note properties, so all regular file properties such as `FullName`, `Name`, and `Length` remain available. + For every hashed file, the cmdlet writes the file object of that file, decorated with the type name `Alphaleonis.Win32.Filesystem.FileInfo+Hash` and extended with the `Hash` and `Algorithm` note properties, so all regular file properties such as `FullName`, `Name`, and `Length` remain available. @@ -6938,10 +6946,18 @@ PS C:\Data> Get-NTFSSecurityDescriptor - System.Object + Alphaleonis.Win32.Filesystem.FileInfo - By default this cmdlet returns nothing. With `-PassThru $true` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it moved, pointing at the new location. + By default this cmdlet returns nothing. With `-PassThru $true` it returns a file object for each file that it moved, pointing at the new location. + + + + + Alphaleonis.Win32.Filesystem.DirectoryInfo + + + With `-PassThru $true` the cmdlet returns a folder object for each folder that it moved, pointing at the new location. @@ -7300,7 +7316,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Alphaleonis.Win32.Filesystem.FileInfo - With `-PassThru`, the cmdlet writes a file object for the new link. The cmdlet writes that object type for a link to a folder as well. Without `-PassThru`, the cmdlet writes nothing. + With `-PassThru`, the cmdlet writes a file object for a new link to a file. Without `-PassThru`, the cmdlet writes nothing. @@ -7308,7 +7324,7 @@ PS C:\Data> Get-NTFSSecurityDescriptor Alphaleonis.Win32.Filesystem.DirectoryInfo - The cmdlet does not write folder objects. A directory symbolic link is also returned as a file object. + With `-PassThru`, the cmdlet writes a folder object for a new link to a folder. Before 5.0.0, it wrote a file object for those links as well. @@ -7545,10 +7561,18 @@ PS C:\Data> Get-NTFSSecurityDescriptor - System.Object + Alphaleonis.Win32.Filesystem.FileInfo - By default this cmdlet returns nothing. With `-PassThru` it returns an `Alphaleonis.Win32.Filesystem.FileInfo` or `Alphaleonis.Win32.Filesystem.DirectoryInfo` object for each item that it deleted. + By default this cmdlet returns nothing. With `-PassThru` it returns a file object for each file that it deleted. + + + + + Alphaleonis.Win32.Filesystem.DirectoryInfo + + + With `-PassThru` the cmdlet returns a folder object for each folder that it deleted. @@ -9508,7 +9532,7 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - System.Object + Security2.FileSystemInheritanceInfo By default this cmdlet returns no output. With `-PassThru` it writes one `Security2.FileSystemInheritanceInfo` object per item, which reports the `AccessInheritanceEnabled` and `AuditInheritanceEnabled` state after the change. @@ -10085,18 +10109,10 @@ PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd - Alphaleonis.Win32.Filesystem.FileInfo - - - `Test-Path2` does not write file objects. For each path it writes a single `System.Boolean` value that is `$true` when the item exists and matches `-PathType`, and `$false` otherwise. - - - - - Alphaleonis.Win32.Filesystem.DirectoryInfo + System.Boolean - `Test-Path2` does not write folder objects either. A folder is reported through the same `System.Boolean` result as a file. + For each path, the cmdlet writes `$true` when the item exists and matches `-PathType`, and `$false` otherwise. diff --git a/Tests/OutputTypes.Tests.ps1 b/Tests/OutputTypes.Tests.ps1 new file mode 100644 index 0000000..643f668 --- /dev/null +++ b/Tests/OutputTypes.Tests.ps1 @@ -0,0 +1,87 @@ +<# + Tests the output types of the cmdlets of the module built in NTFSSecurity\bin\Release: the [OutputType] that + Get-Command reports, and the objects that -PassThru writes. Tests that need a privilege skip without it and run + in CI, whose runners are elevated. +#> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.' +)] +param () + +BeforeDiscovery { + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force + $holdsBackupPrivilege = Test-PrivilegeHeld -Name 'SeBackupPrivilege' + $canCreateSymbolicLinks = Test-PrivilegeHeld -Name 'SeCreateSymbolicLinkPrivilege' + + $itemTypes = @('Alphaleonis.Win32.Filesystem.FileInfo', 'Alphaleonis.Win32.Filesystem.DirectoryInfo') + $declaredTypes = @( + @{ Name = 'Test-Path2'; Types = @('System.Boolean') } + @{ Name = 'Get-FileHash2'; Types = @('Alphaleonis.Win32.Filesystem.FileInfo') } + @{ Name = 'Add-NTFSAudit'; Types = @('Security2.FileSystemAuditRule2') } + @{ Name = 'Remove-NTFSAudit'; Types = @('Security2.FileSystemAuditRule2') } + @{ Name = 'Copy-Item2'; Types = $itemTypes } + @{ Name = 'Move-Item2'; Types = $itemTypes } + @{ Name = 'Remove-Item2'; Types = $itemTypes } + @{ Name = 'Enable-NTFSAccessInheritance'; Types = @('Security2.FileSystemInheritanceInfo') } + @{ Name = 'Disable-NTFSAccessInheritance'; Types = @('Security2.FileSystemInheritanceInfo') } + @{ Name = 'Enable-NTFSAuditInheritance'; Types = @('Security2.FileSystemInheritanceInfo') } + @{ Name = 'Disable-NTFSAuditInheritance'; Types = @('Security2.FileSystemInheritanceInfo') } + @{ Name = 'Set-NTFSInheritance'; Types = @('Security2.FileSystemInheritanceInfo') } + ) +} + +BeforeAll { + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force + $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release\NTFSSecurity.psd1' + Import-Module -Name $modulePath -Force -ErrorAction Stop + $sandbox = New-TestSandbox -Name 'OutputTypes' + Push-Location -LiteralPath $sandbox +} + +AfterAll { + Pop-Location + Remove-TestSandbox -Sandbox $sandbox + Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue +} + +Describe 'Declared output types' { + It ' should declare the type of the objects it writes' -ForEach $declaredTypes { + @((Get-Command -Name $Name).OutputType.Name) | Should -Be $Types + } +} + +Describe 'Privilege cmdlets with -PassThru' { + AfterEach { + Disable-Privileges -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + } + + # Before 5.0.0, -PassThru wrote the privileges as one collection. + It 'Enable-Privileges should write one object per privilege' { + $result = @(Enable-Privileges -PassThru -ErrorAction SilentlyContinue) + + $result.Count | Should -BeGreaterThan 1 + $result | ForEach-Object -Process { $_ | Should -BeOfType [ProcessPrivileges.PrivilegeAndAttributes] } + } + + It 'Disable-Privileges should write one object per privilege' -Skip:(-not $holdsBackupPrivilege) { + Enable-Privileges -ErrorAction SilentlyContinue + + $result = @(Disable-Privileges -PassThru -WarningAction SilentlyContinue) + + $result.Count | Should -BeGreaterThan 1 + $result | ForEach-Object -Process { $_ | Should -BeOfType [ProcessPrivileges.PrivilegeAndAttributes] } + } +} + +Describe 'New-NTFSSymbolicLink with -PassThru' { + # Before 5.0.0, the cmdlet returned a file object for a link to a folder as well. + It 'Should return a folder object for a link to a folder' -Skip:(-not $canCreateSymbolicLinks) { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'Target' -Directory + $link = Join-Path -Path $sandbox -ChildPath 'FolderLink' + Assert-TestSandboxPath -Sandbox $sandbox -Path $link + + $result = New-NTFSSymbolicLink -Path $link -Target $folder -PassThru + + $result | Should -BeOfType [Alphaleonis.Win32.Filesystem.DirectoryInfo] + } +}