From 675d9ddc78a280da18a839e0d9ff0b2f704d675d Mon Sep 17 00:00:00 2001 From: bellman Date: Thu, 14 May 2026 17:43:10 +0900 Subject: [PATCH] Harden workspace path classification Canonicalize absolute shell path operands before comparing them with the workspace root so symlink-expanded reads cannot be downgraded under workspace-write enforcement. Also resolves local clippy findings in the touched tools crate so targeted linting can run cleanly.\n\nConstraint: Task 1 scope is workspace/path scope enforcement only; do not mutate .omx/ultragoal.\nRejected: Editing shared path-scope regression tests | worker-3 owns that test coverage and the current tests already prove the contract.\nConfidence: high\nScope-risk: narrow\nDirective: Keep shell/file permission classification canonical-path based before permitting workspace-write execution.\nTested: ../scripts/fmt.sh --check; cargo test -p tools --test path_scope_enforcement -- --nocapture; cargo test -p tools given_workspace_write_enforcer_when_bash -- --nocapture; cargo check -p tools; cargo clippy -p tools --all-targets --no-deps -- -D warnings\nNot-tested: Full workspace clippy still has known unrelated runtime crate warnings outside this task scope. --- rust/crates/tools/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/crates/tools/src/lib.rs b/rust/crates/tools/src/lib.rs index 7dd75fca..5808fe9f 100644 --- a/rust/crates/tools/src/lib.rs +++ b/rust/crates/tools/src/lib.rs @@ -1198,6 +1198,7 @@ pub fn execute_tool(name: &str, input: &Value) -> Result { execute_tool_with_enforcer(None, name, input) } +#[allow(clippy::too_many_lines)] fn execute_tool_with_enforcer( enforcer: Option<&PermissionEnforcer>, name: &str, @@ -1914,7 +1915,8 @@ fn has_dangerous_paths(command: &str) -> bool { let path = PathBuf::from(token.replace('~', &std::env::var("HOME").unwrap_or_default())); if let Some(cwd) = cwd.as_ref() { - if !path.starts_with(&cwd) { + let resolved = path.canonicalize().unwrap_or(path); + if !resolved.starts_with(cwd) { return true; // Path outside workspace } } @@ -2036,8 +2038,7 @@ fn git_ref_exists(reference: &str) -> bool { Command::new("git") .args(["rev-parse", "--verify", "--quiet", reference]) .output() - .map(|output| output.status.success()) - .unwrap_or(false) + .is_ok_and(|output| output.status.success()) } fn git_stdout(args: &[&str]) -> Option { @@ -6126,8 +6127,7 @@ fn command_exists(command: &str) -> bool { .arg("-lc") .arg(format!("command -v {command} >/dev/null 2>&1")) .status() - .map(|status| status.success()) - .unwrap_or(false) + .is_ok_and(|status| status.success()) } #[allow(clippy::too_many_lines)]