From 88f12d7b1002015f57248b885068a96c774103d4 Mon Sep 17 00:00:00 2001 From: Nik V Date: Tue, 18 Aug 2026 23:05:15 +0800 Subject: [PATCH 1/2] Allow bun install cache writes so OMP plugin resolution works in the sandbox - Add ~/.bun to OMP's writable state roots so bun can write its install cache - Extend the OMP registration test to assert the bun cache path is whitelisted OMP runs bun install at startup to resolve plugins, and bun needs to write ~/.bun/install/cache. Without this path in the Seatbelt allowlist, launching cco omp from a cold project failed with "bun is unable to write files to tempdir: PermissionDenied". --- lib/agents/omp.sh | 1 + tests/test_startup_preflights.sh | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/agents/omp.sh b/lib/agents/omp.sh index 6a4166c..f3a0658 100755 --- a/lib/agents/omp.sh +++ b/lib/agents/omp.sh @@ -7,6 +7,7 @@ configure_omp_mode_paths() { "${XDG_DATA_HOME:-$HOME/.local/share}/omp" "${XDG_STATE_HOME:-$HOME/.local/state}/omp" "${XDG_CACHE_HOME:-$HOME/.cache}/omp" + "$HOME/.bun" ) local p diff --git a/tests/test_startup_preflights.sh b/tests/test_startup_preflights.sh index 4f9befc..e2c021f 100644 --- a/tests/test_startup_preflights.sh +++ b/tests/test_startup_preflights.sh @@ -224,7 +224,8 @@ if ( "$PI_CODING_AGENT_DIR" \ "$XDG_DATA_HOME/omp" \ "$XDG_STATE_HOME/omp" \ - "$XDG_CACHE_HOME/omp" + "$XDG_CACHE_HOME/omp" \ + "$HOME/.bun" rw_paths=() add_rw_path() { rw_paths+=("$1") @@ -232,12 +233,13 @@ if ( is_known_subcommand "omp" configure_agent_subcommand "omp" [[ "$command_flag" == "omp" ]] - [[ ${#rw_paths[@]} -eq 5 ]] + [[ ${#rw_paths[@]} -eq 6 ]] [[ "${rw_paths[0]}" == "$HOME/.omp" ]] [[ "${rw_paths[1]}" == "$PI_CODING_AGENT_DIR" ]] [[ "${rw_paths[2]}" == "$XDG_DATA_HOME/omp" ]] [[ "${rw_paths[3]}" == "$XDG_STATE_HOME/omp" ]] [[ "${rw_paths[4]}" == "$XDG_CACHE_HOME/omp" ]] + [[ "${rw_paths[5]}" == "$HOME/.bun" ]] ); then pass "OMP mode registers its command and writable state roots" else From 455b7a8f9b960d987709a7b48bb601090484c4bb Mon Sep 17 00:00:00 2001 From: Nik V Date: Tue, 29 Sep 2026 14:41:11 +0800 Subject: [PATCH 2/2] Anchor Claude home write rules to prevent prefix escapes - Limit the JSON lock write exception to its directory and contents - Restrict atomic-write temps to flat filenames under HOME - Check permitted Claude writes and rejected lookalike paths under a test HOME Sandboxed processes can no longer create arbitrary prefix-matching siblings or nested temp paths while normal Claude writes remain available. --- sandbox | 4 ++-- tests/test_sandbox.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/sandbox b/sandbox index 29865b2..e914e38 100755 --- a/sandbox +++ b/sandbox @@ -642,8 +642,8 @@ run_macos() { # 6. Claude lock dirs and atomic-write temp files in $HOME printf '(allow file-write* (regex #"^%s/\\.claude\\.lock$"))\n' "$(policy_quote "$HOME")" printf '(allow file-write* (regex #"^%s/\\.claude\\.lock/"))\n' "$(policy_quote "$HOME")" - printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.lock"))\n' "$(policy_quote "$HOME")" - printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.tmp\\."))\n' "$(policy_quote "$HOME")" + printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.lock(/|$)"))\n' "$(policy_quote "$HOME")" + printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.tmp\\.[^/]+$"))\n' "$(policy_quote "$HOME")" # 7. Keychain access for OAuth token refresh (only if explicitly allowed) if [[ "$allow_keychain" == true ]]; then diff --git a/tests/test_sandbox.sh b/tests/test_sandbox.sh index 5c132fa..9e931d8 100755 --- a/tests/test_sandbox.sh +++ b/tests/test_sandbox.sh @@ -155,6 +155,37 @@ else fi fi +if [[ "$OS" == "Darwin" ]]; then + echo "Test: Claude JSON lock directory remains writable" + if HOME="$CLAUDE_HOME" ./sandbox sh -c 'mkdir "$HOME/.claude.json.lock" && : >"$HOME/.claude.json.lock/lock"' 2>/dev/null; then + pass "Claude JSON lock directory remains writable" + else + fail "Claude JSON lock directory remains writable" + fi + + echo "Test: Claude JSON atomic temp file remains writable" + if HOME="$CLAUDE_HOME" ./sandbox sh -c ': >"$HOME/.claude.json.tmp.write"' 2>/dev/null; then + pass "Claude JSON atomic temp file remains writable" + else + fail "Claude JSON atomic temp file remains writable" + fi + + echo "Test: Claude JSON lock prefix cannot create sibling files" + if HOME="$CLAUDE_HOME" ./sandbox sh -c ': >"$HOME/.claude.json.lockx"' 2>/dev/null; then + fail "Claude JSON lock prefix cannot create sibling files" + else + pass "Claude JSON lock prefix cannot create sibling files" + fi + + mkdir "$CLAUDE_HOME/.claude.json.tmp.directory" + echo "Test: Claude JSON temp prefix cannot write nested paths" + if HOME="$CLAUDE_HOME" ./sandbox sh -c ': >"$HOME/.claude.json.tmp.directory/nested"' 2>/dev/null; then + fail "Claude JSON temp prefix cannot write nested paths" + else + pass "Claude JSON temp prefix cannot write nested paths" + fi +fi + rm -rf "$CLAUDE_HOME" #