Browse Source

Merge pull request #104 from raandree/ai/decisions-e

feat!: Set-NTFSInheritance keeps entries, audit switch names, Get-FileHash2 in PowerShell 7 (group E)
pull/112/head
Raimund Andrée 6 days ago
committed by GitHub
parent
commit
b53fe69829
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      .memory-bank/activeContext.md
  2. 34
      .memory-bank/decisions/0013-set-inheritance-keeps-entries.md
  3. 29
      .memory-bank/progress.md
  4. 1
      .memory-bank/systemPatterns.md
  5. 20
      CHANGELOG.md
  6. 2
      Docs/Cmdlets/Clear-NTFSAccess.md
  7. 15
      Docs/Cmdlets/Disable-NTFSAuditInheritance.md
  8. 18
      Docs/Cmdlets/Enable-NTFSAuditInheritance.md
  9. 6
      Docs/Cmdlets/Get-FileHash2.md
  10. 10
      Docs/Cmdlets/Set-NTFSInheritance.md
  11. 10
      Docs/Concepts.md
  12. 19
      NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs
  13. 19
      NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs
  14. 8
      NTFSSecurity/InheritanceCmdlets/SetInheritance.cs
  15. 21
      NTFSSecurity/MiscCmdlets/GetFileHash2.cs
  16. 80
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  17. 74
      Security2/FileSystem/FileInfo/Extensions.cs
  18. 17
      Tests/Access.Tests.ps1
  19. 46
      Tests/FileHash.Tests.ps1
  20. 67
      Tests/Inheritance.Tests.ps1
  21. 2
      Tests/OutputTypes.Tests.ps1

7
.memory-bank/activeContext.md

@ -57,7 +57,10 @@ the PRs, and tags `5.0.0-rc2` after the merges.
the `*-Item2` cmdlets: Windows PowerShell 379 passed, 23 skipped;
PowerShell 7 348 passed, 54 skipped (402 tests).
- `ai/decisions-e` implements D2 to D5 (Decision 13): Windows PowerShell
395 passed, 26 skipped; PowerShell 7 366 passed, 55 skipped (421
tests). `Get-FileHash2` tests now run in PowerShell 7 as well.
## Next step
The E decisions on `ai/decisions-e` (Decision 13 for `Set-NTFSInheritance`),
then the bugs from the issue triage (`ai/issue-fixes`) and 5.0.0-rc2.
The bugs from the issue triage (`ai/issue-fixes`), then 5.0.0-rc2.

34
.memory-bank/decisions/0013-set-inheritance-keeps-entries.md

@ -0,0 +1,34 @@
---
status: accepted
date: 2026-10-05
last-verified: 2026-10-05
owner: shared
source: maintainer decision D3 for the overnight run of 2026-10-04 (defect group E)
---
# Decision 13: Set-NTFSInheritance keeps entries like the dedicated cmdlets
- Choice: `Set-NTFSInheritance` uses the defaults of the dedicated cmdlets.
`-AccessInheritanceEnabled $false` copies the inherited access entries
into the DACL, like `Disable-NTFSAccessInheritance`, and
`-AuditInheritanceEnabled $true` keeps the explicit audit entries, like
`Enable-NTFSAuditInheritance`. The other two directions already matched.
The same applies to a security descriptor in memory, whose kept entries
stay marked as inherited until it is written.
- Way back: `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules`
removes the inherited access entries, and
`Enable-NTFSAuditInheritance -RemoveExplicitAuditRules` removes the
explicit audit entries. `Set-NTFSInheritance` gets no switches for that.
- Rationale: Before 5.0.0, two of the four directions removed entries,
unlike the dedicated cmdlets. Turning access inheritance off on an item
without explicit entries left an empty DACL, which denies access to
everyone. 5.0.0 is a major version, so the change ships there, listed
under `Changed` in the changelog.
- Related: `Clear-NTFSAccess -DisableInheritance` keeps discarding the
inherited entries, because removing every entry is the purpose of that
cmdlet; its page states the empty DACL and its risk (decision D2 of the
same run). The audit switches were renamed to `-RemoveInheritedAuditRules`
and `-RemoveExplicitAuditRules`, with the old names as aliases (D4).
- Rejected: adding `-RemoveInheritedAccessRules` and
`-RemoveExplicitAuditRules` switches to `Set-NTFSInheritance`, which would
duplicate the dedicated cmdlets.

29
.memory-bank/progress.md

@ -166,19 +166,16 @@ Numbered as agreed with the maintainer; each is documented on its page.
- Found with group D: `-PassThru` of the `*-Item2` cmdlets wrote the item
also when `-WhatIf` skipped the operation.
#### E: Maintainer decisions before changing behavior
- `Clear-NTFSAccess -DisableInheritance` removes the explicit entries and
then disables inheritance without copying the inherited ones, which
leaves an empty DACL: keep that, or copy them?
- `Set-NTFSInheritance` differs from the dedicated cmdlets in two of four
directions: `-AccessInheritanceEnabled $false` removes the inherited
access entries, and `-AuditInheritanceEnabled $true` removes the
explicit audit entries; the dedicated cmdlets keep them unless a switch
is given. Align?
- The audit inheritance switches are named `*AccessRules`: add
`*AuditRules` aliases?
- `Get-FileHash2` fails in PowerShell 7 (`RIPEMD160`): drop the algorithm
there, load it lazily, or deprecate the cmdlet? To verify:
`MACTripleDES.Create()` may use a random key, so its result would differ
on every call.
#### E: Maintainer decisions (implemented on `ai/decisions-e`, not merged)
- D2: `Clear-NTFSAccess -DisableInheritance` keeps leaving an empty DACL;
the page states the result and the risk, and a test pins it.
- D3: `Set-NTFSInheritance` keeps entries like the dedicated cmdlets
(Decision 13; listed under `Changed`).
- D4: `-RemoveInheritedAuditRules` and `-RemoveExplicitAuditRules`, with
the `*AccessRules` names as aliases.
- D5: `Get-FileHash2` works in PowerShell 7; `RIPEMD160` and
`MACTripleDES` stop it there with `HashAlgorithmNotAvailable`.
`MACTripleDES` uses a random key (verified), so it is deprecated.
- Review of group E: the changelog now marks the `Set-NTFSInheritance`
change as breaking and warns that it leaves broader access in place.

1
.memory-bank/systemPatterns.md

@ -58,6 +58,7 @@ Each Decision record is a file in `decisions/`; read only the relevant ones.
| 10 | [One version for the manifest, assemblies, and changelog](decisions/0010-one-version.md) |
| 11 | [CI and the wiki run on GitHub Actions](decisions/0011-github-actions.md) |
| 12 | [Releases are built and published by CI on a version tag](decisions/0012-ci-releases.md) |
| 13 | [Set-NTFSInheritance keeps entries like the dedicated cmdlets](decisions/0013-set-inheritance-keeps-entries.md) |
## Patterns

20
CHANGELOG.md

@ -36,10 +36,26 @@ The format is based on
into the documentation, and complete the version history with the release
dates from the PowerShell Gallery, the missing notes for 4.2.2, 4.2.5, and
4.2.6, and detailed notes for 4.2.4
- Rename `-RemoveInheritedAccessRules` of `Disable-NTFSAuditInheritance` to
`-RemoveInheritedAuditRules` and `-RemoveExplicitAccessRules` of
`Enable-NTFSAuditInheritance` to `-RemoveExplicitAuditRules`, because they
act on audit entries; the old names still work as aliases
- **Breaking:** `Set-NTFSInheritance` keeps entries like the dedicated
cmdlets: `-AccessInheritanceEnabled $false` now copies the inherited access
entries into the DACL instead of removing them, and
`-AuditInheritanceEnabled $true` now keeps the explicit audit entries. A
script that used `-AccessInheritanceEnabled $false` to drop the inherited
access entries now leaves them in place, which grants broader access than
before. To remove the entries, use
`Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` or
`Enable-NTFSAuditInheritance -RemoveExplicitAuditRules`
### Deprecated
- Deprecate the `-PassThur` alias of `Remove-Item2`; use `-PassThru`
- Deprecate the `MACTripleDES` value of `Get-FileHash2 -Algorithm`: it uses
a random key, so its result differs on every call; the cmdlet now warns
when you use it
### Fixed
@ -149,5 +165,9 @@ The format is based on
- Fix `-PassThru` of `Copy-Item2`, `Move-Item2`, and `Remove-Item2`, which
wrote the item also when `-WhatIf` or a declined confirmation skipped the
operation
- Fix `Get-FileHash2` in PowerShell 7, where it failed for every algorithm;
`RIPEMD160` and `MACTripleDES`, which .NET lacks there, now stop the
cmdlet with an error that names the algorithm and points to Windows
PowerShell 5.1
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

2
Docs/Cmdlets/Clear-NTFSAccess.md

@ -72,7 +72,7 @@ These commands replace the complete ACL of `C:\Data` in one write. The security
### -DisableInheritance
Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. Without this switch the inherited entries remain in effect.
Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. The item is left with an empty DACL, which denies access to everyone; only its owner can still change the permissions. Without this switch the inherited entries remain in effect.
```yaml
Type: SwitchParameter

15
Docs/Cmdlets/Disable-NTFSAuditInheritance.md

@ -15,13 +15,12 @@ Blocks the inheritance of audit rules on a file or folder.
### Path (Default)
```
Disable-NTFSAuditInheritance [[-Path] <String[]>] [-RemoveInheritedAccessRules] [-PassThru]
[<CommonParameters>]
Disable-NTFSAuditInheritance [[-Path] <String[]>] [-RemoveInheritedAuditRules] [-PassThru] [<CommonParameters>]
```
### SecurityDescriptor
```
Disable-NTFSAuditInheritance [-SecurityDescriptor] <FileSystemSecurity2[]> [-RemoveInheritedAccessRules]
Disable-NTFSAuditInheritance [-SecurityDescriptor] <FileSystemSecurity2[]> [-RemoveInheritedAuditRules]
[-PassThru] [<CommonParameters>]
```
@ -29,7 +28,7 @@ Disable-NTFSAuditInheritance [-SecurityDescriptor] <FileSystemSecurity2[]> [-Rem
The `Disable-NTFSAuditInheritance` cmdlet protects the system access control list (SACL) of a file or folder, so that the audit rules of the parent folder no longer apply to the item. From then on, only the audit rules stored in the item's own SACL decide which access attempts are written to the security event log.
By default, the audit rules that the item currently inherits are copied into its SACL before inheritance is blocked, so the auditing behavior stays the same. The `-RemoveInheritedAccessRules` switch discards the inherited audit rules instead of copying them, which leaves only the audit rules that were already explicit on the item. Despite its name, the switch acts on audit rules, not on access rules.
By default, the audit rules that the item currently inherits are copied into its SACL before inheritance is blocked, so the auditing behavior stays the same. The `-RemoveInheritedAuditRules` switch discards the inherited audit rules instead of copying them, which leaves only the audit rules that were already explicit on the item. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`; that name still works as an alias.
In the `Path` parameter set the cmdlet reads the audit section of the item's security descriptor, changes it, and writes it back to disk immediately. In the `SecurityDescriptor` parameter set it changes the `Security2.FileSystemSecurity2` object in memory only; nothing reaches the file system until you pass that object to `Set-NTFSSecurityDescriptor`.
@ -48,7 +47,7 @@ This command protects the SACL of `C:\Data\Projects` and copies the audit rules
### Example 2: Block audit inheritance and discard the inherited rules
```PowerShell
PS C:\> Disable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveInheritedAccessRules -PassThru
PS C:\> Disable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveInheritedAuditRules -PassThru
```
This command protects the SACL and removes the inherited audit rules instead of copying them, so the folder is audited only by the rules that were already explicit on it. `-PassThru` returns the resulting state, in which `AuditInheritanceEnabled` is `$false`.
@ -105,14 +104,14 @@ Accept pipeline input: True (ByPropertyName, ByValue)
Accept wildcard characters: False
```
### -RemoveInheritedAccessRules
### -RemoveInheritedAuditRules
Indicates that the audit rules the item currently inherits are discarded. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged.
Indicates that the audit rules the item currently inherits are discarded. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`, which remains an alias.
```yaml
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Aliases: RemoveInheritedAccessRules
Required: False
Position: Named

18
Docs/Cmdlets/Enable-NTFSAuditInheritance.md

@ -15,20 +15,20 @@ Restores the inheritance of audit rules on a file or folder.
### Path (Default)
```
Enable-NTFSAuditInheritance [[-Path] <String[]>] [-PassThru] [-RemoveExplicitAccessRules] [<CommonParameters>]
Enable-NTFSAuditInheritance [[-Path] <String[]>] [-PassThru] [-RemoveExplicitAuditRules] [<CommonParameters>]
```
### SecurityDescriptor
```
Enable-NTFSAuditInheritance [-SecurityDescriptor] <FileSystemSecurity2[]> [-PassThru]
[-RemoveExplicitAccessRules] [<CommonParameters>]
[-RemoveExplicitAuditRules] [<CommonParameters>]
```
## DESCRIPTION
The `Enable-NTFSAuditInheritance` cmdlet removes the protection from the system access control list (SACL) of a file or folder, so that the item inherits audit rules from its parent folder again.
By default, the audit rules that are stored directly on the item are kept, and the inherited rules are added to them. An item that was processed by `Disable-NTFSAuditInheritance` therefore ends up with the inherited audit rules twice: once as the explicit copies that were created when inheritance was blocked, and once as true inherited rules. The `-RemoveExplicitAccessRules` switch deletes every audit rule that is stored directly on the item, which leaves only the inherited ones. Despite its name, the switch acts on audit rules, not on access rules.
By default, the audit rules that are stored directly on the item are kept, and the inherited rules are added to them. An item that was processed by `Disable-NTFSAuditInheritance` therefore ends up with the inherited audit rules twice: once as the explicit copies that were created when inheritance was blocked, and once as true inherited rules. The `-RemoveExplicitAuditRules` switch deletes every audit rule that is stored directly on the item, which leaves only the inherited ones. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`; that name still works as an alias.
In the `Path` parameter set the cmdlet reads the audit section of the item's security descriptor, changes it, and writes it back to disk immediately. In the `SecurityDescriptor` parameter set it changes the `Security2.FileSystemSecurity2` object in memory only; nothing reaches the file system until you pass that object to `Set-NTFSSecurityDescriptor`.
@ -47,7 +47,7 @@ This command lets `C:\Data\Projects` inherit the audit rules of `C:\Data` again.
### Example 2: Restore audit inheritance and drop the explicit rules
```PowerShell
PS C:\> Enable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveExplicitAccessRules -PassThru
PS C:\> Enable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveExplicitAuditRules -PassThru
```
This command removes every audit rule that is stored directly on the folder and lets it inherit from `C:\Data` again, so the folder is audited exactly like its parent. `-PassThru` returns the resulting state, in which `AuditInheritanceEnabled` is `$true`.
@ -55,7 +55,7 @@ This command removes every audit rule that is stored directly on the folder and
### Example 3: Repair a whole folder tree
```PowerShell
PS C:\> Get-ChildItem2 -Path C:\Data -Recurse | Get-NTFSInheritance | Where-Object { $_.AuditInheritanceEnabled -eq $false } | Enable-NTFSAuditInheritance -RemoveExplicitAccessRules
PS C:\> Get-ChildItem2 -Path C:\Data -Recurse | Get-NTFSInheritance | Where-Object { $_.AuditInheritanceEnabled -eq $false } | Enable-NTFSAuditInheritance -RemoveExplicitAuditRules
```
This command finds every item below `C:\Data` whose audit inheritance is blocked and restores it. The comparison with `$false` is deliberate: `AuditInheritanceEnabled` is `$null` for items whose audit section could not be read, and those items are skipped instead of being processed.
@ -64,7 +64,7 @@ This command finds every item below `C:\Data` whose audit inheritance is blocked
```PowerShell
PS C:\> $sd = Get-NTFSSecurityDescriptor -Path C:\Data\Projects
PS C:\> Enable-NTFSAuditInheritance -SecurityDescriptor $sd -RemoveExplicitAccessRules
PS C:\> Enable-NTFSAuditInheritance -SecurityDescriptor $sd -RemoveExplicitAuditRules
PS C:\> Set-NTFSSecurityDescriptor -SecurityDescriptor $sd
```
@ -104,14 +104,14 @@ Accept pipeline input: True (ByPropertyName, ByValue)
Accept wildcard characters: False
```
### -RemoveExplicitAccessRules
### -RemoveExplicitAuditRules
Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier.
Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`, which remains an alias.
```yaml
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Aliases: RemoveExplicitAccessRules
Required: False
Position: Named

6
Docs/Cmdlets/Get-FileHash2.md

@ -65,7 +65,7 @@ This command groups the files below `C:\Data` by hash value and returns the grou
### -Algorithm
Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`.
Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`. `RIPEMD160` and `MACTripleDES` are available only in Windows PowerShell 5.1, and `MACTripleDES` is deprecated.
```yaml
Type: HashAlgorithms
@ -117,13 +117,13 @@ For every hashed file, the cmdlet writes the file object of that file, decorated
## NOTES
The cmdlet works only in Windows PowerShell. In PowerShell 7, it fails for every algorithm with the error `Could not load type 'System.Security.Cryptography.RIPEMD160'`, because .NET no longer includes the RIPEMD-160 implementation that the cmdlet references. In PowerShell 7, use the built-in `Get-FileHash` cmdlet instead.
In PowerShell 7, the cmdlet supports `SHA1`, `SHA256`, `SHA384`, `SHA512`, and `MD5`. .NET no longer includes `RIPEMD160` and `MACTripleDES`, so requesting one of them there stops the cmdlet with the error `HashAlgorithmNotAvailable`, which names the algorithm; use Windows PowerShell 5.1 to calculate those hashes. Before 5.0.0, the cmdlet failed in PowerShell 7 for every algorithm with the error `Could not load type 'System.Security.Cryptography.RIPEMD160'`.
If the file cannot be opened because access is denied, the cmdlet takes ownership of the file with the account that runs it, calculates the hash, and restores the previous owner afterward. That fallback fails with a `GetHashError` when the account is not allowed to change the owner of the file. A file that cannot be read produces a `GetHashError` and no result.
The hash is returned as an uppercase hexadecimal string without separators, which differs from the lowercase output of some other hashing tools. Compare hash values case-insensitively.
`MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files.
`MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files. The value is deprecated: the cmdlet writes a warning when you use it, and a future version will remove it.
Before 5.0.0, a folder in a `-Path` array stopped the processing of that array, so the files that followed the folder were not hashed, and a file that could not be read got a result with the hash of the previous file.

10
Docs/Cmdlets/Set-NTFSInheritance.md

@ -29,7 +29,7 @@ Set-NTFSInheritance [-SecurityDescriptor] <FileSystemSecurity2[]> [-AccessInheri
The `Set-NTFSInheritance` cmdlet turns the inheritance of access rules and audit rules on or off in a single call. It reads the current state of the item first and changes a section only when the requested value differs from the current one, which makes the cmdlet suitable for repeatedly applying a desired state to a folder tree.
The cmdlet performs the same operations as `Enable-NTFSAccessInheritance`, `Disable-NTFSAccessInheritance`, `Enable-NTFSAuditInheritance`, and `Disable-NTFSAuditInheritance`, but it does not expose their switches and it does not use their defaults. `-AccessInheritanceEnabled $false` discards the inherited access rules instead of copying them into the item's own DACL, `-AccessInheritanceEnabled $true` keeps the explicit access rules, `-AuditInheritanceEnabled $false` copies the inherited audit rules into the item's own SACL, and `-AuditInheritanceEnabled $true` removes the explicit audit rules. Use the individual Enable and Disable cmdlets when you need the opposite behavior.
The cmdlet performs the same operations as `Enable-NTFSAccessInheritance`, `Disable-NTFSAccessInheritance`, `Enable-NTFSAuditInheritance`, and `Disable-NTFSAuditInheritance`, but it does not expose their switches; it uses their defaults instead. `-AccessInheritanceEnabled $false` copies the inherited access rules into the item's own DACL, `-AccessInheritanceEnabled $true` keeps the explicit access rules, `-AuditInheritanceEnabled $false` copies the inherited audit rules into the item's own SACL, and `-AuditInheritanceEnabled $true` keeps the explicit audit rules. To remove the rules instead, use `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` or `Enable-NTFSAuditInheritance -RemoveExplicitAuditRules`. Before 5.0.0, `-AccessInheritanceEnabled $false` discarded the inherited access rules, and `-AuditInheritanceEnabled $true` removed the explicit audit rules. Review scripts that used `-AccessInheritanceEnabled $false` to drop the inherited access rules: they now keep them, which leaves broader access in place; `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` gives the old result.
Omit `-AccessInheritanceEnabled` or `-AuditInheritanceEnabled` to leave that section unchanged. Changing the audit section requires the Security privilege and therefore an elevated session.
@ -43,7 +43,7 @@ In the `Path` parameter set the cmdlet writes each changed section back to disk
PS C:\> Set-NTFSInheritance -Path C:\Data\Projects -AccessInheritanceEnabled $false -AuditInheritanceEnabled $false
```
This command protects the DACL and the SACL of `C:\Data\Projects`. The inherited access rules are discarded, so make sure the folder has explicit access rules of its own; the inherited audit rules are copied into the folder's SACL. Changing the audit section requires an elevated session.
This command protects the DACL and the SACL of `C:\Data\Projects`. The inherited access and audit rules are copied into the folder's DACL and SACL, so the effective permissions and the auditing stay the same. Changing the audit section requires an elevated session.
### Example 2: Restore inheritance of both sections
@ -51,7 +51,7 @@ This command protects the DACL and the SACL of `C:\Data\Projects`. The inherited
PS C:\> Set-NTFSInheritance -Path C:\Data\Projects -AccessInheritanceEnabled $true -AuditInheritanceEnabled $true -PassThru
```
This command lets the folder inherit from `C:\Data` again. The explicit access rules are kept, the explicit audit rules are removed, and `-PassThru` returns the resulting state.
This command lets the folder inherit from `C:\Data` again. The explicit access and audit rules are kept, and `-PassThru` returns the resulting state.
### Example 3: Save a state and apply it again
@ -77,7 +77,7 @@ The first two commands read the security descriptor and change its inheritance i
### -AccessInheritanceEnabled
Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and discards the rules the item currently inherits, which leaves only its explicit rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.
Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and copies the rules the item currently inherits into it, so the effective permissions stay the same. Before 5.0.0, `$false` discarded the inherited rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.
```yaml
Type: Boolean
@ -93,7 +93,7 @@ Accept wildcard characters: False
### -AuditInheritanceEnabled
Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and removes the audit rules that are stored directly on the item; `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.
Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and keeps the audit rules that are stored directly on the item (before 5.0.0, it removed them); `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.
```yaml
Type: Boolean

10
Docs/Concepts.md

@ -126,11 +126,13 @@ entries:
- `Enable-NTFSAccessInheritance` restores inheritance and keeps the explicit
entries unless you use `-RemoveExplicitAccessRules`.
- `Get-NTFSInheritance` and `Set-NTFSInheritance` read and set both
settings at once. Unlike the dedicated cmdlets, `Set-NTFSInheritance`
removes the inherited access entries when it turns access inheritance off,
and removes the explicit audit entries when it turns audit inheritance on.
settings at once. Like the dedicated cmdlets without their switches,
`Set-NTFSInheritance` keeps the entries; before 5.0.0, it removed the
inherited access entries when it turned access inheritance off, and the
explicit audit entries when it turned audit inheritance on.
The audit equivalents of the dedicated cmdlets are
`Disable-NTFSAuditInheritance` and `Enable-NTFSAuditInheritance`.
`Disable-NTFSAuditInheritance` and `Enable-NTFSAuditInheritance`, with
the switches `-RemoveInheritedAuditRules` and `-RemoveExplicitAuditRules`.
### The AppliesTo parameter

19
NTFSSecurity/InheritanceCmdlets/DisableAuditInheritance.cs

@ -10,7 +10,7 @@ namespace NTFSSecurity
[OutputType(typeof(FileSystemInheritanceInfo))]
public class DisableAuditInheritance : BaseCmdletWithPrivControl
{
private bool removeInheritedAccessRules;
private bool removeInheritedAuditRules;
private bool passThru;
[Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")]
@ -38,11 +38,16 @@ namespace NTFSSecurity
}
}
/// <summary>
/// Removes the inherited audit entries instead of copying them to the item. Before 5.0.0, the switch was
/// named RemoveInheritedAccessRules, which remains an alias.
/// </summary>
[Parameter]
public SwitchParameter RemoveInheritedAccessRules
[Alias("RemoveInheritedAccessRules")]
public SwitchParameter RemoveInheritedAuditRules
{
get { return removeInheritedAccessRules; }
set { removeInheritedAccessRules = value; }
get { return removeInheritedAuditRules; }
set { removeInheritedAuditRules = value; }
}
[Parameter]
@ -77,7 +82,7 @@ namespace NTFSSecurity
try
{
FileSystemInheritanceInfo.DisableAuditInheritance(item, removeInheritedAccessRules);
FileSystemInheritanceInfo.DisableAuditInheritance(item, removeInheritedAuditRules);
}
catch (UnauthorizedAccessException)
{
@ -85,7 +90,7 @@ namespace NTFSSecurity
{
InvokeAsOwner(item, path, () =>
{
FileSystemInheritanceInfo.DisableAuditInheritance(item, removeInheritedAccessRules);
FileSystemInheritanceInfo.DisableAuditInheritance(item, removeInheritedAuditRules);
});
}
catch (Exception ex2)
@ -111,7 +116,7 @@ namespace NTFSSecurity
{
foreach (var sd in securityDescriptors)
{
FileSystemInheritanceInfo.DisableAuditInheritance(sd, removeInheritedAccessRules);
FileSystemInheritanceInfo.DisableAuditInheritance(sd, removeInheritedAuditRules);
if (passThru)
{

19
NTFSSecurity/InheritanceCmdlets/EnableAuditInheritance.cs

@ -9,7 +9,7 @@ namespace NTFSSecurity
[OutputType(typeof(FileSystemInheritanceInfo))]
public class EnableAuditInheritance : BaseCmdletWithPrivControl
{
private bool removeExplicitAccessRules;
private bool removeExplicitAuditRules;
private bool passThru;
[Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")]
@ -44,11 +44,16 @@ namespace NTFSSecurity
set { passThru = value; }
}
/// <summary>
/// Removes the explicit audit entries of the item. Before 5.0.0, the switch was named
/// RemoveExplicitAccessRules, which remains an alias.
/// </summary>
[Parameter]
public SwitchParameter RemoveExplicitAccessRules
[Alias("RemoveExplicitAccessRules")]
public SwitchParameter RemoveExplicitAuditRules
{
get { return removeExplicitAccessRules; }
set { removeExplicitAccessRules = value; }
get { return removeExplicitAuditRules; }
set { removeExplicitAuditRules = value; }
}
protected override void BeginProcessing()
@ -76,7 +81,7 @@ namespace NTFSSecurity
try
{
FileSystemInheritanceInfo.EnableAuditInheritance(item, removeExplicitAccessRules);
FileSystemInheritanceInfo.EnableAuditInheritance(item, removeExplicitAuditRules);
}
catch (UnauthorizedAccessException)
{
@ -84,7 +89,7 @@ namespace NTFSSecurity
{
InvokeAsOwner(item, path, () =>
{
FileSystemInheritanceInfo.EnableAuditInheritance(item, removeExplicitAccessRules);
FileSystemInheritanceInfo.EnableAuditInheritance(item, removeExplicitAuditRules);
});
}
catch (Exception ex2)
@ -110,7 +115,7 @@ namespace NTFSSecurity
{
foreach (var sd in securityDescriptors)
{
FileSystemInheritanceInfo.EnableAuditInheritance(sd, removeExplicitAccessRules);
FileSystemInheritanceInfo.EnableAuditInheritance(sd, removeExplicitAuditRules);
if (passThru)
{

8
NTFSSecurity/InheritanceCmdlets/SetInheritance.cs

@ -142,7 +142,7 @@ namespace NTFSSecurity
else
{
WriteVerbose("Calling DisableAccessInheritance");
FileSystemInheritanceInfo.DisableAccessInheritance(item, true);
FileSystemInheritanceInfo.DisableAccessInheritance(item, false);
}
}
@ -151,7 +151,7 @@ namespace NTFSSecurity
if (auditInheritanceEnabled.Value)
{
WriteVerbose("Calling EnableAuditInheritance");
FileSystemInheritanceInfo.EnableAuditInheritance(item, true);
FileSystemInheritanceInfo.EnableAuditInheritance(item, false);
}
else
{
@ -175,7 +175,7 @@ namespace NTFSSecurity
else
{
WriteVerbose("Calling DisableAccessInheritance");
FileSystemInheritanceInfo.DisableAccessInheritance(sd, true);
FileSystemInheritanceInfo.DisableAccessInheritance(sd, false);
}
}
@ -184,7 +184,7 @@ namespace NTFSSecurity
if (auditInheritanceEnabled.Value)
{
WriteVerbose("Calling EnableAuditInheritance");
FileSystemInheritanceInfo.EnableAuditInheritance(sd, true);
FileSystemInheritanceInfo.EnableAuditInheritance(sd, false);
}
else
{

21
NTFSSecurity/MiscCmdlets/GetFileHash2.cs

@ -11,6 +11,7 @@ namespace NTFSSecurity
public class GetFileHash2 : BaseCmdlet
{
private HashAlgorithms algorithm = HashAlgorithms.SHA256;
private bool deprecationWarningWritten = false;
[Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
@ -39,6 +40,26 @@ namespace NTFSSecurity
protected override void ProcessRecord()
{
try
{
Security2.FileSystem.FileInfo.Extensions.CreateHashAlgorithm(algorithm).Dispose();
}
catch (PlatformNotSupportedException ex)
{
ThrowTerminatingError(new ErrorRecord(ex, "HashAlgorithmNotAvailable", ErrorCategory.NotImplemented, algorithm));
}
catch (Exception ex)
{
// For example, an algorithm that a FIPS policy doesn't allow.
ThrowTerminatingError(new ErrorRecord(ex, "HashAlgorithmNotAvailable", ErrorCategory.NotImplemented, algorithm));
}
if (algorithm == HashAlgorithms.MACTripleDES && !deprecationWarningWritten)
{
WriteWarning("The MACTripleDES algorithm uses a random key, so its result differs on every call. The value is deprecated and will be removed in a future version.");
deprecationWarningWritten = true;
}
foreach (var path in paths)
{
string hash = null;

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

@ -1601,7 +1601,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DisableInheritance</maml:name>
<maml:description>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. Without this switch the inherited entries remain in effect.</maml:para>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. The item is left with an empty DACL, which denies access to everyone; only its owner can still change the permissions. Without this switch the inherited entries remain in effect.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -1628,7 +1628,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DisableInheritance</maml:name>
<maml:description>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. Without this switch the inherited entries remain in effect.</maml:para>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. The item is left with an empty DACL, which denies access to everyone; only its owner can still change the permissions. Without this switch the inherited entries remain in effect.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -1642,7 +1642,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DisableInheritance</maml:name>
<maml:description>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. Without this switch the inherited entries remain in effect.</maml:para>
<maml:para>Indicates that inheritance is disabled after the explicit entries are removed, and that the inherited entries are discarded rather than copied into the item. The item is left with an empty DACL, which denies access to everyone; only its owner can still change the permissions. Without this switch the inherited entries remain in effect.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
@ -2499,7 +2499,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</command:details>
<maml:description>
<maml:para>The `Disable-NTFSAuditInheritance` cmdlet protects the system access control list (SACL) of a file or folder, so that the audit rules of the parent folder no longer apply to the item. From then on, only the audit rules stored in the item's own SACL decide which access attempts are written to the security event log.</maml:para>
<maml:para>By default, the audit rules that the item currently inherits are copied into its SACL before inheritance is blocked, so the auditing behavior stays the same. The `-RemoveInheritedAccessRules` switch discards the inherited audit rules instead of copying them, which leaves only the audit rules that were already explicit on the item. Despite its name, the switch acts on audit rules, not on access rules.</maml:para>
<maml:para>By default, the audit rules that the item currently inherits are copied into its SACL before inheritance is blocked, so the auditing behavior stays the same. The `-RemoveInheritedAuditRules` switch discards the inherited audit rules instead of copying them, which leaves only the audit rules that were already explicit on the item. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`; that name still works as an alias.</maml:para>
<maml:para>In the `Path` parameter set the cmdlet reads the audit section of the item's security descriptor, changes it, and writes it back to disk immediately. In the `SecurityDescriptor` parameter set it changes the `Security2.FileSystemSecurity2` object in memory only; nothing reaches the file system until you pass that object to `Set-NTFSSecurityDescriptor`.</maml:para>
<maml:para>Reading and writing the audit section requires the Security privilege, so run this cmdlet in an elevated session. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so the output of `Get-ChildItem`, `Get-ChildItem2`, `Get-Item2`, and `Get-NTFSInheritance` binds to it. The cmdlet affects only the audit rules; use `Disable-NTFSAccessInheritance` for the access rules.</maml:para>
</maml:description>
@ -2529,10 +2529,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveInheritedAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveInheritedAccessRules">
<maml:name>RemoveInheritedAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged.</maml:para>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`, which remains an alias.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -2568,10 +2568,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveInheritedAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveInheritedAccessRules">
<maml:name>RemoveInheritedAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged.</maml:para>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`, which remains an alias.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -2606,10 +2606,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveInheritedAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveInheritedAccessRules">
<maml:name>RemoveInheritedAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged.</maml:para>
<maml:para>Indicates that the audit rules the item currently inherits are discarded. By default, when the switch is omitted, the inherited audit rules are copied into the item's own SACL as explicit rules and auditing continues unchanged. Before 5.0.0, the switch was named `-RemoveInheritedAccessRules`, which remains an alias.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
@ -2681,7 +2681,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</command:example>
<command:example>
<maml:title>Example 2: Block audit inheritance and discard the inherited rules</maml:title>
<dev:code>PS C:\&gt; Disable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveInheritedAccessRules -PassThru</dev:code>
<dev:code>PS C:\&gt; Disable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveInheritedAuditRules -PassThru</dev:code>
<dev:remarks>
<maml:para>This command protects the SACL and removes the inherited audit rules instead of copying them, so the folder is audited only by the rules that were already explicit on it. `-PassThru` returns the resulting state, in which `AuditInheritanceEnabled` is `$false`.</maml:para>
</dev:remarks>
@ -3116,7 +3116,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</command:details>
<maml:description>
<maml:para>The `Enable-NTFSAuditInheritance` cmdlet removes the protection from the system access control list (SACL) of a file or folder, so that the item inherits audit rules from its parent folder again.</maml:para>
<maml:para>By default, the audit rules that are stored directly on the item are kept, and the inherited rules are added to them. An item that was processed by `Disable-NTFSAuditInheritance` therefore ends up with the inherited audit rules twice: once as the explicit copies that were created when inheritance was blocked, and once as true inherited rules. The `-RemoveExplicitAccessRules` switch deletes every audit rule that is stored directly on the item, which leaves only the inherited ones. Despite its name, the switch acts on audit rules, not on access rules.</maml:para>
<maml:para>By default, the audit rules that are stored directly on the item are kept, and the inherited rules are added to them. An item that was processed by `Disable-NTFSAuditInheritance` therefore ends up with the inherited audit rules twice: once as the explicit copies that were created when inheritance was blocked, and once as true inherited rules. The `-RemoveExplicitAuditRules` switch deletes every audit rule that is stored directly on the item, which leaves only the inherited ones. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`; that name still works as an alias.</maml:para>
<maml:para>In the `Path` parameter set the cmdlet reads the audit section of the item's security descriptor, changes it, and writes it back to disk immediately. In the `SecurityDescriptor` parameter set it changes the `Security2.FileSystemSecurity2` object in memory only; nothing reaches the file system until you pass that object to `Set-NTFSSecurityDescriptor`.</maml:para>
<maml:para>Reading and writing the audit section requires the Security privilege, so run this cmdlet in an elevated session. `-Path` accepts pipeline input by value and by property name through its `FullName` alias, so the output of `Get-ChildItem`, `Get-ChildItem2`, `Get-Item2`, and `Get-NTFSInheritance` binds to it. The cmdlet affects only the audit rules; use `Enable-NTFSAccessInheritance` for the access rules.</maml:para>
</maml:description>
@ -3146,10 +3146,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveExplicitAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveExplicitAccessRules">
<maml:name>RemoveExplicitAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier.</maml:para>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`, which remains an alias.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -3185,10 +3185,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveExplicitAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveExplicitAccessRules">
<maml:name>RemoveExplicitAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier.</maml:para>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`, which remains an alias.</maml:para>
</maml:description>
<dev:type>
<maml:name>SwitchParameter</maml:name>
@ -3223,10 +3223,10 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>RemoveExplicitAccessRules</maml:name>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="RemoveExplicitAccessRules">
<maml:name>RemoveExplicitAuditRules</maml:name>
<maml:description>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. Despite its name, the switch acts on the audit rules in the SACL, not on access rules. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier.</maml:para>
<maml:para>Indicates that every audit rule stored directly on the item is removed when inheritance is restored, so that the item ends up with the inherited audit rules only. By default, when the switch is omitted, the explicit audit rules are kept and the inherited rules are added to them, which usually duplicates the rules that `Disable-NTFSAuditInheritance` copied earlier. Before 5.0.0, the switch was named `-RemoveExplicitAccessRules`, which remains an alias.</maml:para>
</maml:description>
<command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
<dev:type>
@ -3298,14 +3298,14 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</command:example>
<command:example>
<maml:title>Example 2: Restore audit inheritance and drop the explicit rules</maml:title>
<dev:code>PS C:\&gt; Enable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveExplicitAccessRules -PassThru</dev:code>
<dev:code>PS C:\&gt; Enable-NTFSAuditInheritance -Path C:\Data\Projects -RemoveExplicitAuditRules -PassThru</dev:code>
<dev:remarks>
<maml:para>This command removes every audit rule that is stored directly on the folder and lets it inherit from `C:\Data` again, so the folder is audited exactly like its parent. `-PassThru` returns the resulting state, in which `AuditInheritanceEnabled` is `$true`.</maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>------------ Example 3: Repair a whole folder tree ------------</maml:title>
<dev:code>PS C:\&gt; Get-ChildItem2 -Path C:\Data -Recurse | Get-NTFSInheritance | Where-Object { $_.AuditInheritanceEnabled -eq $false } | Enable-NTFSAuditInheritance -RemoveExplicitAccessRules</dev:code>
<dev:code>PS C:\&gt; Get-ChildItem2 -Path C:\Data -Recurse | Get-NTFSInheritance | Where-Object { $_.AuditInheritanceEnabled -eq $false } | Enable-NTFSAuditInheritance -RemoveExplicitAuditRules</dev:code>
<dev:remarks>
<maml:para>This command finds every item below `C:\Data` whose audit inheritance is blocked and restores it. The comparison with `$false` is deliberate: `AuditInheritanceEnabled` is `$null` for items whose audit section could not be read, and those items are skipped instead of being processed.</maml:para>
</dev:remarks>
@ -3313,7 +3313,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:example>
<maml:title>------ Example 4: Change a security descriptor in memory ------</maml:title>
<dev:code>PS C:\&gt; $sd = Get-NTFSSecurityDescriptor -Path C:\Data\Projects
PS C:\&gt; Enable-NTFSAuditInheritance -SecurityDescriptor $sd -RemoveExplicitAccessRules
PS C:\&gt; Enable-NTFSAuditInheritance -SecurityDescriptor $sd -RemoveExplicitAuditRules
PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<dev:remarks>
<maml:para>The first two commands read the security descriptor and restore audit inheritance in memory, which does not change anything on disk. The third command writes the descriptor back and applies the change.</maml:para>
@ -4082,7 +4082,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="2" aliases="none">
<maml:name>Algorithm</maml:name>
<maml:description>
<maml:para>Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`.</maml:para>
<maml:para>Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`. `RIPEMD160` and `MACTripleDES` are available only in Windows PowerShell 5.1, and `MACTripleDES` is deprecated.</maml:para>
</maml:description>
<command:parameterValueGroup>
<command:parameterValue required="false" command:variableLength="false">SHA1</command:parameterValue>
@ -4106,7 +4106,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="2" aliases="none">
<maml:name>Algorithm</maml:name>
<maml:description>
<maml:para>Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`.</maml:para>
<maml:para>Specifies the hash algorithm to use. The accepted values are `SHA1`, `SHA256`, `SHA384`, `SHA512`, `MACTripleDES`, `MD5`, and `RIPEMD160`. When you omit this parameter, the cmdlet uses `SHA256`. `RIPEMD160` and `MACTripleDES` are available only in Windows PowerShell 5.1, and `MACTripleDES` is deprecated.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">HashAlgorithms</command:parameterValue>
<dev:type>
@ -4158,10 +4158,10 @@ PS C:\&gt; Disable-Privileges</dev:code>
</command:returnValues>
<maml:alertSet>
<maml:alert>
<maml:para>The cmdlet works only in Windows PowerShell. In PowerShell 7, it fails for every algorithm with the error `Could not load type 'System.Security.Cryptography.RIPEMD160'`, because .NET no longer includes the RIPEMD-160 implementation that the cmdlet references. In PowerShell 7, use the built-in `Get-FileHash` cmdlet instead.</maml:para>
<maml:para>In PowerShell 7, the cmdlet supports `SHA1`, `SHA256`, `SHA384`, `SHA512`, and `MD5`. .NET no longer includes `RIPEMD160` and `MACTripleDES`, so requesting one of them there stops the cmdlet with the error `HashAlgorithmNotAvailable`, which names the algorithm; use Windows PowerShell 5.1 to calculate those hashes. Before 5.0.0, the cmdlet failed in PowerShell 7 for every algorithm with the error `Could not load type 'System.Security.Cryptography.RIPEMD160'`.</maml:para>
<maml:para>If the file cannot be opened because access is denied, the cmdlet takes ownership of the file with the account that runs it, calculates the hash, and restores the previous owner afterward. That fallback fails with a `GetHashError` when the account is not allowed to change the owner of the file. A file that cannot be read produces a `GetHashError` and no result.</maml:para>
<maml:para>The hash is returned as an uppercase hexadecimal string without separators, which differs from the lowercase output of some other hashing tools. Compare hash values case-insensitively.</maml:para>
<maml:para>`MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files.</maml:para>
<maml:para>`MACTripleDES` is a keyed message authentication code that is created with a key that is generated for each call, so its result is not reproducible across invocations and is not suitable for comparing files. The value is deprecated: the cmdlet writes a warning when you use it, and a future version will remove it.</maml:para>
<maml:para>Before 5.0.0, a folder in a `-Path` array stopped the processing of that array, so the files that followed the folder were not hashed, and a file that could not be read got a result with the hash of the previous file.</maml:para>
</maml:alert>
</maml:alertSet>
@ -9334,7 +9334,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
</command:details>
<maml:description>
<maml:para>The `Set-NTFSInheritance` cmdlet turns the inheritance of access rules and audit rules on or off in a single call. It reads the current state of the item first and changes a section only when the requested value differs from the current one, which makes the cmdlet suitable for repeatedly applying a desired state to a folder tree.</maml:para>
<maml:para>The cmdlet performs the same operations as `Enable-NTFSAccessInheritance`, `Disable-NTFSAccessInheritance`, `Enable-NTFSAuditInheritance`, and `Disable-NTFSAuditInheritance`, but it does not expose their switches and it does not use their defaults. `-AccessInheritanceEnabled $false` discards the inherited access rules instead of copying them into the item's own DACL, `-AccessInheritanceEnabled $true` keeps the explicit access rules, `-AuditInheritanceEnabled $false` copies the inherited audit rules into the item's own SACL, and `-AuditInheritanceEnabled $true` removes the explicit audit rules. Use the individual Enable and Disable cmdlets when you need the opposite behavior.</maml:para>
<maml:para>The cmdlet performs the same operations as `Enable-NTFSAccessInheritance`, `Disable-NTFSAccessInheritance`, `Enable-NTFSAuditInheritance`, and `Disable-NTFSAuditInheritance`, but it does not expose their switches; it uses their defaults instead. `-AccessInheritanceEnabled $false` copies the inherited access rules into the item's own DACL, `-AccessInheritanceEnabled $true` keeps the explicit access rules, `-AuditInheritanceEnabled $false` copies the inherited audit rules into the item's own SACL, and `-AuditInheritanceEnabled $true` keeps the explicit audit rules. To remove the rules instead, use `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` or `Enable-NTFSAuditInheritance -RemoveExplicitAuditRules`. Before 5.0.0, `-AccessInheritanceEnabled $false` discarded the inherited access rules, and `-AuditInheritanceEnabled $true` removed the explicit audit rules. Review scripts that used `-AccessInheritanceEnabled $false` to drop the inherited access rules: they now keep them, which leaves broader access in place; `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` gives the old result.</maml:para>
<maml:para>Omit `-AccessInheritanceEnabled` or `-AuditInheritanceEnabled` to leave that section unchanged. Changing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
<maml:para>In the `Path` parameter set the cmdlet writes each changed section back to disk immediately. In the `SecurityDescriptor` parameter set it changes the `Security2.FileSystemSecurity2` object in memory only; nothing reaches the file system until you pass that object to `Set-NTFSSecurityDescriptor`. `-Path`, `-AccessInheritanceEnabled`, and `-AuditInheritanceEnabled` all accept pipeline input by property name, so a `Security2.FileSystemInheritanceInfo` object from `Get-NTFSInheritance` binds to all three at once.</maml:para>
</maml:description>
@ -9356,7 +9356,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AccessInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and discards the rules the item currently inherits, which leaves only its explicit rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and copies the rules the item currently inherits into it, so the effective permissions stay the same. Before 5.0.0, `$false` discarded the inherited rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9368,7 +9368,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AuditInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and removes the audit rules that are stored directly on the item; `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and keeps the audit rules that are stored directly on the item (before 5.0.0, it removed them); `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9408,7 +9408,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AccessInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and discards the rules the item currently inherits, which leaves only its explicit rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and copies the rules the item currently inherits into it, so the effective permissions stay the same. Before 5.0.0, `$false` discarded the inherited rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9420,7 +9420,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AuditInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and removes the audit rules that are stored directly on the item; `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and keeps the audit rules that are stored directly on the item (before 5.0.0, it removed them); `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9446,7 +9446,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AccessInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and discards the rules the item currently inherits, which leaves only its explicit rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
<maml:para>Specifies whether the item inherits access rules from its parent folder. `$true` removes the protection from the DACL and keeps the access rules that are stored directly on the item; `$false` protects the DACL and copies the rules the item currently inherits into it, so the effective permissions stay the same. Before 5.0.0, `$false` discarded the inherited rules. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the access section is left unchanged.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9458,7 +9458,7 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName)" position="named" aliases="none">
<maml:name>AuditInheritanceEnabled</maml:name>
<maml:description>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and removes the audit rules that are stored directly on the item; `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
<maml:para>Specifies whether the item inherits audit rules from its parent folder. `$true` removes the protection from the SACL and keeps the audit rules that are stored directly on the item (before 5.0.0, it removed them); `$false` protects the SACL and copies the inherited audit rules into it. The section is left untouched when the requested value already matches the current state. When you omit the parameter, the audit section is left unchanged. Reading and writing the audit section requires the Security privilege and therefore an elevated session.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">Boolean</command:parameterValue>
<dev:type>
@ -9558,14 +9558,14 @@ PS C:\&gt; Set-NTFSSecurityDescriptor -SecurityDescriptor $sd</dev:code>
<maml:title>-------- Example 1: Block inheritance of both sections --------</maml:title>
<dev:code>PS C:\&gt; Set-NTFSInheritance -Path C:\Data\Projects -AccessInheritanceEnabled $false -AuditInheritanceEnabled $false</dev:code>
<dev:remarks>
<maml:para>This command protects the DACL and the SACL of `C:\Data\Projects`. The inherited access rules are discarded, so make sure the folder has explicit access rules of its own; the inherited audit rules are copied into the folder's SACL. Changing the audit section requires an elevated session.</maml:para>
<maml:para>This command protects the DACL and the SACL of `C:\Data\Projects`. The inherited access and audit rules are copied into the folder's DACL and SACL, so the effective permissions and the auditing stay the same. Changing the audit section requires an elevated session.</maml:para>
</dev:remarks>
</command:example>
<command:example>
<maml:title>------- Example 2: Restore inheritance of both sections -------</maml:title>
<dev:code>PS C:\&gt; Set-NTFSInheritance -Path C:\Data\Projects -AccessInheritanceEnabled $true -AuditInheritanceEnabled $true -PassThru</dev:code>
<dev:remarks>
<maml:para>This command lets the folder inherit from `C:\Data` again. The explicit access rules are kept, the explicit audit rules are removed, and `-PassThru` returns the resulting state.</maml:para>
<maml:para>This command lets the folder inherit from `C:\Data` again. The explicit access and audit rules are kept, and `-PassThru` returns the resulting state.</maml:para>
</dev:remarks>
</command:example>
<command:example>

74
Security2/FileSystem/FileInfo/Extensions.cs

@ -1,4 +1,6 @@
using System.Text;
using System;
using System.Security.Cryptography;
using System.Text;
namespace Security2.FileSystem.FileInfo
{
@ -15,48 +17,60 @@ namespace Security2.FileSystem.FileInfo
public static class Extensions
{
public static string GetHash(this Alphaleonis.Win32.Filesystem.FileInfo file, HashAlgorithms algorithm)
{
byte[] hash = null;
using (var hashAlgorithm = CreateHashAlgorithm(algorithm))
using (var fileStream = file.OpenRead())
{
switch (algorithm)
{
case HashAlgorithms.MD5:
hash = System.Security.Cryptography.MD5.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.SHA1:
hash = System.Security.Cryptography.SHA1.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.SHA256:
hash = System.Security.Cryptography.SHA256.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.SHA384:
hash = System.Security.Cryptography.SHA384.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.SHA512:
hash = System.Security.Cryptography.SHA512.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.MACTripleDES:
hash = System.Security.Cryptography.MACTripleDES.Create().ComputeHash(fileStream);
break;
case HashAlgorithms.RIPEMD160:
hash = System.Security.Cryptography.RIPEMD160.Create().ComputeHash(fileStream);
break;
}
fileStream.Close();
hash = hashAlgorithm.ComputeHash(fileStream);
}
var sb = new StringBuilder(hash.Length);
var sb = new StringBuilder(hash.Length * 2);
for (var i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}
/// <summary>
/// Creates the hash algorithm. Throws a PlatformNotSupportedException that names the algorithm when the
/// .NET runtime lacks it, as .NET Core and later lack RIPEMD160 and MACTripleDES.
/// </summary>
/// <param name="algorithm">The hash algorithm to create.</param>
/// <returns>A new instance of the hash algorithm.</returns>
public static HashAlgorithm CreateHashAlgorithm(HashAlgorithms algorithm)
{
switch (algorithm)
{
case HashAlgorithms.MD5:
return MD5.Create();
case HashAlgorithms.SHA1:
return SHA1.Create();
case HashAlgorithms.SHA256:
return SHA256.Create();
case HashAlgorithms.SHA384:
return SHA384.Create();
case HashAlgorithms.SHA512:
return SHA512.Create();
case HashAlgorithms.MACTripleDES:
case HashAlgorithms.RIPEMD160:
// Created by name: a reference to the type would make every hash fail where the type is missing.
var hashAlgorithm = CryptoConfig.CreateFromName(algorithm.ToString()) as HashAlgorithm;
if (hashAlgorithm == null)
{
throw new PlatformNotSupportedException(string.Format(
"The hash algorithm '{0}' is not available in this version of .NET. Use Windows PowerShell 5.1 to calculate it.",
algorithm));
}
return hashAlgorithm;
default:
throw new ArgumentOutOfRangeException("algorithm", algorithm, "Unknown hash algorithm.");
}
}
}
}

17
Tests/Access.Tests.ps1

@ -285,3 +285,20 @@ Describe 'Security descriptor parameter sets' {
$rule.InheritanceFlags | Should -Be ([System.Security.AccessControl.InheritanceFlags]::None)
}
}
Describe 'Clear-NTFSAccess' {
Context 'With -DisableInheritance' {
# The cmdlet does not copy the inherited entries, so the item is left with an empty DACL. This is the
# documented behavior; Set-NTFSInheritance and Disable-NTFSAccessInheritance keep the entries.
It 'Should leave the item with an empty, protected DACL' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'ClearAll'
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
Clear-NTFSAccess -Path $file -DisableInheritance
$acl = Get-Acl -LiteralPath $file
$acl.AreAccessRulesProtected | Should -BeTrue
$acl.Access | Should -BeNullOrEmpty
}
}
}

46
Tests/FileHash.Tests.ps1

@ -9,6 +9,7 @@ param ()
BeforeDiscovery {
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
$isElevated = Test-IsElevated
$isCore = $PSVersionTable.PSEdition -eq 'Core'
}
BeforeAll {
@ -34,12 +35,43 @@ AfterAll {
}
Describe 'Get-FileHash2' {
Context 'Algorithms' {
# Before 5.0.0, the cmdlet failed in PowerShell 7 for every algorithm, because it referenced RIPEMD160.
It 'Should return the hash of Get-FileHash for <_>' -ForEach @('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5') {
$result = Get-FileHash2 -Path $first -Algorithm $_
$result.Hash | Should -BeExactly (Get-FileHash -LiteralPath $first -Algorithm $_).Hash
$result.Algorithm | Should -Be $_
}
It 'Should calculate RIPEMD160 in Windows PowerShell' -Skip:$isCore {
$expected = [BitConverter]::ToString(
[System.Security.Cryptography.RIPEMD160]::Create().ComputeHash([IO.File]::ReadAllBytes($first))
).Replace('-', '')
(Get-FileHash2 -Path $first -Algorithm RIPEMD160).Hash | Should -BeExactly $expected
}
It 'Should stop with an error that names <_> in PowerShell 7' -Skip:(-not $isCore) -ForEach @('RIPEMD160', 'MACTripleDES') {
$algorithm = $_
$hashError = { Get-FileHash2 -Path $first -Algorithm $algorithm -ErrorAction Stop } |
Should -Throw -ExpectedMessage "*'$algorithm'*Windows PowerShell 5.1*" -PassThru
$hashError.FullyQualifiedErrorId | Should -BeLike 'HashAlgorithmNotAvailable,*'
}
It 'Should warn once that MACTripleDES is deprecated' -Skip:$isCore {
$results = @(Get-FileHash2 -Path $first, $second -Algorithm MACTripleDES -WarningVariable hashWarnings -WarningAction SilentlyContinue)
$results | Should -HaveCount 2
$results[0].Hash | Should -Not -BeNullOrEmpty
$hashWarnings | Should -HaveCount 1
$hashWarnings[0].Message | Should -BeLike '*MACTripleDES*random key*deprecated*'
}
}
Context 'When -Path contains a folder' {
It 'Should skip the folder and hash the files that follow it' {
if ($PSVersionTable.PSEdition -eq 'Core') {
Set-ItResult -Skipped -Because 'Get-FileHash2 fails in PowerShell 7 until it no longer references RIPEMD160'
}
$results = @(Get-FileHash2 -Path $first, $folder, $second -ErrorVariable hashErrors -ErrorAction SilentlyContinue)
$hashErrors | Should -BeNullOrEmpty
@ -51,9 +83,6 @@ Describe 'Get-FileHash2' {
Context 'When a file cannot be read' {
# Before 5.0.0, the cmdlet wrote a result for the file anyway, with the hash of the previous file.
It 'Should write an error and no result for the file' {
if ($PSVersionTable.PSEdition -eq 'Core') {
Set-ItResult -Skipped -Because 'Get-FileHash2 fails in PowerShell 7 until it no longer references RIPEMD160'
}
$locked = New-TestSandboxItem -Sandbox $sandbox -Name 'Locked'
$stream = [IO.File]::Open($locked, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::None)
try {
@ -72,9 +101,6 @@ Describe 'Get-FileHash2' {
# Before 5.0.0, the account that ran the cmdlet stayed the owner when the second attempt failed. Only an
# elevated process can make another account the owner first, so the test runs in CI.
It 'Should restore the previous owner' -Skip:(-not $isElevated) {
if ($PSVersionTable.PSEdition -eq 'Core') {
Set-ItResult -Skipped -Because 'Get-FileHash2 fails in PowerShell 7 until it no longer references RIPEMD160'
}
$denied = New-TestSandboxItem -Sandbox $sandbox -Name 'Denied'
Assert-TestSandboxPath -Sandbox $sandbox -Path $denied
Set-NTFSOwner -Path $denied -Account 'S-1-5-32-544'

67
Tests/Inheritance.Tests.ps1

@ -108,6 +108,46 @@ Describe 'Inheritance cmdlets with -PassThru' {
}
Describe 'Set-NTFSInheritance' {
Context 'When it changes the inheritance' {
# Before 5.0.0, -AccessInheritanceEnabled $false removed the inherited access entries and
# -AuditInheritanceEnabled $true removed the explicit audit entries, unlike the dedicated cmdlets.
It 'Should keep the inherited access entries as explicit ones when it disables access inheritance' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'KeepAccess'
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
$inheritedCount = @((Get-Acl -LiteralPath $file).Access | Where-Object -Property IsInherited).Count
Set-NTFSInheritance -Path $file -AccessInheritanceEnabled $false
$acl = Get-Acl -LiteralPath $file
$acl.AreAccessRulesProtected | Should -BeTrue
@($acl.Access | Where-Object -Property IsInherited -EQ -Value $false) | Should -HaveCount $inheritedCount
}
# In memory, the kept entries stay marked as inherited; Windows stores them as explicit ones on write.
It 'Should keep the inherited access entries of a security descriptor' {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'KeepDescriptor'
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
$sd = Get-NTFSSecurityDescriptor -Path $file
$sidType = [System.Security.Principal.SecurityIdentifier]
$inheritedCount = @($sd.SecurityDescriptor.GetAccessRules($false, $true, $sidType)).Count
Set-NTFSInheritance -SecurityDescriptor $sd -AccessInheritanceEnabled $false
$sd.SecurityDescriptor.AreAccessRulesProtected | Should -BeTrue
@($sd.SecurityDescriptor.GetAccessRules($true, $true, $sidType)) | Should -HaveCount $inheritedCount
}
It 'Should keep the explicit audit entries when it enables audit inheritance' -Skip:(-not $canChangeAudit) {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'KeepAudit'
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
Add-NTFSAudit -Path $file -Account 'Everyone' -AccessRights Delete -AuditFlags Failure
Disable-NTFSAuditInheritance -Path $file
Set-NTFSInheritance -Path $file -AuditInheritanceEnabled $true
@(Get-NTFSAudit -Path $file -ExcludeInherited) | Should -HaveCount 1
}
}
Context 'When -AccessInheritanceEnabled or -AuditInheritanceEnabled is omitted' {
BeforeEach {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'File'
@ -149,3 +189,30 @@ Describe 'Set-NTFSInheritance' {
}
}
}
Describe 'Audit inheritance switches' {
# Before 5.0.0, the switches were named after access entries, although they remove audit entries.
It '<Command> should take -<Name> with the alias -<Alias>' -ForEach @(
@{ Command = 'Disable-NTFSAuditInheritance'; Name = 'RemoveInheritedAuditRules'; Alias = 'RemoveInheritedAccessRules' }
@{ Command = 'Enable-NTFSAuditInheritance'; Name = 'RemoveExplicitAuditRules'; Alias = 'RemoveExplicitAccessRules' }
) {
$parameter = (Get-Command -Name $Command).Parameters[$Name]
$parameter | Should -Not -BeNullOrEmpty
$parameter.SwitchParameter | Should -BeTrue
$parameter.Aliases | Should -Contain $Alias
}
It '<Command> should bind -<Switch>' -ForEach @(
@{ Command = 'Disable-NTFSAuditInheritance'; Switch = 'RemoveInheritedAuditRules' }
@{ Command = 'Disable-NTFSAuditInheritance'; Switch = 'RemoveInheritedAccessRules' }
@{ Command = 'Enable-NTFSAuditInheritance'; Switch = 'RemoveExplicitAuditRules' }
@{ Command = 'Enable-NTFSAuditInheritance'; Switch = 'RemoveExplicitAccessRules' }
) {
$file = New-TestSandboxItem -Sandbox $sandbox -Name 'AuditSwitch'
Assert-TestSandboxPath -Sandbox $sandbox -Path $file
$parameters = @{ Path = $file; $Switch = $true }
{ & $Command @parameters -ErrorAction SilentlyContinue } | Should -Not -Throw
}
}

2
Tests/OutputTypes.Tests.ps1

@ -99,4 +99,4 @@ Describe 'Cmdlet classes' {
(Get-Command -Name 'Remove-Item2').ImplementingType.GetField('filter', $flags) | Should -BeNullOrEmpty
}
}
}

Loading…
Cancel
Save