Prerequisites
Steps to reproduce
On Windows, create a directory hierarchy where the user cannot access intermediate directories directly, but can traverse them due to the standard SeChangeNotifyPrivilege / Bypass traverse checking privilege and has explicit access to a descendant.
The following example uses:
where z is the accessible target.
The ACL setup needs to be performed from an elevated PowerShell. $TestUser is the non-elevated user that will run the reproduction:
$TestUser = 'DOMAIN\User'
$Sid = (
[System.Security.Principal.NTAccount]::new($TestUser)
).Translate([System.Security.Principal.SecurityIdentifier]).Value
$Principal = "*$Sid"
New-Item -ItemType Directory -Path 'D:\x\y\q\z' -Force | Out-Null
Set-Content -LiteralPath 'D:\x\y\q\z\test.txt' -Value 'This is a test'
# Deny access to the intermediate directories themselves.
# These ACEs don't inherit to descendants.
icacls 'D:\x\y' /deny "$Principal`:F"
icacls 'D:\x\y\q' /deny "$Principal`:F"
# Explicitly grant access to the final target and its descendants.
icacls 'D:\x\y\q\z' /grant:r "$Principal`:(OI)(CI)(M)"
Start a new, non-elevated PowerShell process as $TestUser.
Verify that the standard Windows bypass-traverse privilege is present:
whoami /priv | Select-String SeChangeNotifyPrivilege
For example:
SeChangeNotifyPrivilege Bypass traverse checking Enabled
The target directory is accessible even though an intermediate path component isn't:
Get-Item -LiteralPath 'D:\x\y\q'
Result:
Get-Item: Access to the path 'D:\x\y\q' is denied.
Get-Item: Cannot find path 'D:\x\y\q' because it does not exist.
However, accessing the descendant succeeds:
Get-Item -LiteralPath 'D:\x\y\q\z'
Get-ChildItem -LiteralPath 'D:\x\y\q\z'
Get-Content -LiteralPath 'D:\x\y\q\z\test.txt'
The process current directory can also be set to the target:
[Environment]::CurrentDirectory = 'D:\x\y\q\z'
[Environment]::CurrentDirectory
Result:
Finally:
Set-Location -LiteralPath 'D:\x\y\q\z'
Expected behavior
Set-Location should succeed:
The target directory is accessible and Windows permits traversal through the intermediate directories using SeChangeNotifyPrivilege ("Bypass traverse checking").
Direct navigation to an inaccessible target should still fail, for example:
Set-Location -LiteralPath 'D:\x\y\q'
Actual behavior
Set-Location fails while inspecting the inaccessible intermediate component:
Set-Location: Access to the path 'D:\x\y\q' is denied.
This differs from direct filesystem access to the same final target, which succeeds.
Error details
On current PowerShell master, the exception originates from FileSystemProvider path normalization:
System.UnauthorizedAccessException: Access to the path 'D:\x\y\q' is denied.
at System.IO.FileSystemInfo.EnsureDataInitialized()
at System.IO.FileSystemInfo.get_Attributes()
at Microsoft.PowerShell.Commands.FileSystemProvider.GetFileSystemInfo(String path, Boolean& isContainer)
at Microsoft.PowerShell.Commands.FileSystemProvider.NormalizeThePath(String basepath, Stack`1 tokenizedPathStack)
at Microsoft.PowerShell.Commands.FileSystemProvider.NormalizeRelativePathHelper(String path, String basePath)
at Microsoft.PowerShell.Commands.FileSystemProvider.NormalizeRelativePath(String path, String basePath)
at System.Management.Automation.Provider.NavigationCmdletProvider.NormalizeRelativePath(String path, String basePath, CmdletProviderContext context)
at System.Management.Automation.SessionStateInternal.NormalizeRelativePath(ProviderInfo provider, String path, String basePath, CmdletProviderContext context)
NormalizeThePath() builds the path component by component and calls:
var fsinfo = GetFileSystemInfo(currentPath, out bool _);
GetFileSystemInfo() retrieves FileSystemInfo.Attributes.
As a result, PowerShell requires metadata access to intermediate path components while normalizing the path. This can fail even though Windows itself permits traversal through those components and access to the final target.
Additional context
I tested a proof-of-concept change against current master that ignores UnauthorizedAccessException from GetFileSystemInfo() only for intermediate path components:
FileSystemInfo fsinfo = null;
try
{
fsinfo = GetFileSystemInfo(currentPath, out bool _);
}
catch (UnauthorizedAccessException) when (tokenizedPathStack.Count > 0)
{
// Access to an intermediate path component isn't required when
// the operating system permits traversal through that component.
}
This is intended only as a proof of concept, not necessarily as the proposed implementation.
With this change, the reproduction above behaves as follows:
Get-Item D:\x\y\q -> Access denied
Get-Item D:\x\y\q\z -> succeeds
Get-ChildItem D:\x\y\q\z -> succeeds
Get-Content ...\z\test.txt -> succeeds
Set-Location D:\x\y\q -> Access denied
Set-Location D:\x\y\q\z -> succeeds
Path normalization also retains the expected distinction between an accessible and inaccessible final target:
Set-Location 'D:\x\y\q\z\..\z'
# succeeds, resolves to D:\x\y\q\z
Set-Location 'D:\x\y\q\z\..'
# Access denied, resolves to D:\x\y\q
This suggests that requiring metadata access to every intermediate path component in FileSystemProvider.NormalizeThePath() is what prevents Set-Location from respecting Windows bypass-traverse semantics.
Environment data
Name Value
---- -----
PSVersion 7.6.5
PSEdition Core
GitCommitId 7.6.5
OS Microsoft Windows 10.0.26200
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.4
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
and
Name Value
---- -----
PSVersion 7.7.0-preview.3
PSEdition Core
GitCommitId 7.7.0-preview.3-40-g2dc81fde9b0485883f4689287724503739540443
OS Microsoft Windows 10.0.26200
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.4
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Visuals
No response
Prerequisites
Steps to reproduce
On Windows, create a directory hierarchy where the user cannot access intermediate directories directly, but can traverse them due to the standard SeChangeNotifyPrivilege / Bypass traverse checking privilege and has explicit access to a descendant.
The following example uses:
where z is the accessible target.
The ACL setup needs to be performed from an elevated PowerShell. $TestUser is the non-elevated user that will run the reproduction:
Start a new, non-elevated PowerShell process as $TestUser.
Verify that the standard Windows bypass-traverse privilege is present:
For example:
The target directory is accessible even though an intermediate path component isn't:
Result:
However, accessing the descendant succeeds:
The process current directory can also be set to the target:
Result:
Finally:
Expected behavior
Set-Location should succeed:
The target directory is accessible and Windows permits traversal through the intermediate directories using SeChangeNotifyPrivilege ("Bypass traverse checking").
Direct navigation to an inaccessible target should still fail, for example:
Actual behavior
Set-Location fails while inspecting the inaccessible intermediate component:
This differs from direct filesystem access to the same final target, which succeeds.
Error details
On current PowerShell master, the exception originates from FileSystemProvider path normalization:
NormalizeThePath() builds the path component by component and calls:
GetFileSystemInfo() retrieves FileSystemInfo.Attributes.
As a result, PowerShell requires metadata access to intermediate path components while normalizing the path. This can fail even though Windows itself permits traversal through those components and access to the final target.
Additional context
I tested a proof-of-concept change against current master that ignores UnauthorizedAccessException from GetFileSystemInfo() only for intermediate path components:
This is intended only as a proof of concept, not necessarily as the proposed implementation.
With this change, the reproduction above behaves as follows:
Path normalization also retains the expected distinction between an accessible and inaccessible final target:
This suggests that requiring metadata access to every intermediate path component in FileSystemProvider.NormalizeThePath() is what prevents Set-Location from respecting Windows bypass-traverse semantics.
Environment data
and
Visuals
No response