Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Two drive-path bypasses remain, and CI skips all new rejection tests.
3 open issues
What changed in this PR
Strengthens DeerFlow’s opt-in host-bash guard against Windows drive-letter paths while preserving POSIX behavior.
Changes:
- Adds Windows-specific checks for drive paths, bare drive references, and working-directory changes.
- Adds Windows rejection tests and POSIX compatibility tests.
| File | Description |
|---|---|
| backend/tests/test_sandbox_tools_windows_drive_paths.py | Adds drive-path rejection and compatibility tests. |
| backend/packages/harness/deerflow/sandbox/tools.py | Adds Windows drive-path validation and documents its behavior. |
Review effort: Balanced
| if _is_in_spans(match.start(), url_spans): | ||
| continue |
| if not _WINDOWS_DRIVE_SPEC_PATTERN.fullmatch(token): | ||
| continue |
| "outputs_path": "/tmp/deer-flow/threads/t1/user-data/outputs", | ||
| } | ||
|
|
||
| _windows_only = pytest.mark.skipif(os.name != "nt", reason="drive-letter spellings are absolute host paths only on a Windows host") |
|
All three findings are addressed in
Evidence: the new cases fail at the base ( One residual noted by an independent adversarial pass (pre-existing at the reviewed head, not introduced here): the token-occurrence loop's URL exemption can be satisfied by a decoy non-file URL containing the same text ( |
|
Folded the decoy-URL hardening in as well (commit |
willem-bd
left a comment
There was a problem hiding this comment.
Fresh adversarial review at head 3fff21e. I empirically drove validate_local_bash_command_paths at head (validator invoked directly with _is_windows() patched, matching the PR's own fixtures). Every variant the PR claims to reject is rejected: C:/... and C:\... (quoted and unquoted), the bare roots C:/, C:\, the bare drive C:, cd/pushd targets including cd C: and the cd-then-relative-read case, drive-relative C:Windows/... and C:Windows\... (including the decoy-URL form), the C:// doubling, lowercase drives, 8.3 short names, and Win32-namespaced \\?\C:\...; the -F:/s/// carve-outs, URL-embedded drive text, and the POSIX spellings stay allowed, and the new test file passes 22/22 at head. The #6930 non-ASCII prose exemption does not swallow drive paths: the token scan fires on any word starting with X:, so benign text like python -c "print(f'C:/端口{port}')" stays allowed while cat C:/文档/x is rejected. Three same-class Windows host-path spellings remain uncovered — backslash UNC, drive-root backslash form, and cmd/PowerShell env-var expansion — detailed inline.
| # at all. The ``*`` tail lets a bare drive root (``C:/``, ``C:\``) match too. | ||
| # On POSIX the same spelling is a relative path under the workspace, so the | ||
| # drive scans stay Windows-only (validate_local_bash_command_paths). | ||
| _WINDOWS_DRIVE_PATH_PATTERN = re.compile(r"(?<!\w)[A-Za-z]:[\\/][^\s\"'`;&|<>()]*") |
There was a problem hiding this comment.
Verified at head that this scan (plus the token scan) catches C:/, C:\, bare roots, drive-relative forms, lowercase drives, 8.3 names and C://..., and that //server/share/x is rejected by the pre-existing /-prefixed scan. One same-class spelling still slips every scan: the backslash UNC form. cat '\\server\share\secret.txt', cd '\\server\share' and pushd '\\server\share' are all allowed on a Windows host — there is no / for the absolute-path scan, no X: prefix for the drive scans, and the dotdot token check does not apply — while the equivalent forward-slash spelling //server/share/x is rejected, so coverage is asymmetric across the two UNC spellings. A UNC path is an absolute host path exactly like the drive forms this PR adds (no colon needed), so it merits the same Windows-only treatment (e.g. reject a quoted word that starts with \\, mirroring the X:-prefix argument that no relative workspace path begins with a path-root marker), plus a regression case in test_sandbox_tools_windows_drive_paths.py.
| # added by _apply_cwd_prefix). The ``*`` tail catches the bare drive root | ||
| # itself, and a standalone ``C:`` word names the drive's current directory | ||
| # there (``dir C:`` lists it) — no Windows filename may contain a colon, | ||
| # so neither spelling can be a relative workspace path. A match that |
There was a problem hiding this comment.
Same-family gap: a word beginning with a backslash is also never a relative workspace path on a Windows host, but it has no colon so the new X:-anchored scans cannot see it. Verified at head: cat '\Windows\win.ini' and cd '\Windows' && cat win.ini both pass the guard, yet PowerShell resolves a leading-\ path against the root of the current drive — with the workspace on C: that read lands on C:\Windows\win.ini, i.e. the same host read as the cd-override example in the PR description. The comment's own argument ("no Windows filename may contain a colon, so such a word is never a relative workspace path") applies equally to a leading-\ word on PowerShell/cmd hosts; a Windows-only check for a token starting with \ (or folding it into the drive scans) would close it.
| # the same text also occurs inside a URL would let a decoy URL excuse | ||
| # the real drive-relative path: | ||
| # curl 'https://x/?p=C:Windowswin.ini'; type C\:Windows\win.ini | ||
| for token in tokens: |
There was a problem hiding this comment.
The PR's threat model counts PowerShell and cmd.exe as resolving shells (it uses their C: semantics to justify the drive scans), and on those shells env-var spellings expand at execution time where every scan here is blind. Verified allowed at head: cat %WINDIR%\win.ini, cat %SYSTEMDRIVE%\Windows\win.ini (cmd.exe) and cat $env:SystemRoot\win.ini (PowerShell) — none contain / or an X: prefix, and the $NAME/ scan only fires on the bash spelling ($SYSTEMDRIVE/Windows/win.ini is already rejected for exactly the "runtime value is unknown" reason in the docstring). If cmd/PowerShell are in scope for the drive scans, these are the same class: either reject path-like %NAME% / $env:NAME words on a Windows host or state explicitly in the docstring that shell-native expansions remain out of scope, so the next reviewer does not re-derive the boundary.
|
Thanks for the fresh adversarial pass — all three same-class spellings you found are now closed in
While hardening those I also closed the same-class variants the follow-up review surfaced: concatenated cwd targets ( The escape/format allow-list you called out stays green: At One follow-up is in progress from the latest internal pass: two pattern-argument edges — an assertion followed by another regex escape ( |
8b1a2ac to
f12eb59
Compare
|
Pushed the follow-up series for the remaining same-family gaps, rebased onto current
Every case has a regression test in |
willem-bd
left a comment
There was a problem hiding this comment.
Reviewed head f12eb59d. The coverage added since 3fff21e0 is real: the new suite passes 218/218 with no skips, and I re-verified that the previously reported backslash-UNC, \Windows and %WINDIR%\/$env:... forms are now rejected. Five same-class gaps remain at this head, each reproduced by driving validate_local_bash_command_paths directly with _is_windows() patched: (1) -Path:/Windows/win.ini (and FileSystem::/Windows/win.ini) is allowed because a / preceded by : is skipped by _ABSOLUTE_PATH_PATTERN and the new option scan only accepts a backslash root; (2) the new standalone env-var cwd check is only reached for the literal lowercase cd/pushd, so CD %WINDIR%, chdir %WINDIR% and Set-Location $env:WINDIR change into a host directory; (3) unquoted ..\..\ traversal (shlex strips the backslashes) still escapes the workspace; (4) cmd.exe ^ escaping (C^:/Windows) hides drive paths from every scan; (5) the X: token scan is context-free and rejects legitimate pattern arguments such as grep 'a:b' file. Findings 1-3 are host/workspace escapes; 4-5 are lower severity. The test suite is strong but has no case for any of these. Nothing here duplicates the existing review threads.
| _WINDOWS_OPTION_ROOTED_PATH_PATTERN = re.compile( | ||
| r"(?:^|(?<=[\s;&|()<>`\"']))" | ||
| r"(?:(?P<option>-{1,2}[A-Za-z0-9][A-Za-z0-9_-]*)[:=]|(?P<drive>[A-Za-z]):|(?P<assign>[A-Za-z_][A-Za-z0-9_]*)=)" | ||
| r"['\"]?(?P<path>\\\\?[^\s\"'`;&|<>()]+)" |
There was a problem hiding this comment.
Option values rooted with / are still invisible to the guard. This path group requires a leading backslash, and the pre-existing _ABSOLUTE_PATH_PATTERN deliberately skips a / preceded by : ((?<![\w:])), so a PowerShell parameter value is never scanned. Verified at this head (validator driven directly with _is_windows() patched): Get-Content -Path:/Windows/win.ini is allowed, while PowerShell resolves the leading / against the root of the current drive (Path.GetFullPath('/Windows') -> C:\Windows), i.e. the same host read as the rejected -Path:\Windows\win.ini. Get-Content FileSystem::/Windows/win.ini is allowed for the same reason. Quoting flips the result (-Path:'/Windows/win.ini' is rejected because the / is then preceded by a quote), which is exactly why the unquoted form is easy to miss. Please let this scan accept a / root as well, running it through _is_allowed_local_bash_absolute_path so /mnt/user-data/... stays legal, and add -Path: plus provider-qualified regression cases.
| # even without a trailing separator. Search rather than full-match: a | ||
| # concatenation such as ``%HOMEDRIVE%%HOMEPATH%`` is just as unsafe and | ||
| # would otherwise slip past a single-variable pattern. | ||
| if _is_windows() and _WINDOWS_ENV_VAR_TARGET_PATTERN.search(target): |
There was a problem hiding this comment.
These Windows cwd checks only run when the token's command name is exactly cd or pushd (_LOCAL_BASH_CWD_COMMANDS, exact str membership in _validate_local_bash_shell_tokens). The shells this PR treats as in scope do not work that way: cmd.exe command names are case-insensitive and chdir is a synonym, and PowerShell provides Set-Location/Push-Location (aliased as cd/sl). Verified allowed at this head: CD %WINDIR% && type win.ini, Cd %WINDIR%, chdir %WINDIR% && type win.ini, PUSHD %USERPROFILE%, Set-Location $env:WINDIR && type win.ini, and Push-Location $env:WINDIR — each changes into a host directory and then reads a relative file there, which is the exact scenario the standalone-variable target check was added for. (The drive/backslash spellings are still caught by the path scans, so only the variable form leaks here.) Please match cwd commands case-insensitively and include chdir/Set-Location/Push-Location on the Windows path, or apply the standalone env-var/backslash check to any command's arguments.
| # hide behind the shell lexer. A URL may legitimately contain the same | ||
| # text, so keep the URL span exemption. | ||
| raw_windows_paths: list[tuple[str, int]] = [] | ||
| for match in _WINDOWS_ROOTED_BACKSLASH_PATH_PATTERN.finditer(command): |
There was a problem hiding this comment.
The raw scans only consider words that start with a backslash, so the backslash traversal spelling still slips when unquoted. Verified allowed at this head: type ..\..\secret.txt, Get-Content ..\..\secret.txt, cd ..\..\ && type secret.txt, and type ..\..\..\..\Users\Public\secret.txt, while the quoted type '..\..\secret.txt' is rejected. The cause is the same lexer problem this raw scan exists to solve: shlex strips unquoted backslashes (..\..\secret.txt becomes ....secret.txt), so the pre-existing _DOTDOT_PATH_SEGMENT_PATTERN — which already includes \\ — never sees the segments. This is pre-existing rather than introduced by the PR, but it is the same Windows workspace-escape class and the raw-command walk here is the natural place to close it: scan raw words for (?:^|[\s;&|()<>"'])..[\/](or run the dotdot check against the raw word) and add the unquotedcd`/read cases.
| if command_name is not None: | ||
| raise PermissionError(f"Unsafe working directory change in command: {command_name} {raw_path}. Use paths under {VIRTUAL_PATH_PREFIX}") | ||
| unsafe_paths.append(raw_path) | ||
| for match in _WINDOWS_DRIVE_PATH_PATTERN.finditer(command): |
There was a problem hiding this comment.
cmd.exe removes ^ before parsing, but every scan here inspects the literal text, so the caret hides a drive path: type C^:/Windows/win.ini, type C^:\Windows\win.ini and cd C^:/Windows && type win.ini are all allowed at this head (verified), while cmd executes them as C:/Windows/win.ini / C:\Windows\win.ini. If cmd.exe is in the threat model (the PR relies on its C: semantics and %NAME% expansion to justify the drive scans), handle ^ in the raw-command scan; otherwise please state in the docstring that cmd preprocessing (^, and !VAR! under delayed expansion) remains out of scope, so this boundary is explicit rather than an oversight.
| # the real drive-relative path: | ||
| # curl 'https://x/?p=C:Windowswin.ini'; type C\:Windows\win.ini | ||
| for token in tokens: | ||
| if not _WINDOWS_DRIVE_SPEC_PATTERN.match(token): |
There was a problem hiding this comment.
This token scan rejects any word whose second character is :, regardless of argument context, so legitimate non-path arguments now fail on Windows. Verified at this head: echo a:b, grep 'a:b' file.txt, grep -e 'a:b' file, and Get-ChildItem -Filter 'a:*' all raise Unsafe absolute paths in command: a:b. The comment's premise — no Windows filename may contain a colon — only establishes that such a word cannot be a relative path; it does not make every such word a path. The PR already classifies argument context via _windows_argument_context for the backslash scan, so reusing it here would keep type a:b rejected while allowing the actual pattern argument of a regex tool (and an exemption for PowerShell -Filter-style pattern values if those matter). Please add allow-list regressions for the non-path forms.
willem-bd
left a comment
There was a problem hiding this comment.
Re-review at new head f12eb59 (PR was force-pushed; now +1608/-1 over two files). All three findings from the previous review (5566461327) are FIXED, verified empirically at head by driving validate_local_bash_command_paths with the host gate patched, using the same probe shapes as before:
- Backslash UNC (
cat '\\server\share\secret.txt'quoted and unquoted,cd/pushd '\\server\share') — now rejected via the new raw-command backslash scan, which also defeats shlex stripping unquoted backslashes (cat \\server\share\secret.txtunquoted is caught in the raw text). - Drive-root form (
cat '\Windows\win.ini',cd '\Windows' && cat win.ini) — rejected for both quoted and unquoted spellings, plus a token fallback for non-verbatim shells escapes. - Shell-native expansions (
cat %WINDIR%\win.ini,cat %SYSTEMDRIVE%\Windows\win.ini,cat $env:SystemRoot\win.ini,cd %WINDIR%) — rejected, including cmd substring expansion (%WINDIR:~0,3%), concatenated targets (cd %HOMEDRIVE%%HOMEPATH%), and bash-spelling$HOME\.ssh\id_rsa.
The expanded test file passes 218/218 at head and the pre-existing test_sandbox_tools_security.py passes 304/304 (no POSIX regression). The new escape-vs-root heuristics hold up against the family's known evasion shapes: \\?\C:\Windows\win.ini and \\.\PhysicalDrive0 stay rejected, the decoy-URL drive-relative case stays rejected (curl 'https://x/?p=C:Windowswin.ini'; type C\:Windows\win.ini), PowerShell subexpression $($env:SystemRoot)\win.ini is caught, and the -F:/s/// carve-outs stay allowed.
One new same-class gap found, detailed inline: the raw scan's word-boundary class omits , and =, which cmd.exe treats as standard argument delimiters, so a rooted backslash word after one still reaches the host while the identical word without the delimiter is rejected.
| # token stream. Each match still goes through | ||
| # _is_windows_rooted_backslash_word() so quote-initial escape arguments are | ||
| # not mistaken for roots. | ||
| _WINDOWS_ROOTED_BACKSLASH_PATH_PATTERN = re.compile(r"(?:^|(?<=[\s;&|()<>`\"']))\\\\?[^\s\"'`;&|<>()]+") |
There was a problem hiding this comment.
Verified at head that the three spellings from the previous review (UNC, \Windows-rooted, %VAR%/$env:NAME) are now rejected. One same-class gap remains in this lookbehind class: it omits , and = — both standard cmd.exe argument delimiters — so a rooted backslash word following one is invisible to every scan. Confirmed allowed at head while the same word without the delimiter is rejected:
type ,\Windows\win.ini/type =\Windows\win.ini(host read ofC:\Windows\win.inion cmd.exe)cd ,\Windows && type win.ini/pushd =\Windows && type win.ini(the cd-override shape)type ,\\server\share\secret.txt(UNC) andtype ,\Boot\BCD(the exact word this PR's own tests reject without the comma)
PowerShell reaches the same word too (unary comma builds a one-element array argument). Meanwhile type ,C:\Windows\win.ini is rejected, because the drive scan has no word-boundary lookbehind — so coverage is asymmetric between the two root spellings, mirroring the forward/backslash UNC asymmetry flagged last round.
Adding , and = to this class (and to the identical lookbehind in _WINDOWS_OPTION_ROOTED_PATH_PATTERN and _WINDOWS_ATTACHED_SHORT_OPTION_PATH_PATTERN), with regression cases in test_sandbox_tools_windows_drive_paths.py, would close it. !WINDIR! (delayed expansion) and %~dp0 (batch params) are analogous but non-default execution contexts — lower priority.
|
Addressed all six findings in
Tests: the Windows guard suite is 300 tests (was 218), all passing; the pre-existing |
|
Verified at Re-ran on |
willem-bd
left a comment
There was a problem hiding this comment.
Reviewed 45c06c2. The earlier Windows-path follow-ups are covered by the current tests. Two remaining P2 findings are detailed inline: a residual PowerShell cwd-target gap and a new ripgrep compatibility regression.
Validation: the Windows-path and existing host-bash security suites passed all 604 tests. Eight additional probes expose the two findings (four cases each). The four ripgrep commands execute successfully against temporary files and pass with the base guard function substituted, but the head rejects them. Ruff lint/format, diff and guidance checks passed; current CI checks were green. Windows validator checks used the patched host gate, matching the PR's own fixtures; no native Windows or PowerShell execution was performed. PowerShell parameter semantics were checked against Microsoft's documentation. No production source files were changed during this review.
| if _is_windows() and _WINDOWS_ENV_VAR_TARGET_PATTERN.search(target): | ||
| raise PermissionError(f"Unsafe working directory change in command: {command_name} {target}. Use paths under {VIRTUAL_PATH_PREFIX}") |
There was a problem hiding this comment.
[P2] Consume PowerShell option values before checking the cwd target
These commands still pass validate_local_bash_command_paths with the Windows host gate enabled:
Set-Location -ErrorAction Stop $env:WINDIR; Get-Content win.iniSet-Location -ErrorAction Stop -Path $env:WINDIR; Get-Content win.iniPush-Location -StackName saved $env:WINDIR; Get-Content win.inicd -ErrorAction SilentlyContinue $env:WINDIR; Get-Content win.ini
The existing _next_cd_target skips an option token but does not consume its value, so this check receives Stop, saved, or SilentlyContinue instead of the actual directory. The raw scans also miss a standalone $env:WINDIR because it has no trailing separator. Consequently, recognizing the PowerShell cwd aliases does not close the environment-variable cwd case when a value-taking parameter precedes the target.
PowerShell binds those strings to their named parameters before binding the directory argument; see Microsoft's Set-Location and Push-Location syntax. On a PowerShell host shell, that leaves the same cwd override and subsequent relative host-file read the PR intends to reject. This is a residual coverage gap in the best-effort guard, not a claim that host bash provides filesystem isolation.
Please consume PowerShell value-taking parameters, including common parameters, before validating the actual Path argument and add regression cases. The validator admission is empirically verified; native Windows/PowerShell execution was not tested.
| if name in _WINDOWS_GREP_LIKE_COMMANDS: | ||
| return "grep" |
There was a problem hiding this comment.
[P2] Preserve ripgrep-specific option semantics
Mapping rg to grep's option table loses value-taking ripgrep options such as --glob/-g and --type/-t. All of these valid workspace searches are rejected on the Windows validator path:
rg --glob '*.py' '\Aclass' .rg -g '*.py' '\Aclass' .rg --type py '\Aclass' .rg -t py '\Aclass' .
The parser treats the option as a flag, mistakes its value (*.py or py) for the search pattern, and then classifies the actual regex as a path. The rooted-backslash check consequently reports the regex as an unsafe host path.
I executed each command successfully against a temporary example.py file before invoking the validator. All four pass when substituting the base revision's guard function and fail at this head, confirming an introduced compatibility regression. Please retain tool-specific value-taking option semantics and add allow-list regressions for ripgrep patterns following glob/type options.
45c06c2 to
c59d875
Compare
|
Both P2s from this review are addressed at (a) PowerShell cwd targets. (b) ripgrep option semantics. The delta against the reviewed head Tests actually run (Windows host, backend uv project; the Windows branch of the validator is selected by the suites'
|
|
@BlueX888, the PR is so big for the feature we want. Could you reduce its size? |
|
Thanks — yes, I can. Two things are tangled together here, so let me separate them. The 18 commits are one topic ( The line count is the real cost: ~1200 lines in There is one clean cut I can make on request: land the core first — Which do you prefer: squash only, or core-first split? |
|
@BlueX888 please make a clean cut to do the quick fix. |
c59d875 to
e49e553
Compare
|
Reduced to +1149 / −3 in the same two files (was +2407 / −3) and rebased onto What went. The escape-vs-root oracle (14 regexes plus a 107-line decision What stayed, because you asked for it. Every case from your four review Option arity is now per tool. The single shared "takes a value" set is gone; What still doesn't. The false positives you reported are gone Measured locally (Windows, backend uv project; the Windows branch is selected
I also ran this revision against the current head on a 1440-case differential Full |
cf0f97e to
31a946e
Compare
|
Cut to the quick fix: +296 / −3, one commit, two files ( The Windows branch of the guard now rejects drive paths ( Left out of this cut, because each needs per-tool argument tables to keep pattern text allowed: a single-backslash read root (
|
|
The same match rejects an allowed path: |
The local host-bash guard validates POSIX absolute paths only, so on a Windows host a drive path (``C:/Windows/win.ini``, ``cat C:/``), a Win32 device or extended-length path, a UNC path and a cmd/PowerShell variable expansion reach PowerShell, cmd.exe or Git Bash unchanged. Scan the raw command for those unambiguous spellings on Windows, including a drive path after an attached short option, and reject a working-directory target that names a drive, a backslash root or a variable - also behind a PowerShell parameter such as ``-ErrorAction Stop``. A single-backslash root as a read argument, a bare drive-relative argument inside a script string and a ``/``-rooted option value stay out of scope: telling them apart from pattern text needs per-tool argument tables.
31a946e to
d9177ae
Compare
|
At Took the second branch of this comment: Verified by driving |
|
At
Those results come from driving Closing the cwd case is one class change in |
|
At
These results come from driving One shape still passes: a value-taking parameter whose value starts with |
|
At
The rg-to-grep option table is not in this diff: the cut to +296/−3 removed the per-tool tables, so a non-cwd argument is not parsed for options at all. The remaining Windows scans reject only unambiguous host-path spellings and treat a quoted or single-backslash word as text, so the regex is no longer classified as a path.
Results come from driving |
|
@willem-bd Could you approve the pending code workflows for the current head ( On scope: after the trim, are the two residual boundaries I described in the threads acceptable for this PR, or would you prefer them in a follow-up?
I don't want to expand the diff further without your call on scope. |


Why
On a Windows host with
sandbox.allow_host_bash: true, the local host-bash guard forwards any command that spells a host path with a drive letter to PowerShell, cmd.exe or Git Bash. Onmainatce0fa4c1:cat C:/Windows/win.iniandcat 'C:\Windows\win.ini'run against the host file, no errorcat C:/andcat C:\— the bare drive roots are forwarded toocd C:/Windows/System32 && cat drivers/etc/hosts— the model-issuedcdoverrides thecd <workspace> &&anchor, so the relative read lands in a host directory_ABSOLUTE_PATH_PATTERNneeds a/that is not preceded by:, so a drive path never matches, and_validate_local_bash_cwd_targetonly inspects targets starting with/,$or~.What changed
On a Windows host only, the guard now scans the raw command for the spellings that are unambiguously host paths and rejects them with the existing "Unsafe absolute paths" / "Unsafe working directory change" errors:
C:/…,C:\…,C://…andC:/, found wherever they appear, including inside an interpreted script (python -c "open('C:/Windows/win.ini')",powershell -Command "Get-Content C:\Windows\win.ini") and after an attached short option (curl -oC:/Windows/evil.exe,git -CC:/Windows)\\?\C:\…,\\.\PhysicalDrive0)\\server\share\…)%WINDIR%\win.ini,$env:SystemRoot\win.ini,$HOME\.ssh\id_rsa)cd C:,cd C:/Windows,cd C:Windows), a backslash root (cd \Windows) or a variable (%WINDIR%,$env:WINDIR); cmdchdirand PowerShellSet-Location/Push-Location/slare handled as working-directory commands too..\..\arguments, whose backslashes the token splitter dropsArguments that are text stay text:
grep 'a:b',echo a:b,grep -E '\bword\b',printf '\t%s\n',rg -g '*.py' '\Aclass' .,date +%H:%M,%-formats and a quoted..\inside a pattern (rg 'foo|..\bar' .) pass unchanged. POSIX behaviour is unchanged: each of those spellings is an ordinary relative name there.Kept out of this cut on purpose, because each needs per-tool argument tables to stay clear of pattern text: a single-backslash root as a read argument (
cat \Windows\win.ini), a bare drive-relative argument (cat C:Windows\win.ini), a/-rooted option or PowerShell provider value (-LiteralPath:/Windows/win.ini,FileSystem::/Windows/win.ini,curl -o/Windows/win.ini), cmd^decoding, short-option clusters, and a cwd command inside a quoted script string (powershell -Command "cd \Windows"). A quoted argument stays text, sorg 'then cd \Windows' docspasses.Surface area
backend/packages/harness/deerflow/sandbox/tools.pyBug fix verification
backend/tests/test_sandbox_tools_windows_drive_paths.pymainand green on this branch? yes — onmainatce0fa4c1the file is 35 failed / 20 passed, every failureDID NOT RAISE PermissionError; on this branch 55 passed.Validation
Ran in
backend/:pytest tests/test_sandbox_tools_windows_drive_paths.py tests/test_sandbox_tools_security.py -q— 371 passedruff checkandruff format --checkon both changed files — cleanThe full
make testshard matrix needs the CI Postgres/Redis services, so CI covers it.AI assistance
Tool(s) used: DeepSeek Harness (agentic coding harness)
How you used it: Scanned the existing guard and its POSIX path rules, wrote the Windows scan and its regression tests, and ran the red/green and lint checks above.