Browse Source

fix: pass on what a later command raises through the error stream, and disable an interrupted privilege

Get-ChildItem2 swallowed what a later command threw for the error of a nested folder, for example `Get-ChildItem2 -Recurse 2>&1 | ForEach-Object { throw 'x' }`: the recursion took it for a failure of the folder above, wrote a verbose message, and left the loop over the folders, so the listing ended early and the caller never saw the exception. BaseCmdlet now notes the exception that WriteError raises, as it does for WriteObject, WriteVerbose, and WriteDebug. The report that -ErrorAction Stop is swallowed there too was not reproducible (the error reaches the caller), and a break or Select-Object -First was already passed on by type; the tests keep both.

A cmdlet that enables the privileges wrote the debug message "..enabled" before it noted the privilege, so a later command that ended the pipeline or threw at that message left the privilege enabled: Dispose disables only what is noted. TryEnablePrivilege also took the exception of a later command for a failure to enable the privilege and went on with the next one, so `Get-NTFSOwner 5>&1 | ForEach-Object { if ($_.Message -eq '..enabled') { throw 'x' } }` enabled all four privileges and hid the exception. The privilege is noted before the message, and TryEnablePrivilege passes the exception on.

Red before the fix in the Release build of 7aa8315: three tests (the throw on the error stream, in four configurations the first, in the two elevated ones the two privilege tests). Also covers the typed-throw rows that fail if PowerShell stops wrapping a thrown exception, and Enable-Privileges in a script named NTFSSecurity.Init.ps1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/118/head
Raimund Andree 2 days ago
parent
commit
d44a200aaa
  1. 11
      CHANGELOG.md
  2. 4
      Docs/Cmdlets/Get-ChildItem2.md
  3. 42
      NTFSSecurity/BaseCmdlets.cs
  4. 3
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  5. 137
      Tests/PipelineControl.Tests.ps1
  6. 95
      Tests/Privileges.Tests.ps1

11
CHANGELOG.md

@ -131,8 +131,17 @@ The format is based on
when a later command ended the pipeline: a `break` or `continue`, when a later command ended the pipeline: a `break` or `continue`,
`Select-Object -First`, or a `throw` was handled as a failure of the item, `Select-Object -First`, or a `throw` was handled as a failure of the item,
so that `Remove-Item2 -PassThru | Select-Object -First 1` removed every so that `Remove-Item2 -PassThru | Select-Object -First 1` removed every
item, and the caller never saw the `throw`. They now stop and write no item, and the caller never saw the `throw`. The same held when the later
command took a stream instead of the objects: the verbose messages of
`Get-FileHash2` and `Set-NTFSSecurityDescriptor`, the debug messages of
`Set-NTFSOwner`, and the errors of `Get-ChildItem2` for a folder that it
cannot read, for example with `4>&1` or `2>&1`. They now stop and write no
error, and the error of the later command reaches the caller error, and the error of the later command reaches the caller
- Fix the cmdlets that enable the privileges, which left a privilege enabled
in the session and hid the exception of a later command when that command
took the debug message after the enabling, for example with
`5>&1 | Select-Object -First 2`; they now disable the privilege and pass
the exception on
- Fix `Get-ChildItem2 -Filter`, which read a bracket as the start of a - Fix `Get-ChildItem2 -Filter`, which read a bracket as the start of a
character class, so that it did not return a file with brackets in its name, character class, so that it did not return a file with brackets in its name,
such as `Report[1].txt`, for that name; only `*` and `?` are wildcards. A such as `Report[1].txt`, for that name; only `*` and `?` are wildcards. A

4
Docs/Cmdlets/Get-ChildItem2.md

@ -319,7 +319,9 @@ Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `
Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a pattern of an asterisk, a dot, and an asterisk dropped the items without a dot in their names, most folders among them. Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a pattern of an asterisk, a dot, and an asterisk dropped the items without a dot in their names, most folders among them.
Before 5.0.0, a `break` or `continue` in a later command of the pipeline did not end the cmdlet for an item below the first folder. The enumeration of the AlphaFS library decides which names match, and its rules for a dot differ from those of `Get-ChildItem`: a pattern such as `Report.*` does not return the file `Report`, which has no dot, a pattern that ends in a dot returns nothing, and an empty value returns nothing. Only the pattern of an asterisk, a dot, and an asterisk is treated as a single asterisk.
Before 5.0.0, a `break`, a `continue`, or a `throw` in a later command of the pipeline did not end the cmdlet for an item below the first folder, also when the later command took the error of a folder that the cmdlet cannot read, for example with `2>&1`.
## RELATED LINKS ## RELATED LINKS

42
NTFSSecurity/BaseCmdlets.cs

@ -11,9 +11,11 @@ namespace NTFSSecurity
/// <summary> /// <summary>
/// Recognizes what a later command in the pipeline raises to end the pipeline or the loop around it: the end of the /// Recognizes what a later command in the pipeline raises to end the pipeline or the loop around it: the end of the
/// pipeline, for example for Select-Object -First, and a break or continue in a script block. These exceptions pass /// pipeline, for example for Select-Object -First, and a break or continue in a script block. These exceptions pass
/// through a cmdlet while it writes an object. A catch for the failures of an item must pass them on: reported as /// through a cmdlet while it writes to a stream. A catch-all for the failures of an item must pass them on: reported
/// the error of that item, they would end nothing, and the cmdlet would go on with the next item. Anything else that /// as the error of that item, they would end nothing, and the cmdlet would go on with the next item. BaseCmdlet
/// a Write method raises is recognized by BaseCmdlet.IsFromLaterCommand. /// notes the exception that each of its Write methods raises, which includes everything that a later command can
/// throw; this check by type is a second line of defense for the other calls into PowerShell, which also raise the
/// end of the pipeline. See BaseCmdlet.IsFromLaterCommand.
/// </summary> /// </summary>
internal static class PipelineControl internal static class PipelineControl
{ {
@ -47,9 +49,10 @@ namespace NTFSSecurity
// The exception that a Write method of this cmdlet raised last. A Write method runs the later commands of the // The exception that a Write method of this cmdlet raised last. A Write method runs the later commands of the
// pipeline and so raises what they raise: a throw in a script block, an error with -ErrorAction Stop, the end of the // pipeline and so raises what they raise: a throw in a script block, an error with -ErrorAction Stop, the end of the
// pipeline, a break or a continue. None of it is a failure of the item that the cmdlet processes. A catch for those // pipeline, a break or a continue. None of it is a failure of the item that the cmdlet processes. A catch-all for
// failures must pass it on (IsFromLaterCommand), or the cmdlet reports it as the error of that item, goes on with // those failures must pass it on (IsFromLaterCommand), or the cmdlet reports it as the error of that item, goes on
// the next one, and the caller never sees the exception. // with the next one, and the caller never sees the exception. WriteWarning is the one Write method that isn't
// noted, because no catch-all of the module encloses it.
private Exception laterCommandException; private Exception laterCommandException;
/// <summary>Writes the object to the pipeline and notes what a later command raises, see IsFromLaterCommand.</summary> /// <summary>Writes the object to the pipeline and notes what a later command raises, see IsFromLaterCommand.</summary>
@ -80,7 +83,21 @@ namespace NTFSSecurity
} }
} }
// The streams that a later command can take, for example Select-Object -First with 4>&1. // The error, verbose, and debug streams, which a later command can take too, for example Select-Object -First with 2>&1.
/// <summary>Writes the error and notes what a later command raises, see IsFromLaterCommand.</summary>
public new void WriteError(ErrorRecord errorRecord)
{
try
{
base.WriteError(errorRecord);
}
catch (Exception ex)
{
laterCommandException = ex;
throw;
}
}
/// <summary>Writes a verbose message and notes what a later command raises, see IsFromLaterCommand.</summary> /// <summary>Writes a verbose message and notes what a later command raises, see IsFromLaterCommand.</summary>
public new void WriteVerbose(string text) public new void WriteVerbose(string text)
{ {
@ -424,9 +441,10 @@ namespace NTFSSecurity
WriteDebug(string.Format("The privilege {0} is disabled...", privilege)); WriteDebug(string.Format("The privilege {0} is disabled...", privilege));
//activate it //activate it
privControl.EnablePrivilege(privilege); privControl.EnablePrivilege(privilege);
WriteDebug(string.Format("..enabled")); //remember the privilege so that we can automatically disable it after the cmdlet finished processing; before
//remember the privilege so that we can automatically disable it after the cmdlet finished processing //the next message, which a later command can answer with an exception: Dispose disables only what is noted
enabledPrivileges.Add(privilege.ToString()); enabledPrivileges.Add(privilege.ToString());
WriteDebug(string.Format("..enabled"));
privileges = privControl.GetPrivileges(); privileges = privControl.GetPrivileges();
} }
@ -450,6 +468,12 @@ namespace NTFSSecurity
} }
catch(Exception ex) catch(Exception ex)
{ {
// Not a failure to enable the privilege: a later command that took one of the debug messages raised it.
if (IsFromLaterCommand(ex))
{
throw;
}
WriteDebug(string.Format("Could not enable privilege {0}. The error was: {1}", privilege, ex.Message)); WriteDebug(string.Format("Could not enable privilege {0}. The error was: {1}", privilege, ex.Message));
return false; return false;
} }

3
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -3867,7 +3867,8 @@ PS C:\&gt; Disable-Privileges</dev:code>
<maml:para>A folder that cannot be read produces a non-terminating error with the ID `DirUnauthorizedAccessError` for an access denial or `DirUnspecifiedError` for any other failure, and a path that does not exist produces the error `FileNotFound`. In each case the cmdlet continues with the next path. Failures that occur while `-Recurse` collects the subfolders of a folder are reported as verbose messages only, not as errors.</maml:para> <maml:para>A folder that cannot be read produces a non-terminating error with the ID `DirUnauthorizedAccessError` for an access denial or `DirUnspecifiedError` for any other failure, and a path that does not exist produces the error `FileNotFound`. In each case the cmdlet continues with the next path. Failures that occur while `-Recurse` collects the subfolders of a folder are reported as verbose messages only, not as errors.</maml:para>
<maml:para>Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, `-Attributes` returned only the items that had all the listed attributes, and an empty `-Attributes` value returned every item, also the hidden ones. Earlier builds, including the 5.0.0 prereleases, could also omit the first hidden item with `-Hidden` unless `-Force` was explicitly supplied.</maml:para> <maml:para>Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, `-Attributes` returned only the items that had all the listed attributes, and an empty `-Attributes` value returned every item, also the hidden ones. Earlier builds, including the 5.0.0 prereleases, could also omit the first hidden item with `-Hidden` unless `-Force` was explicitly supplied.</maml:para>
<maml:para>Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a pattern of an asterisk, a dot, and an asterisk dropped the items without a dot in their names, most folders among them.</maml:para> <maml:para>Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a pattern of an asterisk, a dot, and an asterisk dropped the items without a dot in their names, most folders among them.</maml:para>
<maml:para>Before 5.0.0, a `break` or `continue` in a later command of the pipeline did not end the cmdlet for an item below the first folder.</maml:para> <maml:para>The enumeration of the AlphaFS library decides which names match, and its rules for a dot differ from those of `Get-ChildItem`: a pattern such as `Report.*` does not return the file `Report`, which has no dot, a pattern that ends in a dot returns nothing, and an empty value returns nothing. Only the pattern of an asterisk, a dot, and an asterisk is treated as a single asterisk.</maml:para>
<maml:para>Before 5.0.0, a `break`, a `continue`, or a `throw` in a later command of the pipeline did not end the cmdlet for an item below the first folder, also when the later command took the error of a folder that the cmdlet cannot read, for example with `2&gt;&amp;1`.</maml:para>
</maml:alert> </maml:alert>
</maml:alertSet> </maml:alertSet>
<command:examples> <command:examples>

137
Tests/PipelineControl.Tests.ps1

@ -1,10 +1,10 @@
<# <#
Tests how the cmdlets of the module built in NTFSSecurity\bin\Release behave when a later command in the pipeline Tests how the cmdlets of the module built in NTFSSecurity\bin\Release behave when a later command in the pipeline
ends it: a break or continue in a script block, Select-Object -First, or a terminating error such as a throw. The ends it: a break or continue in a script block, Select-Object -First, or a terminating error such as a throw. The
exception that carries it passes through the cmdlet while it writes an object, a verbose message, or a debug message. exception that carries it passes through the cmdlet while it writes an object, an error, a verbose message, or a debug
A catch for the failures of an item must not report it as an error of that item and go on with the next one: a message. A catch-all for the failures of an item must not report it as an error of that item and go on with the next
cmdlet that removes, copies, moves, or changes items would change them all, although the caller ended the pipeline, one: a cmdlet that removes, copies, moves, or changes items would change them all, although the caller ended the
and the caller would never see the exception. Every test works on files and folders in a sandbox. pipeline, and the caller would never see the exception. Every test works on files and folders in a sandbox.
#> #>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute( [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.' 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
@ -473,8 +473,135 @@ Describe 'A later command that ends the pipeline' {
} }
} }
# The errors that Get-ChildItem2 writes for a folder that it cannot read reach a later command too, for example with 2>&1.
# The folders that cannot be read come first in the order of the file system, so that the folder with the file is reached
# only if the listing goes on after the first error.
Describe 'A later command and the error of a folder that Get-ChildItem2 cannot read' {
BeforeAll {
$errorTree = New-TestSandboxItem -Sandbox $sandbox -Name 'ErrorTree' -Directory
$unreadable = foreach ($name in 'A', 'B') {
$folder = Join-Path -Path $errorTree -ChildPath $name
Assert-TestSandboxPath -Sandbox $sandbox -Path $folder
New-Item -ItemType Directory -Path $folder | Out-Null
$folder
}
$readableFile = Join-Path -Path $errorTree -ChildPath 'C\Three.txt'
Assert-TestSandboxPath -Sandbox $sandbox -Path $readableFile
New-Item -ItemType Directory -Path (Split-Path -Path $readableFile -Parent) | Out-Null
Set-Content -LiteralPath $readableFile -Value 'Three'
foreach ($folder in $unreadable) {
Add-TestDenyRule -Sandbox $sandbox -Path $folder -Rights @{ 'S-1-1-0' = 'ReadData' }
}
}
# Before 5.0.0-rc7, the recursion took what the later command threw for the error of a nested folder as a failure of
# the folder above it, wrote a verbose message, and left the loop over the folders: the listing ended early and the
# caller never saw the exception.
It 'Should pass on what a later command throws when it takes the error of a nested folder' {
$emitted = 0
$caught = $null
try {
Get-ChildItem2 -Path $errorTree -Recurse -File 2>&1 | ForEach-Object -Process {
$emitted++
throw 'Downstream failure'
}
}
catch {
$caught = $_
}
$caught.Exception.Message | Should -BeLike '*Downstream failure*'
$emitted | Should -Be 1
}
It 'Should leave the loop for a <Keyword> of a later command that takes the error of a nested folder' -ForEach @(
@{ Keyword = 'break' }
@{ Keyword = 'continue' }
) {
$emitted = 0
$reachedEnd = $false
foreach ($round in 1) {
Get-ChildItem2 -Path $errorTree -Recurse -File 2>&1 | ForEach-Object -Process {
$emitted++
if ($Keyword -eq 'break') { break } else { continue }
}
$reachedEnd = $true
}
$emitted | Should -Be 1
$reachedEnd | Should -BeFalse
}
It 'Should stop with the error of the first nested folder that it cannot read for -ErrorAction Stop' {
$listed = New-Object -TypeName 'System.Collections.Generic.List[object]'
$caught = $null
try {
Get-ChildItem2 -Path $errorTree -Recurse -File -ErrorAction Stop | ForEach-Object -Process { $listed.Add($_) }
}
catch {
$caught = $_
}
$caught | Should -Not -BeNullOrEmpty
$caught.FullyQualifiedErrorId | Should -BeLike 'DirUnauthorizedAccessError,*'
$caught.TargetObject | Should -BeIn $unreadable
$listed | Should -BeNullOrEmpty
}
}
# The catches of Get-ChildItem2 for an UnauthorizedAccessException and of Remove-Item2 for an IOException don't ask where
# the exception comes from: they rely on PowerShell wrapping what a later command throws, so that an exception of these
# types never reaches them as it was thrown. A PowerShell version that hands it on as it is fails these tests.
Describe 'A later command that throws an exception of a type that a cmdlet handles' {
It 'Get-ChildItem2 should pass on a thrown UnauthorizedAccessException' {
$folder = New-TestSandboxItem -Sandbox $sandbox -Name 'ThrownDenied' -Directory
$files = 'One.txt', 'Two.txt' | ForEach-Object -Process { Join-Path -Path $folder -ChildPath $_ }
Assert-TestSandboxPath -Sandbox $sandbox -Path $files
Set-Content -LiteralPath $files -Value 'File'
$emitted = 0
$caught = $null
$Error.Clear()
try {
Get-ChildItem2 -Path $folder -File -ErrorAction SilentlyContinue | ForEach-Object -Process {
$emitted++
throw [System.UnauthorizedAccessException]::new('Downstream failure')
}
}
catch {
$caught = $_
}
$caught.Exception | Should -BeOfType [System.UnauthorizedAccessException]
$caught.Exception.Message | Should -BeExactly 'Downstream failure'
$emitted | Should -Be 1
@($Error | Where-Object -FilterScript { $_.FullyQualifiedErrorId -like 'DirUnauthorizedAccessError,*' }) | Should -BeNullOrEmpty
}
It 'Remove-Item2 should pass on a thrown IOException and leave the next item' {
$pair = New-Pair
$emitted = 0
$caught = $null
$Error.Clear()
try {
Remove-Item2 -Path $pair.First, $pair.Second -PassThru -ErrorAction SilentlyContinue | ForEach-Object -Process {
$emitted++
throw [System.IO.IOException]::new('Downstream failure')
}
}
catch {
$caught = $_
}
$caught.Exception | Should -BeOfType [System.IO.IOException]
$caught.Exception.Message | Should -BeExactly 'Downstream failure'
$emitted | Should -Be 1
$pair.Second | Should -Exist
@($Error | Where-Object -FilterScript { $_.FullyQualifiedErrorId -like 'DeleteError,*' }) | Should -BeNullOrEmpty
}
}
# A cmdlet also meets the end of the pipeline where it did not write: another call of PowerShell can raise it too. The # A cmdlet also meets the end of the pipeline where it did not write: another call of PowerShell can raise it too. The
# check that every catch makes recognizes the exceptions by their types. PowerShell keeps the exceptions of break and # check that every catch-all makes recognizes the exceptions by their types. PowerShell keeps the exceptions of break and
# continue internal, so they are recognized by the name of their base type, which a stand-in with that name shows. # continue internal, so they are recognized by the name of their base type, which a stand-in with that name shows.
Describe 'Recognizing the end of a pipeline by the type of the exception' { Describe 'Recognizing the end of a pipeline by the type of the exception' {
BeforeAll { BeforeAll {

95
Tests/Privileges.Tests.ps1

@ -182,6 +182,101 @@ Describe 'Privileges when the pipeline stops early' {
} }
} }
# A cmdlet enables the privileges one after the other and writes a debug message before and after each one. A later command
# that takes the debug stream can end the pipeline or throw at the message after the enabling, before the cmdlet has noted
# that it enabled the privilege.
Describe 'Privileges when a later command takes the debug messages of the cmdlet' {
BeforeAll {
$privateData['EnablePrivileges'] = $true
$debugFile = New-TestSandboxItem -Sandbox $sandbox -Name 'DebugStopped'
}
AfterAll {
$privateData['EnablePrivileges'] = $enablePrivileges
}
BeforeEach {
Disable-Privileges -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
AfterEach {
Disable-Privileges -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
# Before 5.0.0-rc7, the privilege that the cmdlet had enabled at that moment stayed enabled in the session: nothing
# disabled it, because the cmdlet had not noted yet that it enabled it.
It 'Should disable the privilege when Select-Object -First ends the pipeline at the message after its enabling' -Skip:(-not $holdsPrivileges) {
$DebugPreference = 'Continue'
$messages = @(Get-NTFSOwner -Path $debugFile 5>&1 | ForEach-Object -Process { $_.Message })
$enabledAt = $messages.IndexOf('..enabled') + 1
$enabledAt | Should -BeGreaterThan 0
Get-EnabledFileSystemPrivilege | Should -BeNullOrEmpty
$result = @(Get-NTFSOwner -Path $debugFile 5>&1 | Select-Object -First $enabledAt)
$result | Should -HaveCount $enabledAt
$result[-1].Message | Should -BeExactly '..enabled'
Get-EnabledFileSystemPrivilege | Should -BeNullOrEmpty
}
# Before 5.0.0-rc7, the cmdlet took the exception for the failure to enable the privilege, went on with the next
# privilege, and the caller never saw it; all four privileges stayed enabled.
It 'Should pass on what a later command throws at the message after the enabling and disable the privileges' -Skip:(-not $holdsPrivileges) {
$DebugPreference = 'Continue'
$caught = $null
try {
Get-NTFSOwner -Path $debugFile 5>&1 | ForEach-Object -Process {
if ($_.Message -eq '..enabled') { throw 'Downstream failure' }
$_
} | Out-Null
}
catch {
$caught = $_
}
$caught.Exception.Message | Should -BeLike '*Downstream failure*'
Get-EnabledFileSystemPrivilege | Should -BeNullOrEmpty
}
}
# Enable-Privileges recognizes the script NTFSSecurity.Init.ps1, which a user adds to start the module, by its name: from
# that script, it enables the privileges only for the module setting EnablePrivileges, from any other script always. Each
# test runs the script in a child process, which starts without enabled privileges, so that the privileges of this
# process stay as they are.
Describe 'Enable-Privileges in the script NTFSSecurity.Init.ps1' {
BeforeAll {
function Invoke-StartScript {
param ([string] $ScriptName, [bool] $Setting)
$folder = Join-Path -Path $sandbox -ChildPath ('Start-{0}' -f [guid]::NewGuid().ToString('N').Substring(0, 8))
$script = Join-Path -Path $folder -ChildPath $ScriptName
Assert-TestSandboxPath -Sandbox $sandbox -Path $script
New-Item -ItemType Directory -Path $folder | Out-Null
Set-Content -LiteralPath $script -Value @'
param ($ModulePath, $Setting)
Import-Module -Name $ModulePath -ErrorAction Stop
(Get-Module -Name NTFSSecurity).PrivateData['EnablePrivileges'] = ($Setting -eq 'True')
Enable-Privileges
'BACKUP:{0}' -f (Get-Privileges | Where-Object -Property Privilege -EQ -Value 'Backup').PrivilegeState
'@
$output = & (Get-Process -Id $PID).Path -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $script -ModulePath ([IO.Path]::GetFullPath($modulePath)) -Setting $Setting
@($output | Where-Object -FilterScript { $_ -like 'BACKUP:*' }) -replace '^BACKUP:'
}
}
It 'Should enable the privileges when the module setting EnablePrivileges is $true' -Skip:(-not $holdsPrivileges) {
Invoke-StartScript -ScriptName 'NTFSSecurity.Init.ps1' -Setting $true | Should -Be 'Enabled'
}
It 'Should leave the privileges disabled when the module setting EnablePrivileges is $false' -Skip:(-not $holdsPrivileges) {
Invoke-StartScript -ScriptName 'NTFSSecurity.Init.ps1' -Setting $false | Should -Be 'Disabled'
}
It 'Should enable the privileges in a script of another name also when the module setting EnablePrivileges is $false' -Skip:(-not $holdsPrivileges) {
Invoke-StartScript -ScriptName 'Other.ps1' -Setting $false | Should -Be 'Enabled'
}
}
Describe 'Privileges that another command in the pipeline changes' { Describe 'Privileges that another command in the pipeline changes' {
BeforeAll { BeforeAll {
$privateData['EnablePrivileges'] = $true $privateData['EnablePrivileges'] = $true

Loading…
Cancel
Save