Browse Source

fix: disable the privileges also when the pipeline stops early

The cmdlets that enable the Backup, Restore, Take Ownership, and Security
privileges disabled them only in EndProcessing, which PowerShell skips
when a later command, such as Select-Object -First, or a terminating
error stops the pipeline. The privileges then stayed enabled in the
session. BaseCmdletWithPrivControl now implements IDisposable and
disables them in Dispose as well; Enable-Privileges keeps them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
ai/release-5.0.0-rc6
Raimund Andree 4 days ago
parent
commit
82969ba089
  1. 4
      CHANGELOG.md
  2. 13
      Docs/Concepts.md
  3. 37
      NTFSSecurity/BaseCmdlets.cs
  4. 6
      NTFSSecurity/OtherCmdlets.cs
  5. 41
      Tests/Privileges.Tests.ps1

4
CHANGELOG.md

@ -269,5 +269,9 @@ The format is based on
- Fix `Get-NTFSEffectiveAccess`, which returned no access when the computer
of `-ServerName` couldn't be reached, although it warned that it had
calculated the result on this computer; it now returns that result
- Fix the cmdlets that enable the Backup, Restore, Take Ownership, and
Security privileges, which left them enabled in the session when a later
command, such as `Select-Object -First`, or a terminating error stopped
the pipeline early; they now disable them also then
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

13
Docs/Concepts.md

@ -188,11 +188,14 @@ session runs elevated (**Run as administrator**).
The access, audit, inheritance, owner, and security descriptor cmdlets enable
these privileges automatically while they run and disable the ones they
enabled when they finish. If a privilege cannot be enabled, the cmdlet
continues without it. You can turn this behavior off with the
`EnablePrivileges` module setting. Before 5.0.0, the inheritance cmdlets were
an exception: they always tried to enable the privileges, and when
`EnablePrivileges` was `$false`, they left them enabled.
enabled when they finish, also when a later command such as
`Select-Object -First` or a terminating error stops the pipeline early. If a
privilege cannot be enabled, the cmdlet continues without it. You can turn
this behavior off with the `EnablePrivileges` module setting. Before 5.0.0,
the inheritance cmdlets were an exception: they always tried to enable the
privileges, and when `EnablePrivileges` was `$false`, they left them enabled.
And before 5.0.0, every cmdlet left the privileges enabled in the session when
the pipeline stopped early.
`Enable-Privileges` enables the four privileges for the current PowerShell
process until you run `Disable-Privileges` or close the session.

37
NTFSSecurity/BaseCmdlets.cs

@ -182,13 +182,19 @@ namespace NTFSSecurity
#endregion
}
public class BaseCmdletWithPrivControl : BaseCmdlet
public class BaseCmdletWithPrivControl : BaseCmdlet, IDisposable
{
protected PrivilegeAndAttributesCollection privileges = null;
protected PrivilegeControl privControl = new PrivilegeControl();
private List<string> enabledPrivileges = new List<string>();
Hashtable privateData = null;
// A cmdlet that enables the privileges for the session, such as Enable-Privileges, keeps them enabled.
protected virtual bool KeepEnabledPrivileges
{
get { return false; }
}
protected override void BeginProcessing()
{
privateData = (Hashtable)MyInvocation.MyCommand.Module.PrivateData;
@ -208,15 +214,40 @@ namespace NTFSSecurity
//disable all privileges that have been enabled by this cmdlet
WriteVerbose(string.Format("Disabeling all {0} enabled privileges...", enabledPrivileges.Count));
foreach (var privilege in enabledPrivileges)
foreach (var privilege in DisableEnabledPrivileges())
{
DisablePrivilege((Privilege)Enum.Parse(typeof(Privilege), privilege));
WriteVerbose(string.Format("\t{0} disabled", privilege));
}
WriteVerbose(string.Format("...finished"));
}
}
// PowerShell calls Dispose also when a later command or a terminating error stops the pipeline, and then
// skips EndProcessing. Before 5.0.0-rc6, the privileges that the cmdlet had enabled stayed enabled in the
// session in that case.
public void Dispose()
{
DisableEnabledPrivileges();
GC.SuppressFinalize(this);
}
// Disables the privileges that this cmdlet enabled, once, and returns their names.
private List<string> DisableEnabledPrivileges()
{
var disabled = new List<string>();
if (!KeepEnabledPrivileges)
{
foreach (var privilege in enabledPrivileges)
{
DisablePrivilege((Privilege)Enum.Parse(typeof(Privilege), privilege));
disabled.Add(privilege);
}
}
enabledPrivileges.Clear();
return disabled;
}
protected void EnablePrivilege(Privilege privilege)
{
//throw an exception if the specified prililege is not held by the client

6
NTFSSecurity/OtherCmdlets.cs

@ -61,6 +61,12 @@ namespace NTFSSecurity
{
//nothing as we want to keep the privileges enabled
}
// Keeps the privileges enabled also when the pipeline stops early and PowerShell calls only Dispose.
protected override bool KeepEnabledPrivileges
{
get { return true; }
}
}
#endregion Enable-Privileges

41
Tests/Privileges.Tests.ps1

@ -119,3 +119,44 @@ Describe 'Inheritance cmdlets' {
}
}
}
Describe 'Privileges when the pipeline stops early' {
BeforeAll {
$files = 1..3 | ForEach-Object -Process { New-TestSandboxItem -Sandbox $sandbox -Name "Stopped$_" }
$missing = Join-Path -Path $sandbox -ChildPath 'StoppedMissing.txt'
}
BeforeEach {
Disable-Privileges -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
AfterEach {
# A failing test must not leave the privileges enabled for the tests that follow.
Disable-Privileges -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
# Before 5.0.0-rc6, a cmdlet disabled the privileges that it had enabled only in EndProcessing, which PowerShell
# skips when a later command or a terminating error stops the pipeline. The Backup, Restore, Take Ownership, and
# Security privileges then stayed enabled in the session.
It 'Should disable the privileges after Select-Object -First stops the pipeline' -Skip:(-not $holdsPrivileges) {
Get-BackupPrivilegeState | Should -Be 'Disabled'
Get-NTFSOwner -Path $files | Select-Object -First 1 | Out-Null
Get-BackupPrivilegeState | Should -Be 'Disabled'
}
It 'Should disable the privileges after a terminating error' -Skip:(-not $holdsPrivileges) {
Get-BackupPrivilegeState | Should -Be 'Disabled'
{ Get-NTFSAccess -Path $missing, $files[0] -ErrorAction Stop } | Should -Throw
Get-BackupPrivilegeState | Should -Be 'Disabled'
}
It 'Enable-Privileges should keep the privileges enabled also when the pipeline stops early' -Skip:(-not $holdsPrivileges) {
Enable-Privileges -PassThru | Select-Object -First 1 | Out-Null
Get-BackupPrivilegeState | Should -Be 'Enabled'
}
}

Loading…
Cancel
Save