fix(sandbox): validate requested branch name before shelling out in clone#4499
Merged
Conversation
…lone git.repository.branch is config-supplied and gets interpolated straight into sh -c git commands in spawnClone (ls-remote/clone/checkout) with no validation, unlike the sibling checkout path (git/checkout-branch.ts) which already rejects unsafe ref names before shelling out. Since git allows shell metacharacters in ref names, a branch like `x;touch pwned` runs as a second shell command during the very first clone. Consolidated clone.ts's own isSafeRefName (already used for the remote-controlled default-branch fetch) into the shared git/ref-name.ts validator so both call sites share one allowlist, and used it to validate the requested branch too.
decocms Bot
pushed a commit
that referenced
this pull request
Jul 13, 2026
PR: #4499 fix(sandbox): validate requested branch name before shelling out in clone Bump type: patch - @decocms/sandbox (packages/sandbox/package.json): 1.14.4 -> 1.14.5 Deploy-Scope: both
tlgimenes
added a commit
that referenced
this pull request
Jul 13, 2026
Conflict: main's #4499 (validate config-supplied branch before shelling out in clone) vs this branch's structured-command clone rewrite. Resolution: adopt main's shared isValidRemoteBranchName (git/ref-name.ts) everywhere — the branch's local isSafeRefName copy and its tests are superseded; keep main's new requested-branch guard (argv can't be shell-injected anymore, but flag-shaped/ref-format-invalid names are still rejected before reaching git); comments reworded for the argv era; main's malicious-branch tests adapted to the required askpassPath dep. Co-Authored-By: Claude Fable 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.
Source
A bug found by reading
packages/sandbox/daemon/setup/clone.tswhile looking at recent churn inpackages/sandbox/daemon/git/checkout-branch.ts(#4472, #4470 hardened the sibling checkout path against the same class of issue).The bug
spawnClonebuildssh -cgit commands (ls-remote,clone --branch,checkout -B) by interpolating the config-suppliedgit.repository.branchdirectly into the command string, with no validation. Git allows shell metacharacters (;,$(…), backticks, …) in ref names, so a branch value likex;touch pwnedrunstouch pwnedas a second shell command the momentspawnClonedoes its firstls-remoteprobe — during the very first clone of a sandbox.The same interpolation for the remote-controlled default branch (
base, read fromls-remote --symrefoutput) was already guarded by a hand-rolledisSafeRefNameallowlist in this file, and the analogous requested-branch value in the sibling checkout path (git/checkout-branch.ts) was already hardened in #4472/#4470 — this fixes the one remaining unguarded call site, and folds the two independent ref-name allowlists (clone.ts'sisSafeRefNameandgit/ref-name.ts'sassertValidRemoteBranchName) into the single validator ingit/ref-name.ts.Regression test
packages/sandbox/daemon/setup/clone.test.ts—"rejects a malicious requested branch instead of shelling it out"passes a branch ofx;touch <marker>intospawnCloneand asserts the marker file is never created andspawnClonereturns a non-zero code instead of running the injected command.To verify
Checks run locally
bun run fmtbunx tsc --noEmitinpackages/sandboxbun test packages/sandbox/daemon/setup/clone.test.ts(10 pass)bun test packages/sandbox/daemon/git/ref-name.test.ts(2 pass)bun test packages/sandbox/daemon/git/checkout-branch.test.ts(5 pass, unaffected by the shared validator change)Full CI will run the rest of the suite.
Summary by cubic
Validate config‑supplied branch names in the sandbox clone path before building
sh -cgit commands. This prevents command injection viagit.repository.branchand unifies ref-name validation ingit/ref-name.ts.Bug Fixes
spawnClonerejects invalid/malicious branch names withisValidRemoteBranchNameand exits non‑zero.base) to avoid unsafe interpolation.x;touch <file>) never execute.Refactors
isSafeRefNamewith sharedisValidRemoteBranchName/assertValidRemoteBranchNameingit/ref-name.ts.a//b).Written for commit c94c649. Summary will update on new commits.