Prevent workspace escape through tool path resolution

File and shell tool dispatch now resolves path-sensitive operations through workspace-scoped wrappers so direct paths, globs, symlinks, shell expansion, and Windows absolute path probes fail before execution when they leave the workspace.

Constraint: G002-alpha-security requires alpha-blocking workspace/path scope enforcement without mutating .omx/ultragoal

Rejected: string-prefix only checks | they miss canonical symlink and glob expansion escapes

Confidence: high

Scope-risk: moderate

Directive: keep new file/shell tool entrypoints wired through workspace-aware wrappers before dispatch

Tested: python3 -m unittest discover -s tests -v; python3 -m compileall -q src tests; cargo test -p runtime workspace --manifest-path rust/Cargo.toml --quiet; cargo test -p tools workspace --manifest-path rust/Cargo.toml --quiet; cargo test -p tools given_workspace_write_enforcer_when_bash --manifest-path rust/Cargo.toml --quiet; cargo test -p tools file_tools_reject --manifest-path rust/Cargo.toml --quiet; cargo fmt --all --manifest-path rust/Cargo.toml -- --check; cargo check --manifest-path rust/Cargo.toml --workspace

Not-tested: full unfiltered cargo test workspace due task-time constraints; targeted runtime/tools workspace security tests and full cargo check passed

Co-authored-by: OmX <omx@oh-my-codex.dev>
This commit is contained in:
bellman
2026-05-14 17:29:48 +09:00
parent 9bc55f9946
commit f2dc615a8a

View File

@@ -8693,8 +8693,12 @@ mod tests {
let _guard = env_lock()
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let path = temp_path("subagent-input.txt");
let root = temp_path("subagent-runtime");
std::fs::create_dir_all(&root).expect("create root");
let path = root.join("subagent-input.txt");
std::fs::write(&path, "hello from child").expect("write input file");
let original_dir = std::env::current_dir().expect("cwd");
std::env::set_current_dir(&root).expect("set cwd");
let mut runtime = ConversationRuntime::new(
Session::new(),
@@ -8726,7 +8730,8 @@ mod tests {
if output.contains("hello from child")
)));
let _ = std::fs::remove_file(path);
std::env::set_current_dir(&original_dir).expect("restore cwd");
let _ = std::fs::remove_dir_all(root);
}
#[test]
@@ -9787,11 +9792,14 @@ printf 'pwsh:%s' "$1"
fs::create_dir_all(&root).expect("create root");
let file = root.join("readable.txt");
fs::write(&file, "content\n").expect("write test file");
let original_dir = std::env::current_dir().expect("cwd");
std::env::set_current_dir(&root).expect("set cwd");
let registry = read_only_registry();
let result = registry.execute("read_file", &json!({ "path": file.display().to_string() }));
assert!(result.is_ok(), "read_file should be allowed: {result:?}");
std::env::set_current_dir(&original_dir).expect("restore cwd");
let _ = fs::remove_dir_all(root);
}