Files
codex/codex-rs/windows-sandbox-rs
efrazer-oai b885c3f8b1 Filter Windows sandbox roots from SSH config dependencies (#18493)
## Stack

1. Base PR: #18443 stops granting ACLs on `USERPROFILE`.
2. This PR: filters additional SSH-owned profile roots discovered from
SSH config.

## Bug

The base PR removes the broadest bad grant: `USERPROFILE` itself.

That still leaves one important case. A user profile child can be
SSH-owned even when its name is not one of our fixed exclusions.

For example:

```sshconfig
Host devbox
  IdentityFile ~/.keys/devbox
  CertificateFile ~/.certs/devbox-cert.pub
  UserKnownHostsFile ~/.known_hosts_custom
  Include ~/.ssh/conf.d/*.conf
```

After profile expansion, the sandbox might see these as normal profile
children:

```text
C:\Users\me\.keys
C:\Users\me\.certs
C:\Users\me\.known_hosts_custom
C:\Users\me\.ssh
```

Those paths have another owner: OpenSSH and the tools that manage SSH
identity and host-key state. Codex should not add sandbox ACLs to them.

OpenSSH describes this dependency tree in
[`ssh_config(5)`](https://man.openbsd.org/ssh_config.5), and the client
parser follows the same shape in `readconf.c`:

- `Include` recursively reads more config files and expands globs
- `IdentityFile` and `CertificateFile` name authentication files
- `UserKnownHostsFile`, `GlobalKnownHostsFile`, and `RevokedHostKeys`
name host-key files
- `ControlPath` and `IdentityAgent` can name profile-owned sockets or
control files
- these path directives can use forms such as `~`, `%d`, and `${HOME}`

## Change

This PR adds a small SSH config dependency scanner.

It starts at:

```text
~/.ssh/config
```

Then it returns concrete paths named by `Include` and by path-valued SSH
config directives:

```text
IdentityFile
CertificateFile
UserKnownHostsFile
GlobalKnownHostsFile
RevokedHostKeys
ControlPath
IdentityAgent
```

For example:

```sshconfig
IdentityFile ~/.keys/devbox
CertificateFile ~/.certs/devbox-cert.pub
Include ~/.ssh/conf.d/*.conf
```

returns paths like:

```text
C:\Users\me\.keys\devbox
C:\Users\me\.certs\devbox-cert.pub
C:\Users\me\.ssh\conf.d\devbox.conf
```

The setup code then maps those paths back to their top-level
`USERPROFILE` child and filters matching sandbox roots out of both the
writable and readable root lists.

## Why this shape

The parser reports what SSH config references. The sandbox setup code
decides which `USERPROFILE` roots are unsafe to grant.

That keeps the policy simple:

1. expand broad profile grants
2. remove the profile root
3. remove fixed sensitive profile folders
4. remove profile folders referenced by SSH config dependencies

If a path has two possible owners, the sandbox steps back. SSH keeps
control of SSH config, keys, certificates, known-hosts files, sockets,
and included config files.

## Tests

- `cargo test -p codex-windows-sandbox --lib`
- `just bazel-lock-check`
- `just fix -p codex-windows-sandbox`
- `git diff --check`
2026-04-19 14:58:33 -07:00
..