From 2bb470f0b28b75863809f127b7f12222db07496c Mon Sep 17 00:00:00 2001 From: main Date: Wed, 25 Mar 2026 20:20:06 -0400 Subject: Bypass current_dir for absolute consult cwd --- crates/phone-opus/src/mcp/service.rs | 65 +++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) (limited to 'crates/phone-opus/src/mcp') diff --git a/crates/phone-opus/src/mcp/service.rs b/crates/phone-opus/src/mcp/service.rs index 4a65e6d..051ff83 100644 --- a/crates/phone-opus/src/mcp/service.rs +++ b/crates/phone-opus/src/mcp/service.rs @@ -250,14 +250,39 @@ struct WorkingDirectory(PathBuf); impl WorkingDirectory { fn resolve(raw: Option) -> Result { - let base = - std::env::current_dir().map_err(|source| ConsultRequestError::CurrentDir { source })?; - let requested = raw.map_or_else(|| base.clone(), PathBuf::from); - let candidate = if requested.is_absolute() { - requested - } else { - base.join(requested) - }; + let requested = raw.map(PathBuf::from); + let base = requested + .as_ref() + .is_none_or(|path| !path.is_absolute()) + .then(|| { + std::env::current_dir().map_err(|source| ConsultRequestError::CurrentDir { source }) + }) + .transpose()?; + let candidate = Self::resolve_candidate(base.as_deref(), requested)?; + Self::canonicalize_directory(candidate) + } + + fn resolve_candidate( + base: Option<&Path>, + requested: Option, + ) -> Result { + match requested { + Some(path) if path.is_absolute() => Ok(path), + Some(path) => Ok(Self::require_base(base)?.join(path)), + None => Ok(Self::require_base(base)?.to_path_buf()), + } + } + + fn require_base(base: Option<&Path>) -> Result<&Path, ConsultRequestError> { + base.ok_or_else(|| ConsultRequestError::CurrentDir { + source: io::Error::new( + io::ErrorKind::NotFound, + "current working directory is unavailable", + ), + }) + } + + fn canonicalize_directory(candidate: PathBuf) -> Result { let canonical = candidate .canonicalize() @@ -2252,3 +2277,27 @@ fn generation_from_wire(raw: u64) -> Generation { } generation } + +#[cfg(test)] +mod tests { + use super::WorkingDirectory; + use std::path::{Path, PathBuf}; + + #[test] + fn absolute_working_directory_resolution_bypasses_base() { + let absolute = PathBuf::from("/tmp"); + let nonexistent = Path::new("/definitely/not/a/real/base"); + let resolved = + WorkingDirectory::resolve_candidate(Some(nonexistent), Some(absolute.clone())).unwrap(); + assert_eq!(resolved, absolute); + } + + #[test] + fn relative_working_directory_resolution_uses_base() { + let base = Path::new("/tmp"); + let resolved = + WorkingDirectory::resolve_candidate(Some(base), Some(PathBuf::from("phone_opus"))) + .unwrap(); + assert_eq!(resolved, PathBuf::from("/tmp/phone_opus")); + } +} -- cgit v1.2.3