Repository navigation
fix(sandbox): keep document and seed values out of host shell code - #394
Merged
Merged
Conversation
…t scripts A signed GET or PUT URL went into the VM host's script inside double quotes, where bash still expands $(...) and backticks; a Cloud Storage URL carries the bucket name of a stored artifact as written. The agent container's run script pasted env var names, the container name and the image name bare, and install_cli pasted the CLI's entrypoint path bare into its chmod. Each is now shell-quoted; a value with no shell syntax renders exactly as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l fields substitute_seed_in_steps replaced each <key> raw and key by key, so a seed row's text became part of a step's command before the step's own quoting could see a placeholder, and substituted text was re-scanned for other keys. Placeholders are now rendered in one left-to-right pass. A step declares the fields it runs as shell text (TaskStep.shell_fields); a seed value bound for one must be text the shell reads literally, or the run is refused before any step starts. Every other field takes any value, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e refused seed in its error Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…failure messages verify_sandbox declared all of criteria as shell text, so a seed bound for a probe_file_contains regex or a path was refused although it never reaches a shell; shell_fields now names fields by their path in the stored step, and verify_sandbox declares only criteria.bash_cmd. run_docker_container's missing-Dockerfile and readiness messages put dockerfile_path and container_name inside single quotes in a host script; each message is quoted whole. The verifier's timeout_sec is quoted in its timeout call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tution The per-step shell_fields declaration and the refusal of shell syntax in seeds bound for them are removed: they added a concept every step author would have to know. Seeds are still rendered in one left-to-right pass, so substituted text is never re-scanned, and every quoting fix at the script sinks stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…belongs in the env vars Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
@greptileai review |
moto checks a conditional write's condition and stores the item as two steps, so two threads can both pass the same condition and one write is lost; DynamoDB evaluates the condition atomically. The document store's tests already serialized botocore calls for their racing cases; the step-journal suite's concurrent-completions test did not, and lost a completion now and then in CI. The serialization moves to the AWS tests' conftest as a fixture both use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
Places where a value from a stored document became shell code on a sandbox host or in an env container are now quoted. On the
localprovider that host is the operator's machine. Seed placeholders are also rendered in one pass.Quoted at the sink (
sandbox.py,a2a_agent.py,run_docker_container.py, the unit-tests verifier):_load_docker_images) put the signed GET URL inside double quotes, where bash still expands$(...)and backticks. A Cloud Storage URL carries the bucket name of a storedDockerImageArtifactas written.upload_vm_filedid the same with the signed PUT URL. Both are nowshlex.quoted. Every provider's_load_tarballsoverride goes through this base function.A2AAgent._run_container). Env var names, the container name and the image name went in bare, and an agent document'sdefault_env_varskeys are free text. All three are now quoted. The value keeps its existing'...'\''...'form because sail_vm's model-key scrubber matches the key in that encoding. A normal key and value render exactly as before.install_cli's chmod. The CLI's/opt/cli/<command_name>/<entrypoint>path, taken from the stored CliArtifact, went intodocker exec ... chmod +xunquoted. It is now quoted. The host-mode branch already quoted it.run_docker_container's failure messages. The missing-Dockerfile and readiness messages putdockerfile_pathandcontainer_nameinside single quotes in a host script. Each message is now quoted whole.timeout_secis quoted in itstimeoutcall.Seeds (
task.py):substitute_seed_in_stepsreplaced each<key>key by key, so substituted text was re-scanned. A value of<b>was then rewritten by a later keyb. It now renders all placeholders in one left-to-right pass.A flaky unit test (
tst/unit/store/aws/):dynamodb_step_journal_test.py::test_concurrent_completions_all_journal_and_an_undo_replays_the_survivorsfailed now and then in CI, on this PR and on other branches.put_item's condition and stores the item as two steps, so two threads can both pass the same condition and one completion is lost. DynamoDB evaluates the condition atomically, so the store's code is correct.dynamodb_document_store_test.pyalready ran its racing cases one botocore call at a time for exactly this reason. That serialization is now aconftest.pyfixture,one_aws_call_at_a_time, which both suites use.Not in this PR
command, averify_sandboxbash_cmdor arun_docker_containercommand still becomes part of that command, as before.shell_fieldsdeclaration. It was dropped so step authors don't have a new concept to learn."$INPUT_URL", not a placeholder.tar_gz_object_urland CliArtifact names when they're written, refusing foreign buckets inGcsObjectStore, and refusing non-identifier env var names when an agent is read. Each sink now passes the value as data, so these aren't needed to stop the injections fixed here.run_docker_containerenv var names. They're still pasted bare. They come from the task document, whose author already runs shell on that host by design.Testing
curl/gunzip/docker/seqcommands that only record their arguments (tst/util/exec_scripts.run_with_stubs). The hostile value reaches the command as one literal argument, and nopwnedfile appears. Covered: the signed GET and PUT URLs, the agent env var name, the CLI chmod, and the Dockerfile and readiness messages. Each fails on main and passes here.tst/unit/task/seed_substitution_test.py: placeholders take their values, including inside double quotes, and substituted text isn't re-scanned.vm_sandbox_test.pyandvm_file_transfer_test.pylose the double quotes around the signed URL. OneAsyncMocksandbox now sets a realcontainer_name.test_hermes_bridge.py::test_the_event_pipe_is_not_left_open_in_the_serverreads/proc/self/fd, which only Linux has.task_run_teardown_test.py::test_keep_still_tears_down_a_run_ctrl_c_cancelspassed 3 of 3 times when run alone.ruff checkis clean.82880f0e), every CI check passed, including CodeBuild'stest_task_steps_e2e[modal_vm]. That test loads S3 image tarballs into Modal VMs through the re-quoted signed-URL download.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge within its stated scope.Summary
Quotes stored document values before placing them in shell scripts and renders seed placeholders in one pass.
Reviews (6) · Last reviewed commit: "test(store): run the DynamoDB step-journ..." · Reviewed by Greptile