From 594f737597701cca8c820de6afd0c49e19ed142d Mon Sep 17 00:00:00 2001 From: spydisec <9101327+spydisec@users.noreply.github.com> Date: Fri, 2 Oct 2026 12:21:56 +1000 Subject: [PATCH 1/2] Test: flag PowerShell transcription enabled with no OutputDirectory (#91) A read-only Safety row, in the same style as the CrashOnAuditFail check. The kit never sets the transcription policy (removed in 2.0.0, #36), but another policy might; on with no OutputDirectory, every Windows PowerShell session writes a transcript into the user's Documents folder, which Microsoft documents as the policy's default. That state fails; on with a folder passes with a note; off passes. Uncategorised, so it drives the exit code without landing in a behaviour category. Get-TranscriptionPolicyState in WinLogKit.Common.ps1 does the read, so the three states and the Wow6432Node note are unit-tested with Get-RegValue mocked. Safety never-do table gains a row; CHANGELOG Unreleased entry. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 4 ++++ Test-LoggingBaseline.ps1 | 16 +++++++++++++++ WinLogKit.Common.ps1 | 19 +++++++++++++++++ docs/safety.md | 1 + tests/Kit.Tests.ps1 | 44 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1ba39..f7f2bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Releases are tagged `vX.Y.Z` and published with a zip and a SHA256 checksum. ## [Unreleased] +### Added + +- 🧾 **Transcription policy check.** `Test-LoggingBaseline.ps1` now has a read-only Safety row for the PowerShell transcription policy, which the kit never sets. On with no `OutputDirectory` fails, because every session then writes a transcript into the user's Documents folder; on with a folder passes with a note; off passes. The Safety never-do table explains it. [#91](https://github.com/spydisec/WinLogKit/issues/91) + ## [2.2.2] - 2026-09-24 Documentation only: one checklist for fitting the kit to your own infrastructure before rolling it out. No script or setting changes; nothing to do when upgrading from 2.2.1. diff --git a/Test-LoggingBaseline.ps1 b/Test-LoggingBaseline.ps1 index ed85a44..2e01544 100644 --- a/Test-LoggingBaseline.ps1 +++ b/Test-LoggingBaseline.ps1 @@ -244,6 +244,22 @@ if ($null -eq $crash -or "$crash" -eq '0') { Add-Row @('Logging tampered with') 'Safety' $crashLabel '0 or absent' "$crash" 'FAIL' 'CrashOnAuditFail is on: the host halts when the Security log fills. Set by another policy; the kit never changes it. See Safety (never-do list).' } +# PowerShell transcription (#91): the kit never sets it, but another policy +# might. On with no OutputDirectory, every session writes a transcript into +# the user's Documents folder (secrets typed on a command line included), so +# that state fails. On with a folder is someone's deliberate choice: PASS +# with a note. Uncategorised: it is a data-exposure finding, not a logging +# one, so it drives the exit code without landing in a behaviour category. +$tx = Get-TranscriptionPolicyState +$txLabel = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription\EnableTranscripting' +$txWow = '' +if ($tx.Wow6432NodeOn -and $tx.State -eq 'Off') { $txWow = ' The Wow6432Node copy of the key enables it; check that path too.' } +switch ($tx.State) { + 'Off' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' '' 'PASS' $txWow.Trim() } + 'Directed' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' "1, OutputDirectory=$($tx.OutputDirectory)" 'PASS' 'Transcription is on and directed to a folder. Set by another policy; the kit never changes it. Keep that folder readable only by the people who need the transcripts.' } + 'Undirected' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' '1, no OutputDirectory' 'FAIL' 'Transcription is on with no OutputDirectory: every Windows PowerShell session writes a transcript into the user''s Documents folder, including anything typed on a command line. Set by another policy, not the kit. Either set an OutputDirectory with restricted access, or remove the Transcription policy. See Safety (never-do list).' } +} + # ----------------------- SMB auditing (Windows 11 24H2 / Server 2025+) ------ $smbState = Get-SmbAuditState diff --git a/WinLogKit.Common.ps1 b/WinLogKit.Common.ps1 index 4c5990d..2a4d206 100644 --- a/WinLogKit.Common.ps1 +++ b/WinLogKit.Common.ps1 @@ -140,6 +140,25 @@ function Test-RetentionForcedByPolicy { return ("$v" -eq '1') } +# The PowerShell transcription policy (#91). The kit never sets it (removed +# in 2.0.0, #36), but another policy might. Enabled with no OutputDirectory, +# every Windows PowerShell session writes a transcript into the user's +# Documents folder: Microsoft documents that as the policy's default and +# says to restrict access to the output location. Returns a hashtable: +# State Off | Directed (on, folder set) | Undirected (on, no folder) +# OutputDirectory the configured folder, or '' +# Wow6432NodeOn whether the Wow6432Node copy of the key also enables it +# Read-only; compared as text so DWORD and string 1 read the same. +function Get-TranscriptionPolicyState { + $base = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' + $on = ("$(Get-RegValue -Path $base -Name 'EnableTranscripting')" -eq '1') + $dir = "$(Get-RegValue -Path $base -Name 'OutputDirectory')".Trim() + $wow = ("$(Get-RegValue -Path 'HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\Transcription' -Name 'EnableTranscripting')" -eq '1') + $state = 'Off' + if ($on) { if ($dir -ne '') { $state = 'Directed' } else { $state = 'Undirected' } } + return @{ State = $state; OutputDirectory = $dir; Wow6432NodeOn = $wow } +} + # The Set-/Get-Smb*Configuration property an SMB audit item maps to: its # Setting when it has one (the same setting exists on client and server, # and Ids must be unique), otherwise its Id. Works for settings-table diff --git a/docs/safety.md b/docs/safety.md index b729c14..9cfe76b 100644 --- a/docs/safety.md +++ b/docs/safety.md @@ -53,6 +53,7 @@ kit never touches them, in any mode: | Global object access auditing | SACLs on every kernel/file/registry object - extreme volume, measurable performance degradation. | | Blanket File System / Registry SACLs | A careless wildcard SACL can bury a file server. Scoping SACLs is a design decision, never a default. | | Shrinking logs, rebooting, restarting services | Sizes are only raised; the one restart-requiring setting (AD CS AuditFilter) is set with a warning and left to your change window. | +| PowerShell transcription | Writes text files outside the event log; removed from the kit in 2.0.0 (script block logging already records the code that ran). If another policy turns it on **without an `OutputDirectory`**, every Windows PowerShell session writes a transcript into the user's Documents folder, which Microsoft documents as the policy's default behaviour ([Policy CSP](https://learn.microsoft.com/windows/client-management/mdm/policy-csp-admx-powershellexecutionpolicy#enabletranscripting)). The test **fails** that state and passes transcription that is directed to a folder. If you want transcripts, set them by GPO to a central folder readable only by the people who need them. | ## Volume-impact settings (the HighVolume tier and friends) diff --git a/tests/Kit.Tests.ps1 b/tests/Kit.Tests.ps1 index 65919c4..03f43fd 100644 --- a/tests/Kit.Tests.ps1 +++ b/tests/Kit.Tests.ps1 @@ -116,7 +116,7 @@ Describe 'Scripts' { $expected = @('Test-IsAdmin', 'Get-DomainRole', 'Test-PowerShell7Installed', 'Get-OsType', 'ConvertTo-NetRegPath', 'Get-RegValue', 'ConvertFrom-AuditPolicyBackup', 'Get-AuditPolicyByGuid', 'Get-AuditSettingValue', 'Format-AuditSetting', 'Get-SmbAuditState', 'Get-BaselineItemKeySet', 'Import-BaselineSelection', 'Test-ReferenceBaselineItem', 'Write-IncludeOptionalWarning', - 'Test-TierSelected', 'Resolve-BaselineSelection', 'Test-ItemSelected') + 'Test-TierSelected', 'Resolve-BaselineSelection', 'Test-ItemSelected', 'Get-TranscriptionPolicyState') $notInCommon = @($expected | Where-Object { -not $Defs.ContainsKey($_) -or (($Defs[$_] -join ';') -ne 'WinLogKit.Common.ps1') }) $notInCommon | Should -BeNullOrEmpty } @@ -397,6 +397,48 @@ Describe 'Audit integrity' { Select-String -Path (Join-Path $KitRoot 'Test-LoggingBaseline.ps1') -Pattern "-Name 'CrashOnAuditFail'" -Quiet | Should -BeTrue } } +# #91: transcription on with no OutputDirectory fills every user's Documents +# folder. The kit never sets it; Test reports it. +Describe 'Transcription policy check' { + It 'never sets the transcription policy' { + @($BaselineRegistrySettings | Where-Object { $_.Path -like '*\PowerShell\Transcription' }) | Should -BeNullOrEmpty + } + + It 'reads Off when the value is absent or 0' { + Mock Get-RegValue { $null } + (Get-TranscriptionPolicyState).State | Should -Be 'Off' + Mock Get-RegValue { 0 } + (Get-TranscriptionPolicyState).State | Should -Be 'Off' + } + + It 'reads Directed when on with an OutputDirectory, and keeps the folder' { + Mock Get-RegValue { if ($Name -eq 'OutputDirectory') { 'D:\Transcripts' } else { 1 } } + $s = Get-TranscriptionPolicyState + $s.State | Should -Be 'Directed' + $s.OutputDirectory | Should -Be 'D:\Transcripts' + } + + It 'reads Undirected when on with no OutputDirectory, DWORD or string' { + Mock Get-RegValue { if ($Name -eq 'OutputDirectory') { $null } else { 1 } } + (Get-TranscriptionPolicyState).State | Should -Be 'Undirected' + Mock Get-RegValue { if ($Name -eq 'OutputDirectory') { ' ' } else { '1' } } + (Get-TranscriptionPolicyState).State | Should -Be 'Undirected' + } + + It 'notes the Wow6432Node copy separately' { + Mock Get-RegValue { if ($Path -like '*Wow6432Node*') { 1 } else { $null } } + $s = Get-TranscriptionPolicyState + $s.State | Should -Be 'Off' + $s.Wow6432NodeOn | Should -BeTrue + } + + It 'is assessed by Test as an uncategorised Safety row that fails only when Undirected' { + $src = Get-Content (Join-Path $KitRoot 'Test-LoggingBaseline.ps1') -Raw + $src | Should -Match 'Get-TranscriptionPolicyState' + $src | Should -Match "'Undirected' \{ Add-Row @\(\) 'Safety' .*'FAIL'" + $src | Should -Match "'Directed' \{ Add-Row @\(\) 'Safety' .*'PASS'" + } +} # #74: Test tells you when the AppLocker logs can't record anything. Describe 'AppLocker readiness note' { It 'checks for an effective AppLocker policy, read-only, and never fails on it' { From 60177058b45f2cc946d521c925e0e063a4fb88b9 Mon Sep 17 00:00:00 2001 From: spydisec <9101327+spydisec@users.noreply.github.com> Date: Fri, 2 Oct 2026 12:33:45 +1000 Subject: [PATCH 2/2] Transcription check: read the user policy when the machine hive is silent; note Wow6432Node both ways CodeRabbit on #92. The policy exists under Computer and User Configuration with Computer taking precedence, so a user-only policy with no folder was reported Off. The helper now reads HKCU only when HKLM has no value and reports which hive decided. The Wow6432Node copy is returned as text and noted whenever it is present and disagrees with the effective state, in either direction; absent stays distinct from 0. ConvertTo-NetRegPath maps HKCU: too. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 2 +- Test-LoggingBaseline.ps1 | 17 ++++++++++++----- WinLogKit.Common.ps1 | 35 +++++++++++++++++++++++++---------- docs/safety.md | 2 +- tests/Kit.Tests.ps1 | 28 ++++++++++++++++++++++++++-- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f2bc1..f40f517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Releases are tagged `vX.Y.Z` and published with a zip and a SHA256 checksum. ### Added -- 🧾 **Transcription policy check.** `Test-LoggingBaseline.ps1` now has a read-only Safety row for the PowerShell transcription policy, which the kit never sets. On with no `OutputDirectory` fails, because every session then writes a transcript into the user's Documents folder; on with a folder passes with a note; off passes. The Safety never-do table explains it. [#91](https://github.com/spydisec/WinLogKit/issues/91) +- 🧾 **Transcription policy check.** `Test-LoggingBaseline.ps1` now has a read-only Safety row for the PowerShell transcription policy, which the kit never sets. On with no `OutputDirectory` fails, because every session then writes a transcript into the user's Documents folder; on with a folder passes with a note; off passes. Machine policy takes precedence; the user policy of the account running the test is read when no machine policy is set, and a `Wow6432Node` copy that disagrees is noted. The Safety never-do table explains it. [#91](https://github.com/spydisec/WinLogKit/issues/91) ## [2.2.2] - 2026-09-24 diff --git a/Test-LoggingBaseline.ps1 b/Test-LoggingBaseline.ps1 index 2e01544..1457020 100644 --- a/Test-LoggingBaseline.ps1 +++ b/Test-LoggingBaseline.ps1 @@ -252,12 +252,19 @@ if ($null -eq $crash -or "$crash" -eq '0') { # one, so it drives the exit code without landing in a behaviour category. $tx = Get-TranscriptionPolicyState $txLabel = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription\EnableTranscripting' -$txWow = '' -if ($tx.Wow6432NodeOn -and $tx.State -eq 'Off') { $txWow = ' The Wow6432Node copy of the key enables it; check that path too.' } +$txExpected = 'absent or 0, or 1 with an OutputDirectory' +$txScope = '' +if ($tx.Scope -eq 'User') { $txScope = ' (user policy of the account running this test; no machine policy is set)' } +# The Wow6432Node copy is noted whenever it is present and disagrees with +# the effective state, in either direction; an absent copy is not a mismatch. +$txNote = '' +if ($tx.Wow6432NodeValue -ne '' -and (($tx.Wow6432NodeValue -eq '1') -ne ($tx.State -ne 'Off'))) { + $txNote = " The Wow6432Node copy of the key reads EnableTranscripting=$($tx.Wow6432NodeValue), which differs; check that path too." +} switch ($tx.State) { - 'Off' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' '' 'PASS' $txWow.Trim() } - 'Directed' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' "1, OutputDirectory=$($tx.OutputDirectory)" 'PASS' 'Transcription is on and directed to a folder. Set by another policy; the kit never changes it. Keep that folder readable only by the people who need the transcripts.' } - 'Undirected' { Add-Row @() 'Safety' $txLabel 'absent or 0, or 1 with an OutputDirectory' '1, no OutputDirectory' 'FAIL' 'Transcription is on with no OutputDirectory: every Windows PowerShell session writes a transcript into the user''s Documents folder, including anything typed on a command line. Set by another policy, not the kit. Either set an OutputDirectory with restricted access, or remove the Transcription policy. See Safety (never-do list).' } + 'Off' { Add-Row @() 'Safety' $txLabel $txExpected '' 'PASS' $txNote.Trim() } + 'Directed' { Add-Row @() 'Safety' $txLabel $txExpected "1, OutputDirectory=$($tx.OutputDirectory)$txScope" 'PASS' ('Transcription is on and directed to a folder. Set by another policy; the kit never changes it. Keep that folder readable only by the people who need the transcripts.' + $txNote) } + 'Undirected' { Add-Row @() 'Safety' $txLabel $txExpected "1, no OutputDirectory$txScope" 'FAIL' ('Transcription is on with no OutputDirectory: every Windows PowerShell session writes a transcript into the user''s Documents folder, including anything typed on a command line. Set by another policy, not the kit. Either set an OutputDirectory with restricted access, or remove the Transcription policy. See Safety (never-do list).' + $txNote) } } # ----------------------- SMB auditing (Windows 11 24H2 / Server 2025+) ------ diff --git a/WinLogKit.Common.ps1 b/WinLogKit.Common.ps1 index 2a4d206..88b1dc9 100644 --- a/WinLogKit.Common.ps1 +++ b/WinLogKit.Common.ps1 @@ -52,7 +52,7 @@ function Get-OsType { # Registry access uses the .NET API throughout, not *-ItemProperty, because # one required value is literally named '*' and the ItemProperty cmdlets # treat that as a wildcard. -function ConvertTo-NetRegPath { param([string]$Path) $Path -replace '^HKLM:\\', 'HKEY_LOCAL_MACHINE\' } +function ConvertTo-NetRegPath { param([string]$Path) $Path -replace '^HKLM:\\', 'HKEY_LOCAL_MACHINE\' -replace '^HKCU:\\', 'HKEY_CURRENT_USER\' } function Get-RegValue { param([string]$Path, [string]$Name) @@ -144,19 +144,34 @@ function Test-RetentionForcedByPolicy { # in 2.0.0, #36), but another policy might. Enabled with no OutputDirectory, # every Windows PowerShell session writes a transcript into the user's # Documents folder: Microsoft documents that as the policy's default and -# says to restrict access to the output location. Returns a hashtable: -# State Off | Directed (on, folder set) | Undirected (on, no folder) -# OutputDirectory the configured folder, or '' -# Wow6432NodeOn whether the Wow6432Node copy of the key also enables it +# says to restrict access to the output location. The policy exists under +# Computer and User Configuration, Computer taking precedence, so the user +# hive (of the account running this) is read only when the machine hive has +# no value. Returns a hashtable: +# State Off | Directed (on, folder set) | Undirected (on, no folder) +# OutputDirectory the configured folder, or '' +# Scope Machine | User (which hive decided) | '' (neither configured) +# Wow6432NodeValue the Wow6432Node copy's EnableTranscripting as text, '' if absent # Read-only; compared as text so DWORD and string 1 read the same. function Get-TranscriptionPolicyState { - $base = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' - $on = ("$(Get-RegValue -Path $base -Name 'EnableTranscripting')" -eq '1') - $dir = "$(Get-RegValue -Path $base -Name 'OutputDirectory')".Trim() - $wow = ("$(Get-RegValue -Path 'HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\Transcription' -Name 'EnableTranscripting')" -eq '1') + $machine = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' + $user = 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' + $scope = 'Machine'; $path = $machine + $raw = Get-RegValue -Path $machine -Name 'EnableTranscripting' + if ($null -eq $raw) { + $scope = 'User'; $path = $user + $raw = Get-RegValue -Path $user -Name 'EnableTranscripting' + if ($null -eq $raw) { $scope = '' } + } + $on = ("$raw" -eq '1') + $dir = '' + if ($on) { $dir = "$(Get-RegValue -Path $path -Name 'OutputDirectory')".Trim() } + $wowRaw = Get-RegValue -Path 'HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\Transcription' -Name 'EnableTranscripting' + $wow = '' + if ($null -ne $wowRaw) { $wow = "$wowRaw" } $state = 'Off' if ($on) { if ($dir -ne '') { $state = 'Directed' } else { $state = 'Undirected' } } - return @{ State = $state; OutputDirectory = $dir; Wow6432NodeOn = $wow } + return @{ State = $state; OutputDirectory = $dir; Scope = $scope; Wow6432NodeValue = $wow } } # The Set-/Get-Smb*Configuration property an SMB audit item maps to: its diff --git a/docs/safety.md b/docs/safety.md index 9cfe76b..f41ca3f 100644 --- a/docs/safety.md +++ b/docs/safety.md @@ -53,7 +53,7 @@ kit never touches them, in any mode: | Global object access auditing | SACLs on every kernel/file/registry object - extreme volume, measurable performance degradation. | | Blanket File System / Registry SACLs | A careless wildcard SACL can bury a file server. Scoping SACLs is a design decision, never a default. | | Shrinking logs, rebooting, restarting services | Sizes are only raised; the one restart-requiring setting (AD CS AuditFilter) is set with a warning and left to your change window. | -| PowerShell transcription | Writes text files outside the event log; removed from the kit in 2.0.0 (script block logging already records the code that ran). If another policy turns it on **without an `OutputDirectory`**, every Windows PowerShell session writes a transcript into the user's Documents folder, which Microsoft documents as the policy's default behaviour ([Policy CSP](https://learn.microsoft.com/windows/client-management/mdm/policy-csp-admx-powershellexecutionpolicy#enabletranscripting)). The test **fails** that state and passes transcription that is directed to a folder. If you want transcripts, set them by GPO to a central folder readable only by the people who need them. | +| PowerShell transcription | Writes text files outside the event log; removed from the kit in 2.0.0 (script block logging already records the code that ran). If another policy turns it on **without an `OutputDirectory`** (machine policy, or the user policy of the account running the test when no machine policy is set), every Windows PowerShell session writes a transcript into the user's Documents folder, which Microsoft documents as the policy's default behaviour ([Policy CSP](https://learn.microsoft.com/windows/client-management/mdm/policy-csp-admx-powershellexecutionpolicy#enabletranscripting)). The test **fails** that state and passes transcription that is directed to a folder. If you want transcripts, set them by GPO to a central folder readable only by the people who need them. | ## Volume-impact settings (the HighVolume tier and friends) diff --git a/tests/Kit.Tests.ps1 b/tests/Kit.Tests.ps1 index 03f43fd..b59d825 100644 --- a/tests/Kit.Tests.ps1 +++ b/tests/Kit.Tests.ps1 @@ -425,11 +425,35 @@ Describe 'Transcription policy check' { (Get-TranscriptionPolicyState).State | Should -Be 'Undirected' } - It 'notes the Wow6432Node copy separately' { + It 'reports the Wow6432Node copy as text, absent as empty' { Mock Get-RegValue { if ($Path -like '*Wow6432Node*') { 1 } else { $null } } $s = Get-TranscriptionPolicyState $s.State | Should -Be 'Off' - $s.Wow6432NodeOn | Should -BeTrue + $s.Wow6432NodeValue | Should -Be '1' + Mock Get-RegValue { if ($Path -like '*Wow6432Node*') { 0 } elseif ($Name -eq 'OutputDirectory') { $null } else { 1 } } + $s = Get-TranscriptionPolicyState + $s.State | Should -Be 'Undirected' + $s.Wow6432NodeValue | Should -Be '0' + Mock Get-RegValue { if ($Path -like '*Wow6432Node*') { $null } else { 1 } } + (Get-TranscriptionPolicyState).Wow6432NodeValue | Should -Be '' + } + + It 'falls back to the user policy only when the machine hive has no value' { + Mock Get-RegValue { if ($Path -like 'HKCU:*') { if ($Name -eq 'OutputDirectory') { $null } else { 1 } } else { $null } } + $s = Get-TranscriptionPolicyState + $s.State | Should -Be 'Undirected' + $s.Scope | Should -Be 'User' + Mock Get-RegValue { if ($Path -like 'HKCU:*') { 1 } elseif ($Path -like '*Wow6432Node*') { $null } else { 0 } } + $s = Get-TranscriptionPolicyState + $s.State | Should -Be 'Off' + $s.Scope | Should -Be 'Machine' + Mock Get-RegValue { $null } + (Get-TranscriptionPolicyState).Scope | Should -Be '' + } + + It 'maps HKCU: as well as HKLM: for the .NET registry API' { + ConvertTo-NetRegPath 'HKCU:\SOFTWARE\Policies\X' | Should -Be 'HKEY_CURRENT_USER\SOFTWARE\Policies\X' + ConvertTo-NetRegPath 'HKLM:\SOFTWARE\Policies\X' | Should -Be 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\X' } It 'is assessed by Test as an uncategorised Safety row that fails only when Undirected' {