fix(sandbox): keep command paths POSIX on a Windows host - #4958
Open
hyeonsang010716 wants to merge 1 commit into
Open
fix(sandbox): keep command paths POSIX on a Windows host#4958hyeonsang010716 wants to merge 1 commit into
hyeonsang010716 wants to merge 1 commit into
Conversation
Three sandbox commands built an argument with `str()` on a host-native `Path`. On a Windows host that yields backslashes, which a Linux sandbox reads as an ordinary filename rather than a separator. `GitRepo.apply` sent `\workspace\repo/` as the `cp` destination, so the clone landed in a directory with that literal name and the real destination stayed empty. `cp` exits 0, so the misplacement was silent. `SandboxMemoryStorage.ensure_text_file` probed with `test -f` on a backslash path that can never match, so `ensure_layout` replaced an existing `MEMORY.md` with an empty file on every rollout enqueue and flush. `E2BSandboxSession.mkdir` probed the parent the same way, so every `mkdir` without `parents=True` raised `ExecNonZeroError` even when the parent existed. Each site now uses `sandbox_path_str`, the helper the surrounding code already applies to sandbox command arguments. Two assertions in `test_entries.py` normalized separators before comparing, which masked the `cp` destination bug. They now compare the argument directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three sandbox commands built an argument with
str()on a host-nativePath. On a Windows host thatyields backslashes, and a Linux sandbox reads a backslash as an ordinary filename character rather
than a separator, so each command silently addressed the wrong path.
GitRepo.applysent\workspace\repo/as thecpdestination. GNUcp -R -- src/. '\workspace\repo/'exits 0 and creates a directory with that literal name, so the clone was misplaced and the real
destination stayed empty with no error to surface. I confirmed the exit status and the resulting
layout against real coreutils 9.4 rather than inferring it.
SandboxMemoryStorage.ensure_text_fileprobedtest -f \workspace\memories\MEMORY.md, which cannever match.
ensure_layouttherefore treated an existing file as absent and overwrote it with anempty one. It runs on every rollout enqueue and on flush, so a user's
MEMORY.mdwas lostrepeatedly.
E2BSandboxSession.mkdirprobed the parent the same way, so everymkdirwithoutparents=Trueraised
ExecNonZeroErroreven when the parent existed.Each site now uses
sandbox_path_str, whichentries/mounts/patterns.py,sandboxes/docker.py,runtime.pyand the rest ofe2b/sandbox.pyalready apply to sandbox command arguments..agents/references/sandbox-runtime-boundary.mdstates the rule these three sites broke.Scope. The change is limited to values that become sandbox command arguments. I swept every
exec/exec_streamargument insrc/agents/sandboxandsrc/agents/extensions/sandboxand thesethree are the complete set; the remaining callers already normalize, and
util/github.pycorrectlyuses
str()because it runsgiton the host throughsubprocess.Two deliberate exclusions, happy to fold either in if you would prefer:
GitCopyError(dest=...),memory/manager.py:189,sandboxes/docker.py:520,base_sandbox_session.py:795). These producebackslashes in Windows error text but break no command, and including them would widen the rule
from "command argument" to "command argument or serialized value" without covering every sibling.
_prepare_exec_command, wherestr(c)stringifies aPathat the publicexec(*command: str | Path)boundary. Normalizing there fixes none of the three bugs above, because each call site stringifies
before reaching it, and no first-party caller passes a bare
Path. It is worth doing asdefense-in-depth, but as an untested behavior change at a public boundary it does not belong in a
bug fix.
Test plan
tests/sandbox/test_entries.py:test_git_repo_copies_to_a_posix_destination_from_a_windows_hostdrives
GitRepo.applywith aPureWindowsPathdestination and asserts thecpargument and that noargument contains a backslash.
PureWindowsPathis flavour-fixed, so this case reproduces thehost-native shape and fails on every platform, not only Windows. I verified that on Linux directly.
tests/sandbox/test_posix_tool_paths.py:test_memory_layout_probes_existing_files_with_posix_pathsasserts the exact probe arguments
ensure_layoutissues. It lives here rather than insandbox/test_memory.pybecause that file is in the Windowscollect_ignorelist, where the bug isthe only place it reproduces.
tests/extensions/sandbox/test_e2b.py:test_e2b_mkdir_probes_the_parent_with_a_posix_pathassertsthe parent probe. Before the fix the recorded command is
test -d '\workspace\sub'.src/fails 9 cases: the 3 new ones plus the 6 existingcpassertions, which nowdetect the bug on Windows as well.
test_entries.pyapplied.replace("\\", "/")to thecpdestinationbefore comparing, which is exactly what hid this bug. They now compare the argument directly.
tests-windowsjob exercises them. The memoryand E2B cases detect the regression only on that leg, since a native
Pathalready stringifies toPOSIX on Linux; the
GitRepocase guards every leg.ruff format --check,ruff check,check_optional_truthiness.py src/agents,mypy src(312 files)and
pyrightare clean.are the pre-existing symlink-privilege ones (Sandbox tar extraction and 12 sandbox tests fail on Windows without symlink privilege #4852); I confirmed a pristine tree on this host
produces the same 12.
makeis not installed here, so theMakefiletargets were run directly in the script's order:format, lint, typecheck, then the parallel and serial suites.
Issue number
None.
Checks
.agents/skills/code-change-verification/scripts/run.sh/reviewbefore submitting this PR