Repository navigation
getAvailableToolsPolicy grants read access to the whole system drive on BaseContainer when pwsh is on PATH #1455
Description
Relevant area(s)
Windows
Brief description of your issue
When PowerShell 7 (pwsh.exe) is on PATH, getAvailableToolsPolicy adds the drive root C:\ to readonlyPaths. This is documented ("so that interactive PowerShell sessions work correctly inside the container"). But on a BaseContainer host the grant covers the whole subtree, so a sandbox built from the README's recommended baseline (getAvailableToolsPolicy + getTemporaryFilesPolicy) can read every file the user can: the user profile, %USERPROFILE%\.aws, .ssh, browser profiles, other projects. Most developer machines with PowerShell 7 installed get this policy, and nothing in the result or the logs signals that the sandbox no longer limits reads.
Steps to reproduce
Windows 11 25H2, build 26200.9457. getPlatformSupport() reports isolationTier: "base-container". @microsoft/mxc-sdk 0.9.0 and 1.0.0 (policy.filesystem.getAvailableToolsPolicy from @microsoft/mxc-sdk/v1), Node 24.21.0.
- The helper's result depends only on whether PowerShell 7 is on
PATH:
import { delimiter, join } from 'node:path';
import { policy } from '@microsoft/mxc-sdk/v1';
const tools = policy.filesystem.getAvailableToolsPolicy;
const sys = join(process.env.SystemRoot, 'System32');
const node = join(process.env.ProgramFiles, 'nodejs');
const pwsh = join(process.env.ProgramFiles, 'PowerShell', '7');
const roots = (t) => t.readonlyPaths.filter((p) => p.length <= 3 && p[1] === ':');
roots(tools({ SystemRoot: process.env.SystemRoot, PATH: [sys, node].join(delimiter) })); // []
roots(tools({ SystemRoot: process.env.SystemRoot, PATH: [sys, node, pwsh].join(delimiter) })); // ['C:\\']- Use the result in a policy that should only allow a project folder (0.9.0 API shown; 1.0.0
runbehaves the same):
import { createConfigFromPolicy, getAvailableToolsPolicy, spawnSandboxFromConfig } from '@microsoft/mxc-sdk';
const config = createConfigFromPolicy(
{
version: '0.9.0-alpha',
filesystem: { readwritePaths: [project], readonlyPaths: getAvailableToolsPolicy(process.env).readonlyPaths },
network: { egress: { default: 'deny' }, ingress: { default: 'deny', hostLoopback: 'deny' } },
},
'process',
);
config.process.commandLine = `cmd.exe /d /c dir /b "${process.env.USERPROFILE}" & type "${outsideFile}"`;
config.process.cwd = project;
spawnSandboxFromConfig(config, { usePty: false });- The sandboxed command lists the user profile and prints a file outside the project and outside every tool folder.
- Remove
C:\fromreadonlyPathsand run it again: both are denied (Access is denied), as expected.
Expected behavior
The recommended discovery baseline does not grant read access to the whole system drive. For example:
- grant only what PowerShell needs at the root: metadata or enumeration of
C:\itself (aswxc-host-prep prepare-system-drivedoes on the DACL tier, orenumeratePathswhere PSEC 1.1 is available), not file contents of the subtree; or - return the root under a separate, clearly named field or option so callers opt in; and
- document the effect on BaseContainer in the README's PowerShell pitfall and the helper's doc comment.
Actual behavior
readonlyPaths contains C:\ whenever pwsh.exe is on PATH, and on BaseContainer the sandboxed process can read and list everything on the system drive that the user can. Callers who follow the README get a sandbox that only restricts writes and network, without any warning.
Activity
mvanhorn commentedon Oct 10, 2026
getPowerShellPolicy appended SystemDrive (falling back to C:) plus a trailing slash to readonlyPaths whenever it found pwsh.exe. BaseContainer treats a drive-root readonly path as a recursive read of the subtree, so the grant meant to let PowerShell stat the root became a whole-drive content read.
The drive root is added only when allowPowerShellDriveRootRead is true; the default is false.
#1463