Repository navigation
fix(env): stage a loaded artifact without passing its names to the shell as code - #374
Merged
Merged
Conversation
…ell as code MCPServerEnv and WebsiteEnv staged a loaded environment artifact on a VM sandbox's host with bash lines that spliced the artifact's file name and the env's name in unquoted. A name holding shell syntax ran as commands on that host, which on the local provider is the operator's machine. - MCPServerEnv quotes its staging lines and removes the staged file through docker_cp's remove_source, which passes the paths as arguments. - WebsiteEnv quotes the compose exec, compose cp and rm lines. - Both refuse a file name that is not one plain name (a '/', a '\', '.', '..', empty, or a control character) before anything runs, since it builds host and container paths. Tests: a real LocalSandbox runs each env's staging lines with a stub docker, for file and env names with ';', $(), backticks, quotes and spaces; the gateway integration tests load a shell-named seed into an MCP server and a website and check it loads and runs nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
added a commit
that referenced
this pull request
Oct 29, 2026
…l names its cause - A secret app setting is checked only as a reference (or null, where it's optional), never against the constraints on its value, which its value then never reaches; a required one left out is named. - A data/add, data/get or run whose connection to the driver breaks fails naming the env, the call and the cause, which httpx often leaves empty (found by the chaos run on EC2: a bare "ReadError: "). - test_artifact_staging imports LocalSandbox from local_docker, where #369 moved it; main's unit tier has failed on it since #374. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 29, 2026
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.
What and why
When a task loads an environment artifact into an MCP server or a website on a VM sandbox (the gateway deployment, which includes the default
localprovider), the env stages the file on the sandbox's host with a few bash lines. Those lines spliced in the artifact's file name and the env's name unquoted:MCPServerEnv: thedocker exec … mkdirandrm -f <staged file>lines;WebsiteEnv: thedocker compose exec,docker compose cpandrm -flines.A name holding shell syntax ran as commands on that host. On
local, that host is the operator's machine, as the operator. The file name comes from whoever wrote the bundle or the stored artifact, so loading someone else's bundle was enough. The download before these lines already quoted the path, so the file was staged fine and the load still passed: nothing showed that a command had run.This PR:
MCPServerEnvremoves the staged file throughdocker_cp(..., remove_source=True), which passes paths as arguments.WebsiteEnvquotes the service, the paths and the compose file./or\,.,.., an empty name, or a control character. The env builds host and container paths from it (/data/<name>), so a name with a/could reach outside the staging directory. Names written byFileArtifact.putand bundle folders are basenames already;put_bytes(filename=...)and stored documents could carry others.The env name itself isn't validated here. Its other uses, in the compose file and the deploy script, need their own change.
How it was tested
Unit:
tst/unit/env/envs/test_artifact_staging.py(new). A realLocalSandboxruns each env's staging lines, with a stubdockerthat logs its arguments. The cases are file and env names with;,$(), backticks, quotes and spaces: each is staged and copied as one argument, the staged file is removed, and nothing runs. Names that aren't one plain file name are refused before any command runs. Onmainthe same tests fail: the injectedtouchruns and the copy's arguments split. The full unit suite passes (11,348 passed, 66 skipped).Integration:
test_gateway_with_agentenv_environmentandtest_gateway_with_website_env_v1now also load a seed named$(touch pwned) \touch pwned`.jsonthrough a real gateway, and check that it loads and that nopwned` file appears.End to end on Linux (Ubuntu 24.04 box,
localprovider): a bundle whose environment artifact file is named$(touch pwned) \touch pwned`.json, run withagent-env run`:main(33a9eab): the task passes, andpwnedappears in the operator's working directory.pwnedfile appears.Regression on the same box, against this branch: the local-provider integration tests that deploy envs and load artifacts into them. That's
env/gateway/gateway_test.py,env/envs/server_env_local_test.py,env/envs/multi_env_local_test.py,env/task_step/task_steps_test.py,cli/run_bundle_built_envs_test.pyandcli/run_bundle_cli_folder_test.py.remote_sandbox(535),mcp_server_sources(64),default_a2a_agent(20) andmodel_endpoint_configured(8).main's source, and both fail there. The MCP server load passes, butpwnedappears. The website'sdocker compose cpline splits on the name, and the copy fails.Stress, on a second Linux box (
localprovider, this branch):-m 'not int_test_slow' -n auto) ran 3 times: 175 passed each time.modal_vm:
test_gateway_with_multi_mcp_server[modal_vm]andtest_gateway_triggers_extension[modal_vm]both passed on real Modal VM sandboxes. They load a universe and an environment artifact into MCP servers, so the changed staging path, includingdocker_cp(..., remove_source=True), runs over modal_vm's exec transport. No existing website test is parametrized by provider, so the website path ran onlocalonly.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no actionable issues were found.Summary
The PR makes artifact staging treat names as data rather than shell commands.
Scope acknowledgment:
earakely-scaleexplicitly deferred other uses of the environment name in compose generation and deployment scripts.Reviews (1) · Last reviewed commit: "fix(env): stage a loaded artifact withou..." · Reviewed by Greptile