Fixes the 400 Bad Request error when launching agents in directories outside ~/, such as /Volumes/workplace on macOS. #110
Conversation
The previous validation required all working directories to be under the user's home directory (~/). This broke users on macOS with workspaces on external volumes (e.g. /Volumes/workplace) and corporate dev desktops with non-home project paths. Change the approach from "only allow ~/" to "block known-dangerous system paths". The existing _BLOCKED_DIRECTORIES blocklist (/, /etc, /var, /tmp, /dev, /proc, /sys, /root, /boot, /bin, /sbin, /usr/bin, /usr/sbin, /lib, /lib64) becomes the primary guard. Paths outside ~/ like /Volumes/workplace or /opt/projects are now allowed. Also adds /private/etc, /private/var, /private/tmp to the blocklist for macOS where /etc is a symlink to /private/etc. Fixes: 400 Bad Request when launching agents in /Volumes/workplace Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Independent Review & Test ResultsReviewed and tested locally on Amazon Linux 2 (Python 3.12.3, tmux next-3.6). SummaryClean fix for the regression where working directory validation rejected paths outside Test Results
Review Notes
Minor note: The blocklist only checks exact matches ( LGTM 👍 |
Add startswith("/") guard after realpath() to satisfy CodeQL's
py/path-injection two-state taint model. CodeQL recognizes
str.startswith() as a SafeAccessCheck that clears NormalizedUnchecked
taint. The guard is always true after realpath() but explicitly
rejects relative paths and satisfies the static analysis requirement.
Regression was introduced in d22ebde (#110) which relaxed the home
directory containment check to allow paths outside ~/. This removed
the startswith(home_dir) guard that CodeQL relied on.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add startswith("/") guard after realpath() to satisfy CodeQL's
py/path-injection two-state taint model (code-scanning alert #5).
CodeQL recognizes str.startswith() as a SafeAccessCheck that clears
NormalizedUnchecked taint state. The guard is always true after
realpath() but explicitly rejects relative paths and satisfies the
static analysis requirement.
Regression was introduced in d22ebde (#110) which removed the
startswith(home_dir) guard to allow paths outside ~/. This fix
restores CodeQL compliance without re-restricting allowed paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
) Add startswith("/") guard after realpath() to satisfy CodeQL's py/path-injection two-state taint model (code-scanning alert #5). CodeQL recognizes str.startswith() as a SafeAccessCheck that clears NormalizedUnchecked taint state. The guard is always true after realpath() but explicitly rejects relative paths and satisfies the static analysis requirement. Regression was introduced in d22ebde (#110) which removed the startswith(home_dir) guard to allow paths outside ~/. This fix restores CodeQL compliance without re-restricting allowed paths. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…120) * fix(claude_code): handle bypass permissions prompt on startup (#119) Claude Code v2.1.41+ shows a "Bypass Permissions mode" confirmation dialog on every launch with --dangerously-skip-permissions unless skipDangerousModePermissionPrompt is persisted in ~/.claude/settings.json. This blocks CAO initialization with a 30-second timeout. Two-layer fix: - Preventive: write skipDangerousModePermissionPrompt: true to ~/.claude/settings.json before launching Claude Code - Defensive: detect "Yes, I accept" in tmux buffer and send Down+Enter as a fallback if the settings-based fix doesn't take effect Also: - Rename _handle_trust_prompt → _handle_startup_prompts to reflect it now handles both bypass permissions and workspace trust prompts - Use continue (not return) after accepting bypass prompt so a subsequent trust prompt is still handled - Exclude bypass prompt from WAITING_USER_ANSWER status detection Closes #119 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: apply black formatting to test file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): add CodeQL SafeAccessCheck guard for path injection (#5) Add startswith("/") guard after realpath() to satisfy CodeQL's py/path-injection two-state taint model. CodeQL recognizes str.startswith() as a SafeAccessCheck that clears NormalizedUnchecked taint. The guard is always true after realpath() but explicitly rejects relative paths and satisfies the static analysis requirement. Regression was introduced in d22ebde (#110) which relaxed the home directory containment check to allow paths outside ~/. This removed the startswith(home_dir) guard that CodeQL relied on. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: apply black formatting to test assertions * chore: remove test artifacts accidentally included in merge --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Reported by: User on macOS with workspace at
/Volumes/workplace/PODCollectionEngine— worked before v1.1.1, broke after the working directory validation was added.Problem
The previous validation required all working directories to be under the user's home directory (
~/). This blocked legitimate use cases:/Volumes/workplace/...)/opt/projects/...)Fix
Changed the approach from "only allow
~/" to "block known-dangerous system paths". The existing_BLOCKED_DIRECTORIESblocklist becomes the primary guard instead of the home-directory containmentcheck.
Now allowed:
/Volumes/workplace/PODCollectionEngine/opt/projects/my-appStill blocked:
/,/etc,/var,/tmp,/dev,/proc,/sys,/root,/boot,/bin,/sbin,/usr/bin,/usr/sbin,/lib,/lib64/private/etc,/private/var,/private/tmpChanges
src/cli_agent_orchestrator/clients/tmux.py/private/*paths to blocklisttest/providers/test_tmux_working_directory.pyREADME.mddocs/working-directory.mdTest plan
/Volumes/workplace/...paths now accepted/opt/projects/...paths now accepted/etc,/var,/tmp,/root,/still rejected