Browse Source

docs(access): say what a blank, null, or script block value of a property does, and pin it

R1-1 (major): the four pages claimed that a blank cell of AppliesTo, AccessType or
AuditFlags, InheritanceFlags, or PropagationFlags is an InvalidParameterValue error
for every shape. That is wrong when the blank, $null, or DBNull value belongs to
the group that -AppliesTo did not select: PowerShell counts a property that is
present, even blank, as offered for choosing the parameter set, the same as a
real value, so AppliesTo together with a blank InheritanceFlags or
PropagationFlags (for example a short CSV row, whose missing trailing columns
Import-Csv fills with $null) is AmbiguousParameterSet, and rc8 created the
entry. The pages now say that a CSV file should have the columns of only one
group, that mixing both groups fails with AmbiguousParameterSet, that a literal
$null named or piped for -AppliesTo itself fails with its own, unrelated,
pre-existing error because -AppliesTo is mandatory, and keep the sentence that a
bad value within the chosen group is InvalidParameterValue.

R1-3 (minor): the pages now also say that a script block is a plain value for
these parameters and is not evaluated once for each piped object the way rc8
evaluated it, so a named script block is a terminating InvalidParameterValue
error.

Neither finding needed a code change; both already read this way. Pinned with
new table-driven tests, over the four cmdlets and their Path and SD parameter
sets, for AppliesTo valid combined with a $null, an empty string, or DBNull
InheritanceFlags or PropagationFlags and the reverse, and for a named script
block of each of the five parameters: the error id the binder already gives,
nothing changed, and, where applicable, the next piped object still processed.
Confirmed unchanged against the pre-R1-2/R1-4 build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/131/head
Raimund Andree 2 weeks ago
parent
commit
140c790c11
  1. 2
      Docs/Cmdlets/Add-NTFSAccess.md
  2. 2
      Docs/Cmdlets/Add-NTFSAudit.md
  3. 2
      Docs/Cmdlets/Remove-NTFSAccess.md
  4. 2
      Docs/Cmdlets/Remove-NTFSAudit.md
  5. 98
      Tests/ParameterSets.Access.Tests.ps1
  6. 112
      Tests/ParameterSets.Audit.Tests.ps1

2
Docs/Cmdlets/Add-NTFSAccess.md

@ -46,7 +46,7 @@ Adds an access control entry (ACE) to the discretionary access control list (DAC
`-AccessRights` accepts the basic rights such as `Read`, `Modify`, and `FullControl` as well as the granular rights such as `CreateFiles` or `WriteAttributes`, and several values can be combined, for example `-AccessRights ReadData, WriteData, Delete`. For the mapping between the values of this module, the rights that Windows displays, and the entries of the advanced security dialog, see [Concepts](../Concepts.md). `-AccessRights` accepts the basic rights such as `Read`, `Modify`, and `FullControl` as well as the granular rights such as `CreateFiles` or `WriteAttributes`, and several values can be combined, for example `-AccessRights ReadData, WriteData, Delete`. For the mapping between the values of this module, the rights that Windows displays, and the entries of the advanced security dialog, see [Concepts](../Concepts.md).
The cmdlet has four parameter sets. The `Path` sets read the item from disk and write the changed DACL back immediately, while the `SD` sets change a `Security2.FileSystemSecurity2` object returned by `Get-NTFSSecurityDescriptor` in memory until `Set-NTFSSecurityDescriptor` writes it back. The `Simple` sets take `-AppliesTo`, the `Complex` sets take `-InheritanceFlags` and `-PropagationFlags`; both describe the same ACE flags. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both kinds, an object that has both `Path` and `SecurityDescriptor`, or one whose `AppliesTo` is `$null`, fails. `Import-Csv` gives `$null` for a column that a row lacks because it has fewer fields than the header. An `AppliesTo`, `AccessType`, `InheritanceFlags`, or `PropagationFlags` that PowerShell can't convert to its type, such as the blank cell of a CSV file or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets. The cmdlet has four parameter sets. The `Path` sets read the item from disk and write the changed DACL back immediately, while the `SD` sets change a `Security2.FileSystemSecurity2` object returned by `Get-NTFSSecurityDescriptor` in memory until `Set-NTFSSecurityDescriptor` writes it back. The `Simple` sets take `-AppliesTo`, the `Complex` sets take `-InheritanceFlags` and `-PropagationFlags`; both describe the same ACE flags. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both `Path` and `SecurityDescriptor` fails. A `$null`, an empty string, or `DBNull` value of a property counts as present for this choice, the same as a real value, so a CSV file, or any other source of piped objects, should have the columns of only one group: `AppliesTo`, or `InheritanceFlags` and `PropagationFlags`. A row or object that has properties of both groups, even if one of them is `$null` (for example a short CSV row, whose missing trailing columns `Import-Csv` fills with `$null`), an empty string, or `DBNull`, is an `AmbiguousParameterSet` error: terminating when named directly on the command line, and reported for just that object, unchanged, when it comes from the pipeline. A literal `$null`, named or piped, for `-AppliesTo` itself fails the same way but with its own, unrelated error, because `-AppliesTo` is mandatory. Within the group that PowerShell did choose, an `AppliesTo`, `AccessType`, `InheritanceFlags`, or `PropagationFlags` value that PowerShell can't convert to its type, such as a blank cell or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. A script block is a plain value for these parameters, not evaluated once for each piped object the way it would be for a strongly typed parameter, so a named script block, for example `-AppliesTo { 'FilesOnly' }`, fails with a terminating `InvalidParameterValue` error, the same as any other named value of the wrong type. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets.
When `-AccessType`, `-AppliesTo`, `-InheritanceFlags`, and `-PropagationFlags` are omitted, the cmdlet adds an `Allow` ACE that applies to this folder, subfolders, and files, which corresponds to the inheritance flags `ContainerInherit, ObjectInherit` and no propagation flags. An `Allow` ACE always receives the `Synchronize` right in addition to the requested rights, inheritance and propagation flags are ignored on files, and rights for an account that already has an ACE with the same access type and the same flags are merged into that ACE. The cmdlet writes no output unless `-PassThru` is used, and a failure on one item is reported as a non-terminating error while the remaining items are processed. When `-AccessType`, `-AppliesTo`, `-InheritanceFlags`, and `-PropagationFlags` are omitted, the cmdlet adds an `Allow` ACE that applies to this folder, subfolders, and files, which corresponds to the inheritance flags `ContainerInherit, ObjectInherit` and no propagation flags. An `Allow` ACE always receives the `Synchronize` right in addition to the requested rights, inheritance and propagation flags are ignored on files, and rights for an account that already has an ACE with the same access type and the same flags are merged into that ACE. The cmdlet writes no output unless `-PassThru` is used, and a failure on one item is reported as a non-terminating error while the remaining items are processed.

2
Docs/Cmdlets/Add-NTFSAudit.md

@ -46,7 +46,7 @@ The `Add-NTFSAudit` cmdlet adds an audit entry to the system access control list
In the `PathSimple` and `PathComplex` parameter sets the cmdlet reads the security descriptor of every item in `-Path`, adds the entry, and writes the descriptor back right away. In the `SDSimple` and `SDComplex` parameter sets it adds the entry to an in-memory `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned; that change only reaches the file system when you pass the object to `Set-NTFSSecurityDescriptor`. The simple sets describe the scope of the entry with the single `-AppliesTo` parameter, the complex sets with `-InheritanceFlags` and `-PropagationFlags`. In the `PathSimple` and `PathComplex` parameter sets the cmdlet reads the security descriptor of every item in `-Path`, adds the entry, and writes the descriptor back right away. In the `SDSimple` and `SDComplex` parameter sets it adds the entry to an in-memory `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned; that change only reaches the file system when you pass the object to `Set-NTFSSecurityDescriptor`. The simple sets describe the scope of the entry with the single `-AppliesTo` parameter, the complex sets with `-InheritanceFlags` and `-PropagationFlags`.
A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both kinds, an object that has both `Path` and `SecurityDescriptor`, or one whose `AppliesTo` is `$null`, fails. `Import-Csv` gives `$null` for a column that a row lacks because it has fewer fields than the header. An `AppliesTo`, `AuditFlags`, `InheritanceFlags`, or `PropagationFlags` that PowerShell can't convert to its type, such as the blank cell of a CSV file or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both `Path` and `SecurityDescriptor` fails. A `$null`, an empty string, or `DBNull` value of a property counts as present for this choice, the same as a real value, so a CSV file, or any other source of piped objects, should have the columns of only one group: `AppliesTo`, or `InheritanceFlags` and `PropagationFlags`. A row or object that has properties of both groups, even if one of them is `$null` (for example a short CSV row, whose missing trailing columns `Import-Csv` fills with `$null`), an empty string, or `DBNull`, is an `AmbiguousParameterSet` error: terminating when named directly on the command line, and reported for just that object, unchanged, when it comes from the pipeline. A literal `$null`, named or piped, for `-AppliesTo` itself fails the same way but with its own, unrelated error, because `-AppliesTo` is mandatory. Within the group that PowerShell did choose, an `AppliesTo`, `AuditFlags`, `InheritanceFlags`, or `PropagationFlags` value that PowerShell can't convert to its type, such as a blank cell or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. A script block is a plain value for these parameters, not evaluated once for each piped object the way it would be for a strongly typed parameter, so a named script block, for example `-AppliesTo { 'FilesOnly' }`, fails with a terminating `InvalidParameterValue` error, the same as any other named value of the wrong type. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets.
When you omit them, `-AuditFlags` is `Success, Failure`, `-InheritanceFlags` is `ContainerInherit, ObjectInherit`, `-PropagationFlags` is `None`, and `-AppliesTo` is `ThisFolderSubfoldersAndFiles`, so both the simple and the complex set audit the item, its subfolders, and its files by default. Inheritance applies to folders only: when the item is a file, the cmdlet stores the entry without inheritance and propagation flags. When you omit them, `-AuditFlags` is `Success, Failure`, `-InheritanceFlags` is `ContainerInherit, ObjectInherit`, `-PropagationFlags` is `None`, and `-AppliesTo` is `ThisFolderSubfoldersAndFiles`, so both the simple and the complex set audit the item, its subfolders, and its files by default. Inheritance applies to folders only: when the item is a file, the cmdlet stores the entry without inheritance and propagation flags.

2
Docs/Cmdlets/Remove-NTFSAccess.md

@ -48,7 +48,7 @@ Only the specified rights are taken away: when an entry grants more than `-Acces
Inherited entries cannot be removed from the item that inherits them. Remove them from the folder named in the `InheritedFrom` property, or run `Disable-NTFSAccessInheritance` on the item first, which copies the inherited entries into it as explicit ones that this cmdlet can then remove. Inherited entries cannot be removed from the item that inherits them. Remove them from the folder named in the `InheritedFrom` property, or run `Disable-NTFSAccessInheritance` on the item first, which copies the inherited entries into it as explicit ones that this cmdlet can then remove.
The cmdlet has four parameter sets. The `Path` sets read the item from disk and write the changed DACL back immediately, while the `SD` sets change a `Security2.FileSystemSecurity2` object returned by `Get-NTFSSecurityDescriptor` in memory until `Set-NTFSSecurityDescriptor` writes it back. The `Simple` sets take `-AppliesTo`, the `Complex` sets take `-InheritanceFlags` and `-PropagationFlags`. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both kinds, an object that has both `Path` and `SecurityDescriptor`, or one whose `AppliesTo` is `$null`, fails. `Import-Csv` gives `$null` for a column that a row lacks because it has fewer fields than the header. An `AppliesTo`, `AccessType`, `InheritanceFlags`, or `PropagationFlags` that PowerShell can't convert to its type, such as the blank cell of a CSV file or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets. All relevant parameters bind by property name, so the output of `Get-NTFSAccess` and `Get-NTFSOrphanedAccess` can be piped directly into this cmdlet. The cmdlet writes no output unless `-PassThru` is used. The cmdlet has four parameter sets. The `Path` sets read the item from disk and write the changed DACL back immediately, while the `SD` sets change a `Security2.FileSystemSecurity2` object returned by `Get-NTFSSecurityDescriptor` in memory until `Set-NTFSSecurityDescriptor` writes it back. The `Simple` sets take `-AppliesTo`, the `Complex` sets take `-InheritanceFlags` and `-PropagationFlags`. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both `Path` and `SecurityDescriptor` fails. A `$null`, an empty string, or `DBNull` value of a property counts as present for this choice, the same as a real value, so a CSV file, or any other source of piped objects, should have the columns of only one group: `AppliesTo`, or `InheritanceFlags` and `PropagationFlags`. A row or object that has properties of both groups, even if one of them is `$null` (for example a short CSV row, whose missing trailing columns `Import-Csv` fills with `$null`), an empty string, or `DBNull`, is an `AmbiguousParameterSet` error: terminating when named directly on the command line, and reported for just that object, unchanged, when it comes from the pipeline. A literal `$null`, named or piped, for `-AppliesTo` itself fails the same way but with its own, unrelated error, because `-AppliesTo` is mandatory. Within the group that PowerShell did choose, an `AppliesTo`, `AccessType`, `InheritanceFlags`, or `PropagationFlags` value that PowerShell can't convert to its type, such as a blank cell or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. A script block is a plain value for these parameters, not evaluated once for each piped object the way it would be for a strongly typed parameter, so a named script block, for example `-AppliesTo { 'FilesOnly' }`, fails with a terminating `InvalidParameterValue` error, the same as any other named value of the wrong type. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets. All relevant parameters bind by property name, so the output of `Get-NTFSAccess` and `Get-NTFSOrphanedAccess` can be piped directly into this cmdlet. The cmdlet writes no output unless `-PassThru` is used.
## EXAMPLES ## EXAMPLES

2
Docs/Cmdlets/Remove-NTFSAudit.md

@ -46,7 +46,7 @@ The `Remove-NTFSAudit` cmdlet removes an audit entry from the system access cont
Because the inheritance and propagation flags take part in the match, they must describe the entry you want to remove. `-AppliesTo ThisFolderOnly` removes an entry that is not inherited by child items, which is also the shape of every audit entry on a file, while the default of the complex parameter sets removes an entry that applies to the folder, its subfolders, and its files. An entry that an item inherits from a parent folder is stored on that parent, so remove it there, or use `Clear-NTFSAudit` with `-DisableInheritance` to drop the inherited entries on the item. Because the inheritance and propagation flags take part in the match, they must describe the entry you want to remove. `-AppliesTo ThisFolderOnly` removes an entry that is not inherited by child items, which is also the shape of every audit entry on a file, while the default of the complex parameter sets removes an entry that applies to the folder, its subfolders, and its files. An entry that an item inherits from a parent folder is stored on that parent, so remove it there, or use `Clear-NTFSAudit` with `-DisableInheritance` to drop the inherited entries on the item.
In the `PathSimple` and `PathComplex` parameter sets the cmdlet reads the security descriptor of every item in `-Path` and writes it back right away. In the `SDSimple` and `SDComplex` parameter sets it changes an in-memory `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned, and the change reaches the file system only when you pass the object to `Set-NTFSSecurityDescriptor`. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both kinds, an object that has both `Path` and `SecurityDescriptor`, or one whose `AppliesTo` is `$null`, fails. `Import-Csv` gives `$null` for a column that a row lacks because it has fewer fields than the header. An `AppliesTo`, `AuditFlags`, `InheritanceFlags`, or `PropagationFlags` that PowerShell can't convert to its type, such as the blank cell of a CSV file or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets. In the `PathSimple` and `PathComplex` parameter sets the cmdlet reads the security descriptor of every item in `-Path` and writes it back right away. In the `SDSimple` and `SDComplex` parameter sets it changes an in-memory `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned, and the change reaches the file system only when you pass the object to `Set-NTFSSecurityDescriptor`. A command without `-AppliesTo` uses a `Complex` set, also when it works on a security descriptor, and a command that has `-AppliesTo` together with `-InheritanceFlags` or `-PropagationFlags` fails, because PowerShell can't choose between the sets. The same holds for an object that you pipe in: its property `AppliesTo` selects a `Simple` set, its properties `InheritanceFlags` and `PropagationFlags` select a `Complex` set, and an object that has both `Path` and `SecurityDescriptor` fails. A `$null`, an empty string, or `DBNull` value of a property counts as present for this choice, the same as a real value, so a CSV file, or any other source of piped objects, should have the columns of only one group: `AppliesTo`, or `InheritanceFlags` and `PropagationFlags`. A row or object that has properties of both groups, even if one of them is `$null` (for example a short CSV row, whose missing trailing columns `Import-Csv` fills with `$null`), an empty string, or `DBNull`, is an `AmbiguousParameterSet` error: terminating when named directly on the command line, and reported for just that object, unchanged, when it comes from the pipeline. A literal `$null`, named or piped, for `-AppliesTo` itself fails the same way but with its own, unrelated error, because `-AppliesTo` is mandatory. Within the group that PowerShell did choose, an `AppliesTo`, `AuditFlags`, `InheritanceFlags`, or `PropagationFlags` value that PowerShell can't convert to its type, such as a blank cell or a misspelled name, is an `InvalidParameterValue` error for that object: the object is left unchanged, and the next one in the pipeline is still processed; the same bad value named on the command line fails the whole command instead. A script block is a plain value for these parameters, not evaluated once for each piped object the way it would be for a strongly typed parameter, so a named script block, for example `-AppliesTo { 'FilesOnly' }`, fails with a terminating `InvalidParameterValue` error, the same as any other named value of the wrong type. Before 5.0.0, such a value was silently ignored, and the object was handled as one without it. Before 5.0.0, a command that used `-SecurityDescriptor` without `-AppliesTo`, `-InheritanceFlags`, or `-PropagationFlags` failed, because PowerShell couldn't choose between the two `SD` sets.
When you omit them, `-AuditFlags` is `Success, Failure`, `-InheritanceFlags` is `ContainerInherit, ObjectInherit`, `-PropagationFlags` is `None`. All parameters bind by property name, and `-Path` also binds by value and through its `FullName` alias, so you can pipe the output of `Get-NTFSAudit`, `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` into the cmdlet. The cmdlet writes no object unless you use `-PassThru`. When you omit them, `-AuditFlags` is `Success, Failure`, `-InheritanceFlags` is `ContainerInherit, ObjectInherit`, `-PropagationFlags` is `None`. All parameters bind by property name, and `-Path` also binds by value and through its `FullName` alias, so you can pipe the output of `Get-NTFSAudit`, `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` into the cmdlet. The cmdlet writes no object unless you use `-PassThru`.

98
Tests/ParameterSets.Access.Tests.ps1

@ -1431,6 +1431,44 @@ Describe 'Invalid and valid values of AccessType, InheritanceFlags, PropagationF
@{ Parameter = 'PropagationFlags'; Word = 'Inh'; ExpectedName = 'InheritOnly' } @{ Parameter = 'PropagationFlags'; Word = 'Inh'; ExpectedName = 'InheritOnly' }
@{ Parameter = 'AppliesTo'; Word = 'ThisFolderO'; ExpectedName = 'ThisFolderOnly' } @{ Parameter = 'AppliesTo'; Word = 'ThisFolderO'; ExpectedName = 'ThisFolderOnly' }
) )
# R1-1: because the five parameters are object, PowerShell counts a $null, an empty string, or DBNull value
# of a property as present when it chooses the parameter set, the same as a real value, so AppliesTo (the
# Simple group) together with a $null/''/DBNull InheritanceFlags or PropagationFlags (the Complex group)
# offers both groups and is AmbiguousParameterSet -- not InvalidParameterValue, which is what the docs
# wrongly claimed for every shape. The reverse (AppliesTo blank, a Complex property valid) is
# AmbiguousParameterSet too, except that AppliesTo is Mandatory, so a literal $null of it (unlike '' or
# DBNull) is rejected earlier by the binder's own Mandatory check, unrelated to and unchanged by this
# finding. Covers the four cmdlets' Path and SD sets; the Audit half of the four cmdlets is pinned in
# ParameterSets.Audit.Tests.ps1.
$accessAmbiguousOtherGroupCases = foreach ($cmdlet in 'Add-NTFSAccess', 'Remove-NTFSAccess') {
foreach ($targetKind in 'Path', 'SD') {
foreach ($shape in 'a $null', 'an empty string', 'DBNull') {
$blankValue = switch ($shape) { 'a $null' { $null }; 'an empty string' { '' }; 'DBNull' { [System.DBNull]::Value } }
@{
Cmdlet = $cmdlet; TargetKind = $targetKind; Shape = $shape; BlankValue = $blankValue
Direction = 'AppliesTo valid, InheritanceFlags blank'; AppliesToIsBlank = $false
ExpectedErrorId = 'AmbiguousParameterSet'
}
@{
Cmdlet = $cmdlet; TargetKind = $targetKind; Shape = $shape; BlankValue = $blankValue
Direction = 'AppliesTo blank, PropagationFlags valid'; AppliesToIsBlank = $true
ExpectedErrorId = $(if ($shape -eq 'a $null') { 'ParameterArgumentValidationErrorNullNotAllowed' } else { 'AmbiguousParameterSet' })
}
}
}
}
# R1-3: with the object type, a script block is a plain value (rc8's delay-bind evaluation, once per piped
# object, no longer applies), so it fails the same way any other value of the wrong shape does: a
# terminating InvalidParameterValue from ValidateNamedValue in BeginProcessing, before any piped object is
# processed.
$accessScriptBlockCases = foreach ($cmdlet in 'Add-NTFSAccess', 'Remove-NTFSAccess') {
@{ Cmdlet = $cmdlet; Parameter = 'AccessType' }
@{ Cmdlet = $cmdlet; Parameter = 'InheritanceFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'PropagationFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'AppliesTo' }
}
} }
Context 'A bad value bound from the pipeline' { Context 'A bad value bound from the pipeline' {
@ -1751,6 +1789,66 @@ Describe 'Invalid and valid values of AccessType, InheritanceFlags, PropagationF
} }
} }
# R1-1: rc8 created the entry for every shape below; the object-typed parameters make a $null, an empty
# string, or DBNull value of a property of the other group count as present too, so PowerShell cannot resolve
# the parameter set and nothing is changed.
Context 'A blank value of the other groups property' {
It 'Should reject <Cmdlet> (<TargetKind>) for <Direction> (<Shape>) with the error the binder gives, change nothing, and still process the next object' -ForEach $accessAmbiguousOtherGroupCases {
$bad = New-TestSandboxItem -Sandbox $sandbox -Name 'AmbiguousOtherGroupBad' -Directory
$next = New-TestSandboxItem -Sandbox $sandbox -Name 'AmbiguousOtherGroupNext' -Directory
$before = (Get-Acl -LiteralPath $bad).Sddl
$badTarget = if ($TargetKind -eq 'Path') { @{ Path = $bad } } else { @{ SecurityDescriptor = (Get-NTFSSecurityDescriptor -Path $bad -ErrorAction Stop) } }
$nextTarget = if ($TargetKind -eq 'Path') { @{ Path = $next } } else { @{ SecurityDescriptor = (Get-NTFSSecurityDescriptor -Path $next -ErrorAction Stop) } }
$badExtra = @{ Account = 'S-1-1-0'; AccessRights = 'ReadData' }
if ($AppliesToIsBlank) {
$badExtra['AppliesTo'] = $BlankValue
$badExtra['PropagationFlags'] = 'None'
}
else {
$badExtra['AppliesTo'] = 'ThisFolderOnly'
$badExtra['InheritanceFlags'] = $BlankValue
}
$rows = @(
[pscustomobject] ($badTarget + $badExtra)
[pscustomobject] ($nextTarget + @{ Account = 'S-1-1-0'; AccessRights = 'ReadData' })
)
$result = @($rows | & $Cmdlet -PassThru -ErrorVariable pipelineErrors -ErrorAction SilentlyContinue)
$pipelineErrors | Should -HaveCount 1
$pipelineErrors[0].FullyQualifiedErrorId | Should -BeLike "$ExpectedErrorId,*"
(Get-Acl -LiteralPath $bad).Sddl | Should -BeExactly $before
$result | Should -Not -BeNullOrEmpty
$result | ForEach-Object -Process { $_.FullName | Should -Be $next }
}
}
# R1-3: with the object type, a script block is a plain value (rc8's delay-bind evaluation, once per piped
# object, no longer applies), so ValidateNamedValue in BeginProcessing rejects it the same as any other bad
# value, before any piped object is processed.
Context 'A named script block is a terminating error' {
It 'Should throw a terminating InvalidParameterValue error for a named script block value of <Cmdlet> -<Parameter>, and change nothing' -ForEach $accessScriptBlockCases {
$folder = New-TestSandboxItem -Sandbox $sandbox -Name 'NamedScriptBlock' -Directory
$before = (Get-Acl -LiteralPath $folder).Sddl
$namedArguments = @{ Path = $folder; Account = 'S-1-1-0'; AccessRights = 'ReadData' }
$namedArguments[$Parameter] = { 'Deny' }
$caughtId = $null
try {
& $Cmdlet @namedArguments -ErrorAction Stop
}
catch {
$caughtId = $_.FullyQualifiedErrorId
}
$caughtId | Should -BeLike 'InvalidParameterValue,*'
(Get-Acl -LiteralPath $folder).Sddl | Should -BeExactly $before
}
}
Context 'Tab completion and the parameter type' { Context 'Tab completion and the parameter type' {
It '<Cmdlet> -<Parameter> should complete exactly the names of its enumeration' -ForEach $accessCompleterCases { It '<Cmdlet> -<Parameter> should complete exactly the names of its enumeration' -ForEach $accessCompleterCases {
$inputScript = '{0} -{1} ' -f $Cmdlet, $Parameter $inputScript = '{0} -{1} ' -f $Cmdlet, $Parameter

112
Tests/ParameterSets.Audit.Tests.ps1

@ -1099,6 +1099,44 @@ Describe 'Invalid and valid values of AuditFlags, InheritanceFlags, PropagationF
$auditCompletionFormCases = @( $auditCompletionFormCases = @(
@{ Parameter = 'AuditFlags'; Word = 'F'; ExpectedName = 'Failure' } @{ Parameter = 'AuditFlags'; Word = 'F'; ExpectedName = 'Failure' }
) )
# R1-1: because the five parameters are object, PowerShell counts a $null, an empty string, or DBNull value
# of a property as present when it chooses the parameter set, the same as a real value, so AppliesTo (the
# Simple group) together with a $null/''/DBNull InheritanceFlags or PropagationFlags (the Complex group)
# offers both groups and is AmbiguousParameterSet -- not InvalidParameterValue, which is what the docs
# wrongly claimed for every shape. The reverse (AppliesTo blank, a Complex property valid) is
# AmbiguousParameterSet too, except that AppliesTo is Mandatory, so a literal $null of it (unlike '' or
# DBNull) is rejected earlier by the binder's own Mandatory check, unrelated to and unchanged by this
# finding. Covers the four cmdlets' Path and SD sets; the Access half is pinned in
# ParameterSets.Access.Tests.ps1.
$auditAmbiguousOtherGroupCases = foreach ($cmdlet in 'Add-NTFSAudit', 'Remove-NTFSAudit') {
foreach ($targetKind in 'Path', 'SD') {
foreach ($shape in 'a $null', 'an empty string', 'DBNull') {
$blankValue = switch ($shape) { 'a $null' { $null }; 'an empty string' { '' }; 'DBNull' { [System.DBNull]::Value } }
@{
Cmdlet = $cmdlet; TargetKind = $targetKind; Shape = $shape; BlankValue = $blankValue
Direction = 'AppliesTo valid, InheritanceFlags blank'; AppliesToIsBlank = $false
ExpectedErrorId = 'AmbiguousParameterSet'
}
@{
Cmdlet = $cmdlet; TargetKind = $targetKind; Shape = $shape; BlankValue = $blankValue
Direction = 'AppliesTo blank, PropagationFlags valid'; AppliesToIsBlank = $true
ExpectedErrorId = $(if ($shape -eq 'a $null') { 'ParameterArgumentValidationErrorNullNotAllowed' } else { 'AmbiguousParameterSet' })
}
}
}
}
# R1-3: with the object type, a script block is a plain value (rc8's delay-bind evaluation, once per piped
# object, no longer applies), so it fails the same way any other value of the wrong shape does: a
# terminating InvalidParameterValue from ValidateNamedValue in BeginProcessing, before any piped object is
# processed.
$auditScriptBlockCases = foreach ($cmdlet in 'Add-NTFSAudit', 'Remove-NTFSAudit') {
@{ Cmdlet = $cmdlet; Parameter = 'AuditFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'InheritanceFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'PropagationFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'AppliesTo' }
}
} }
Context 'A bad value bound from the pipeline' { Context 'A bad value bound from the pipeline' {
@ -1407,6 +1445,80 @@ Describe 'Invalid and valid values of AuditFlags, InheritanceFlags, PropagationF
} }
} }
# R1-1: rc8 created the entry for every shape below; the object-typed parameters make a $null, an empty
# string, or DBNull value of a property of the other group count as present too, so PowerShell cannot resolve
# the parameter set and nothing is changed. The conversion/parameter-set failure happens before the cmdlet
# ever touches the SACL, so the core assertion (the error id, and that the bad object's audit entries are
# unchanged) runs in every configuration; only the "good next object succeeded" part needs the Security
# privilege, the same way the rest of this file does.
Context 'A blank value of the other groups property' {
It 'Should reject <Cmdlet> (<TargetKind>) for <Direction> (<Shape>) with the error the binder gives, change nothing, and still process the next object' -ForEach $auditAmbiguousOtherGroupCases {
$bad = New-TestSandboxItem -Sandbox $sandbox -Name 'AmbiguousOtherGroupBad' -Directory
$next = New-TestSandboxItem -Sandbox $sandbox -Name 'AmbiguousOtherGroupNext' -Directory
$before = if ($canReadAudit) { Get-TestAuditSddl -Path $bad }
$badTarget = if ($TargetKind -eq 'Path') { @{ FullName = $bad } } else { @{ SecurityDescriptor = (Get-NTFSSecurityDescriptor -Path $bad -ErrorAction Stop) } }
$nextTarget = if ($TargetKind -eq 'Path') { @{ FullName = $next } } else { @{ SecurityDescriptor = (Get-NTFSSecurityDescriptor -Path $next -ErrorAction Stop) } }
$badExtra = @{ Account = 'S-1-1-0'; AccessRights = 'ReadData' }
if ($AppliesToIsBlank) {
$badExtra['AppliesTo'] = $BlankValue
$badExtra['PropagationFlags'] = 'None'
}
else {
$badExtra['AppliesTo'] = 'ThisFolderOnly'
$badExtra['InheritanceFlags'] = $BlankValue
}
$rows = @(
[pscustomobject] ($badTarget + $badExtra)
[pscustomobject] ($nextTarget + @{ Account = 'S-1-1-0'; AccessRights = 'ReadData' })
)
$result = @($rows | & $Cmdlet -PassThru -ErrorVariable pipelineErrors -ErrorAction SilentlyContinue)
$badErrors = @($pipelineErrors | Where-Object -FilterScript {
($TargetKind -eq 'Path' -and $_.TargetObject.FullName -eq $bad) -or
($TargetKind -eq 'SD' -and $_.TargetObject.SecurityDescriptor -eq $badTarget['SecurityDescriptor'])
})
$badErrors | Should -HaveCount 1
$badErrors[0].FullyQualifiedErrorId | Should -BeLike "$ExpectedErrorId,*"
if ($canReadAudit) {
Get-TestAuditSddl -Path $bad | Should -BeExactly $before
$pipelineErrors | Should -HaveCount 1
$result | Should -Not -BeNullOrEmpty
$result | ForEach-Object -Process { $_.FullName | Should -Be $next }
}
}
}
# R1-3: with the object type, a script block is a plain value (rc8's delay-bind evaluation, once per piped
# object, no longer applies), so ValidateNamedValue in BeginProcessing rejects it the same as any other bad
# value, before any piped object is processed and before the cmdlet ever touches the SACL. The terminating
# error itself needs no Security privilege; reading the SACL back to confirm nothing changed does, the same
# way the rest of this file does.
Context 'A named script block is a terminating error' {
It 'Should throw a terminating InvalidParameterValue error for a named script block value of <Cmdlet> -<Parameter>, and change nothing' -ForEach $auditScriptBlockCases {
$folder = New-TestSandboxItem -Sandbox $sandbox -Name 'NamedScriptBlock' -Directory
$before = if ($canReadAudit) { Get-TestAuditSddl -Path $folder }
$namedArguments = @{ Path = $folder; Account = 'S-1-1-0'; AccessRights = 'ReadData' }
$namedArguments[$Parameter] = { 'Deny' }
$caughtId = $null
try {
& $Cmdlet @namedArguments -ErrorAction Stop
}
catch {
$caughtId = $_.FullyQualifiedErrorId
}
$caughtId | Should -BeLike 'InvalidParameterValue,*'
if ($canReadAudit) {
Get-TestAuditSddl -Path $folder | Should -BeExactly $before
}
}
}
Context 'Tab completion and the parameter type' { Context 'Tab completion and the parameter type' {
It '<Cmdlet> -<Parameter> should complete exactly the names of its enumeration' -ForEach $auditCompleterCases { It '<Cmdlet> -<Parameter> should complete exactly the names of its enumeration' -ForEach $auditCompleterCases {
$inputScript = '{0} -{1} ' -f $Cmdlet, $Parameter $inputScript = '{0} -{1} ' -f $Cmdlet, $Parameter

Loading…
Cancel
Save