Repository navigation
fix(cli): sandbox the claude-code harness and run the local ACP on loopback - #554
Draft
stephen-wang24 wants to merge 2 commits into
Draft
stephen-wang24 wants to merge 2 commits into
stephen-wang24 wants to merge 2 commits into
Conversation
…opback VULN-3136. The three claude-code templates (and their tutorials) spawned `claude -p` in the ACP's working directory, which under `agentex agents run` is the project directory holding .env, with the ACP's full environment and no tool policy, while `agentex agents run` served the unauthenticated ACP on 0.0.0.0. - Spawn the CLI in a per-task scratch directory under the temp dir (stable across turns, so Temporal `-r <session_id>` resume still works), with only ANTHROPIC_API_KEY, PATH, HOME and USER in its environment. - Pass an explicit policy: read-only tools (--tools Read,Glob,Grep), --permission-mode dontAsk, no setting sources, --strict-mcp-config. - Pin @anthropic-ai/claude-code@2.1.142 in the template and tutorial Dockerfiles, the version the tutorials CI installs. - `agentex agents run` now listens on 127.0.0.1. Override with --host 0.0.0.0 or AGENTEX_AGENT_HOST=0.0.0.0; the tutorials CI sets the variable because its Docker server reaches agents through host-gateway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
…trol On a shared /tmp another account could pre-create agentex-claude-code, or link it to the developer's project, and mkdir(exist_ok=True) would accept it. Check the root with lstat: a real directory, owned by this user, with no group or other access (POSIX only; the Windows temp dir is per-user). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
This branch has not been deployed
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.
The three Claude Code harness templates (
sync-claude-code,default-claude-code,temporal-claude-code) and their threeclaude_codetutorials now run theclaudeCLI in a per-task scratch directory, with a short environment allowlist and an explicit tool policy.agentex agents runnow listens on127.0.0.1by default.What changed
$TMPDIR/agentex-claude-code/<sha256(task id)>instead of the agent's working directory, which underagentex agents runis the project folder with.envandmanifest.yaml. The directory is stable per task, so Temporal's-r <session_id>resume still finds the previous turn, and its root must be a private directory owned by the current user (anlstatcheck on POSIX). The CLI's environment is onlyANTHROPIC_API_KEY,PATH,HOMEandUSER(USERlets it find a macOS keychain login).--tools Read,Glob,Grep --permission-mode dontAsk --setting-sources "" --strict-mcp-config: the model gets read-only tools, anything that would need approval (such as a read outside its working directory) is refused, and the developer's own Claude Code settings, hooks, plugins and MCP servers are not loaded. The template and tutorial Dockerfiles pin@anthropic-ai/claude-code@2.1.142, the version the tutorials CI installs; all four flags exist in 2.1.142 and in 2.1.311.agentex agents run(also with--debugor--debug-acp) listens on127.0.0.1. Override with--host 0.0.0.0orAGENTEX_AGENT_HOST=0.0.0.0. The tutorials CI sets the variable: its AgentEx server runs in Docker on Linux and reaches agents athost.docker.internalthroughhost-gateway, which maps to the Docker bridge, not loopback. The override is documented in the CLI help, the root README and the three template READMEs.Before / After
agentex agents run.envandmanifest.yaml.envand the manifest'senv:ANTHROPIC_API_KEY,PATH,HOME,USER2.1.142agentex agents runlistens on*:<port>)127.0.0.1:<port>, unless--hostorAGENTEX_AGENT_HOSTsays otherwiseProof
Asking the agent for its project files. On
mainthe agent returns the test values from both.envandmanifest.yaml; on this branch it returns neither. Each row is onemessage/sendrequest sent straight to the agent's port, to the sync template started withagentex agents runfrom a terminal-like environment with the localclaudesigned in. The project's.envand itsmanifest.yamlenv:block each hold one test value.main.envandmanifest.yamlfrom your working directory<project>/.envby its full pathPermission to use Read has been denied because Claude Code is running in don't ask modecat <project>/.envthrough Bash-pmode<project>with Grep for the test valuegrepthrough Bash needed approvalPermission to use Grep has been denied because Claude Code is running in don't ask mode4242Tool steps for the first two rows, from the agent's response stream (the
.envline is shown as a placeholder):Listen address (
lsof -iTCP:<port> -sTCP:LISTENwhileagentex agents runis up; the two rows are uvicorn's reloader and worker):What the CLI loads. Counted from the CLI's own
system/initstream-json message, with a terminal-like environment and this Mac's real~/.claudeconfig:maindefaultdontAskdefaultdontAskNormal prompts and resume. The sync template is the last row of the first table. For async and Temporal, the full loop needs the AgentEx server, Redis and Temporal, which this Mac could not run, so each rendered template's real
_spawn_clauderan from its project folder:The fourth line is why the scratch directory is stable per task: the pinned CLI resumes a session only from the directory it started in.
End to end in CI. The tutorials CI on this PR ran the three
claude_codetutorials live (CLI 2.1.142, real key) against the AgentEx server in Docker on Linux, reaching the agents throughAGENTEX_AGENT_HOST=0.0.0.0.TestClaudeCodeLivepassed for060_claude_code(sync),130_claude_code(async) and140_claude_code(Temporal).Local dev loop on macOS. The AgentEx server (scale-agentex
mainat 4ccf8b8, built from source for arm64) ran in Docker on Rancher Desktop. Inside its containerhost.docker.internalresolves to192.168.5.2, the VM's gateway to the Mac. The sync template agent from this branch registeredhttp://host.docker.internal:18002, and a message sent through the server came back with the agent's answer, with the default bind and with the override:For the async and Temporal templates, the server's calls reached the agents on
127.0.0.1as well (2 and 3POST /api200s in the agents' logs), but each agent's own call back to this locally built server failed before the Claude CLI ran, in code this PR does not touch; their replies are shown by the tutorials CI above. Linuxhost-gatewaysetups dial the Docker bridge address instead, which a loopback bind does not serve; they need the override, as in the tutorials CI.Supporting checks. New tests:
tests/lib/cli/test_claude_code_spawn_policy.pyrenders each claude-code template and runs its real_spawn_claudein a child interpreter against a stubclaudeonPATH. It asserts the argv policy, a scratch directory outside the project (stable per task, not steerable by the task id, refused when its root is a link or world-writable), the four-variable environment, and-r <session_id>resuming in the same directory.tests/lib/cli/test_agents_run_host.pyasserts the--hostdefault, flag and environment variable, and the host passed to uvicorn in normal and debug mode.CI on d38f5ce: all 49 checks pass. The
testjob (both Python versions) shows1778 passed, 1622 skipped; its first attempt hit one failure intests/lib/test_build_provenance.py::test_capture_clean_tree(a temporary.git/objects/maintenance.lockvanished mid-hash, in code this PR does not touch; it passes locally 5/5) and passed on re-run.Not verified yet
Risk to agentex
host-gateway, or from another machine, must now pass--host 0.0.0.0or setAGENTEX_AGENT_HOST=0.0.0.0. Rancher Desktop on macOS (VM-to-host hop tested above) and the server's no-Docker mode (which dials127.0.0.1) keep working without it.CLAUDE_POLICY_ARGSon purpose, for example with--add-dir.ANTHROPIC_BASE_URL,HTTPS_PROXY) must add that variable toCLAUDE_ENV_KEYS; the template READMEs say so._spawn_claudefunctions; its new test calls the old one-argument signature). Whichever merges second needs a small rebase.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; the previous scratch-root issue is fixed.Summary
The PR restricts Claude's tools, environment, and working directory, and makes local ACP servers listen on loopback by default.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Local caller] --> B[ACP on loopback by default] B --> C[Check private scratch root] C --> D[Stable task directory] D --> E[Claude with read-only tools and limited environment]Reviews (2) · Last reviewed commit: "fix(cli): refuse a claude-code scratch r..." · Reviewed by Greptile