Relevant area(s)
Linux, macOS, Windows
Brief description of your issue
A relative process.cwd is never validated, so it is resolved against the host process's current directory. The sandboxed child then runs in a directory chosen by host state that the policy never granted — silently, with a successful exit.
Nothing in the config pipeline checks the shape of the value:
config_parser.rs:802 takes process.cwd verbatim (process.cwd.unwrap_or_default()).
validator.rs has no rule for working_directory at all (it only appears in a test fixture).
- Backends hand it straight to the OS: Seatbelt via
Command::current_dir (seatbelt_runner.rs:226), Bubblewrap via --chdir (bwrap_command.rs:260), Windows ProcessContainer via CreateProcessW (working_directory.rs:71).
For an absolute path this is correct and matches the documented contract. For a relative path, current_dir / --chdir resolve it against the launching process's cwd, so the effective sandbox cwd depends on where the host binary happened to be invoked from.
This defeats the reason the resolution logic exists in the first place (core/wxc_common/src/models.rs:771):
Backends that must not let the child inherit the host process's cwd use this to fall back to a policy-granted path.
An explicit relative cwd re-introduces exactly that inheritance through the back door.
Steps to reproduce
On macOS (same shape applies to Bubblewrap on Linux and ProcessContainer on Windows):
mkdir -p /tmp/mxccwd/sub
- Build a policy granting only
/tmp/mxccwd as readwritePaths, then:
let mut request = build_request(&policy, None)?;
request.set_script("pwd").set_working_directory("sub");
let output = run(request)?;
- Run the resulting binary from
/tmp/mxccwd.
Equivalently via a JSON config with "process": { "cwd": "sub" }.
Expected behavior
A relative process.cwd is rejected with a malformed-request / policy_validation error naming the value. The sandboxed child's working directory should never depend on the host process's cwd.
Actual behavior
Observed output from the reproduction above:
host_cwd="/private/tmp/mxccwd" requested="sub" -> "/private/tmp/mxccwd/sub" (Exited(0))
The child ran in a host-relative directory, exit code 0, no error and no warning. Invoking the same binary from a different directory silently changes the sandbox's working directory.
Note for whoever picks this up: the check is not a shared one-liner. "Absolute" is backend-relative — C:\x is not absolute per Path::is_absolute on Unix, and /x is a valid in-container path for WSLc but not a valid Windows host path. The rule likely belongs at each backend's resolution point (or as a per-backend predicate passed into the shared helper), not as a single check in validator.rs.
Related: #872 (WSLc one-shot silently drops an untranslatable process.cwd) — same field, same class of "wrong working directory, no diagnostic" failure, but a separate root cause and fix.
Relevant area(s)
Linux, macOS, Windows
Brief description of your issue
A relative
process.cwdis never validated, so it is resolved against the host process's current directory. The sandboxed child then runs in a directory chosen by host state that the policy never granted — silently, with a successful exit.Nothing in the config pipeline checks the shape of the value:
config_parser.rs:802takesprocess.cwdverbatim (process.cwd.unwrap_or_default()).validator.rshas no rule forworking_directoryat all (it only appears in a test fixture).Command::current_dir(seatbelt_runner.rs:226), Bubblewrap via--chdir(bwrap_command.rs:260), Windows ProcessContainer viaCreateProcessW(working_directory.rs:71).For an absolute path this is correct and matches the documented contract. For a relative path,
current_dir/--chdirresolve it against the launching process's cwd, so the effective sandbox cwd depends on where the host binary happened to be invoked from.This defeats the reason the resolution logic exists in the first place (
core/wxc_common/src/models.rs:771):An explicit relative cwd re-introduces exactly that inheritance through the back door.
Steps to reproduce
On macOS (same shape applies to Bubblewrap on Linux and ProcessContainer on Windows):
mkdir -p /tmp/mxccwd/sub/tmp/mxccwdasreadwritePaths, then:/tmp/mxccwd.Equivalently via a JSON config with
"process": { "cwd": "sub" }.Expected behavior
A relative
process.cwdis rejected with a malformed-request /policy_validationerror naming the value. The sandboxed child's working directory should never depend on the host process's cwd.Actual behavior
Observed output from the reproduction above:
The child ran in a host-relative directory, exit code 0, no error and no warning. Invoking the same binary from a different directory silently changes the sandbox's working directory.
Note for whoever picks this up: the check is not a shared one-liner. "Absolute" is backend-relative —
C:\xis not absolute perPath::is_absoluteon Unix, and/xis a valid in-container path for WSLc but not a valid Windows host path. The rule likely belongs at each backend's resolution point (or as a per-backend predicate passed into the shared helper), not as a single check invalidator.rs.Related: #872 (WSLc one-shot silently drops an untranslatable
process.cwd) — same field, same class of "wrong working directory, no diagnostic" failure, but a separate root cause and fix.