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 - Fix `Get-NTFSEffectiveAccess`, which returned no access when the computer
of `-ServerName` couldn't be reached, although it warned that it had of `-ServerName` couldn't be reached, although it warned that it had
calculated the result on this computer; it now returns that result 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 [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 The access, audit, inheritance, owner, and security descriptor cmdlets enable
these privileges automatically while they run and disable the ones they these privileges automatically while they run and disable the ones they
enabled when they finish. If a privilege cannot be enabled, the cmdlet enabled when they finish, also when a later command such as
continues without it. You can turn this behavior off with the `Select-Object -First` or a terminating error stops the pipeline early. If a
`EnablePrivileges` module setting. Before 5.0.0, the inheritance cmdlets were privilege cannot be enabled, the cmdlet continues without it. You can turn
an exception: they always tried to enable the privileges, and when this behavior off with the `EnablePrivileges` module setting. Before 5.0.0,
`EnablePrivileges` was `$false`, they left them enabled. 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 `Enable-Privileges` enables the four privileges for the current PowerShell
process until you run `Disable-Privileges` or close the session. process until you run `Disable-Privileges` or close the session.

37
NTFSSecurity/BaseCmdlets.cs

@ -182,13 +182,19 @@ namespace NTFSSecurity
#endregion #endregion
} }
public class BaseCmdletWithPrivControl : BaseCmdlet public class BaseCmdletWithPrivControl : BaseCmdlet, IDisposable
{ {
protected PrivilegeAndAttributesCollection privileges = null; protected PrivilegeAndAttributesCollection privileges = null;
protected PrivilegeControl privControl = new PrivilegeControl(); protected PrivilegeControl privControl = new PrivilegeControl();
private List<string> enabledPrivileges = new List<string>(); private List<string> enabledPrivileges = new List<string>();
Hashtable privateData = null; 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() protected override void BeginProcessing()
{ {
privateData = (Hashtable)MyInvocation.MyCommand.Module.PrivateData; privateData = (Hashtable)MyInvocation.MyCommand.Module.PrivateData;
@ -208,15 +214,40 @@ namespace NTFSSecurity
//disable all privileges that have been enabled by this cmdlet //disable all privileges that have been enabled by this cmdlet
WriteVerbose(string.Format("Disabeling all {0} enabled privileges...", enabledPrivileges.Count)); 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("\t{0} disabled", privilege));
} }
WriteVerbose(string.Format("...finished")); 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) protected void EnablePrivilege(Privilege privilege)
{ {
//throw an exception if the specified prililege is not held by the client //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 //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 #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