From 4cc64572d3cddacc3845ff8a7108f004843950dd Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 7 Nov 2026 07:23:49 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20command=20validation=20bypass=20for=20restricted=20bin?= =?UTF-8?q?aries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Severity: CRITICAL Vulnerability: Shell agent validation could be bypassed for restricted binaries (like `rm`, `sudo`, etc.) when used as subcommands (e.g. `npm run rm`). This happened because the joined command string didn't perfectly match the `BLOCKED_TOKENS` whitespace expectations (e.g. `"rm "` vs `"rm"` at the end of the string). Impact: Allowed bypassing shell security restrictions to run potentially destructive commands through valid package manager wrappers. Fix: Added explicit token-by-token exact matching to catch restricted binaries regardless of their position in the command arguments. Verification: Covered by new exact match tests in the workdir_cmd tests module. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ crates/agent/src/tools/workdir_cmd.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 1a9528255..0c360279e 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -17,3 +17,7 @@ **Vulnerability:** The `verify_github_sig` function in `apps/server/src/infra_routes.rs` verified GitHub webhook signatures using standard string comparison `expected.eq_ignore_ascii_case(hex_sig)`. **Learning:** String comparison functions exit early as soon as a character mismatch is found. In a cryptographic context, this variable response time leaks information and allows an attacker to perform a timing attack to forge a valid HMAC signature by guessing it byte by byte. **Prevention:** Always use a constant-time comparison library (like `hmac`'s `verify_slice()`) to compare cryptographic hashes, secrets, and signatures to prevent side-channel timing attacks. Avoid custom implementations of standard cryptographic algorithms. +## 2026-11-07 - [Strict Binary Enforcement in Shell Agent] +**Vulnerability:** Shell agent validation only matched joined strings against the `BLOCKED_TOKENS` blocklist. If a blocked command token did not match whitespace boundaries correctly within the overall command string (e.g. `npm run rm` rather than `rm `), restricted binaries like `rm` could bypass validation. +**Learning:** Checking a single joined command string for blocked tokens is insufficient because word boundaries might not line up cleanly when parsing complex subcommands. Multi-word checks or partial substring blocks require both full-string substring checks AND exact-word token matching. +**Prevention:** Always perform tokenization and exact matching on word boundaries in addition to substring blocking. diff --git a/crates/agent/src/tools/workdir_cmd.rs b/crates/agent/src/tools/workdir_cmd.rs index 46a5ff54a..e983d0bca 100644 --- a/crates/agent/src/tools/workdir_cmd.rs +++ b/crates/agent/src/tools/workdir_cmd.rs @@ -112,6 +112,14 @@ fn validate_command(raw: &str) -> std::result::Result, String> { return Err(format!("token interdit dans la commande : {bad}")); } } + + for part in &parts { + let p_lower = part.to_ascii_lowercase(); + if p_lower == "rm" || p_lower == "sudo" || p_lower == "chmod" || p_lower == "chown" { + return Err(format!("token exact interdit : {part}")); + } + } + let bin = parts[0].as_str(); let bin_base = Path::new(bin) .file_name() @@ -355,6 +363,14 @@ mod tests { assert!(validate_command("npm run \"s\"udo").is_err()); assert!(validate_command("cat \"..\"/\"..\"/etc/passwd").is_err()); } + + #[test] + fn blocks_exact_match_sensitive_binaries() { + assert!(validate_command("npm run rm").is_err()); + assert!(validate_command("npm run sudo").is_err()); + assert!(validate_command("npm run chmod").is_err()); + assert!(validate_command("npm run chown").is_err()); + } } /// Tue le groupe de processus (npm et ses scripts enfants) si le futur est abandonné.