From ccdb86aee6caab9d230ba0849e31a0b5062c813a Mon Sep 17 00:00:00 2001 From: Hardonian <118695431+Hardonian@users.noreply.github.com> Date: Fri, 29 May 2026 05:02:46 +0000 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20?= =?UTF-8?q?[HIGH]=20Fix=20shell=20command=20injection=20vulnerability=20in?= =?UTF-8?q?=20sandbox=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 5 +++++ src/lib/state/sandbox.ts | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 96cdd2a20b..ceb05c7376 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -5,3 +5,8 @@ **Learning:** When invoking external commands like `docker`, one should prefer spawning the command directly with its arguments in an array instead of concatenating them into a shell string. **Prevention:** Use `run` instead of `runShell` where possible, taking advantage of the argv array parameter. Avoid passing un-sanitized user input into strings executed via the shell. + +## 2026-05-29 - [Sandbox SSH Command Injection] +**Vulnerability:** Shell Command Injection via interpolation in `spawnSync('ssh')` when clearing sandbox state directories. +**Learning:** `shellQuote` and shell array expansions can be unreliable when passed through SSH command boundaries, leading to potential RCE if filenames are attacker-controlled. +**Prevention:** Avoid shell string interpolation for dynamic lists. Pass arguments as null-terminated strings via `stdin` to remote `xargs -0` for deterministic, un-interpolated execution. diff --git a/src/lib/state/sandbox.ts b/src/lib/state/sandbox.ts index c04a7f86a6..026900ed70 100644 --- a/src/lib/state/sandbox.ts +++ b/src/lib/state/sandbox.ts @@ -1205,10 +1205,12 @@ function restoreStateDirs( // Remove existing state dirs before extracting so stale files from // later snapshots don't persist after restoring an earlier one. - const rmCmd = localDirs.map((d) => `rm -rf -- ${shellQuote(`${dir}/${d}`)}`).join(" && "); - _log(`Cleaning target dirs before restore: ${rmCmd}`); + const rmCmd = "xargs -0 rm -rf --"; + const rmInput = localDirs.map((d) => `${dir}/${d}`).join("\0"); + _log(`Cleaning target dirs before restore via xargs null-terminated pipe`); const rmResult = spawnSync("ssh", [...sshArgs(configFile, sandboxName), rmCmd], { - stdio: ["ignore", "pipe", "pipe"], + input: rmInput, + stdio: ["pipe", "pipe", "pipe"], timeout: 30000, }); if (rmResult.status !== 0 || rmResult.error || rmResult.signal) { From 831d31c30557352d55900370469ceeb67a5f971a Mon Sep 17 00:00:00 2001 From: Hardonian <118695431+Hardonian@users.noreply.github.com> Date: Fri, 29 May 2026 05:04:47 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20?= =?UTF-8?q?[HIGH]=20Fix=20shell=20command=20injection=20vulnerability=20in?= =?UTF-8?q?=20sandbox=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 01b07aed69b89f053248e6332e9c82cad95ccbd3 Mon Sep 17 00:00:00 2001 From: Hardonian <118695431+Hardonian@users.noreply.github.com> Date: Fri, 29 May 2026 05:41:41 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20?= =?UTF-8?q?[HIGH]=20Fix=20shell=20command=20injection=20vulnerability=20in?= =?UTF-8?q?=20sandbox=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit