diff options
| author | main <main@swarm.moe> | 2026-03-25 20:20:06 -0400 |
|---|---|---|
| committer | main <main@swarm.moe> | 2026-03-25 20:20:06 -0400 |
| commit | 2bb470f0b28b75863809f127b7f12222db07496c (patch) | |
| tree | 3c6f7a1917bcd80edfafba8fd1acf00141de725d /crates/phone-opus/src/mcp/service.rs | |
| parent | 0dda9748e55bcffe6194c4b1d15f10c7753e8b29 (diff) | |
| download | phone_opus-2bb470f0b28b75863809f127b7f12222db07496c.zip | |
Bypass current_dir for absolute consult cwd
Diffstat (limited to 'crates/phone-opus/src/mcp/service.rs')
| -rw-r--r-- | crates/phone-opus/src/mcp/service.rs | 65 |
1 files changed, 57 insertions, 8 deletions
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<String>) -> Result<Self, ConsultRequestError> { - 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<PathBuf>, + ) -> Result<PathBuf, ConsultRequestError> { + 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<Self, ConsultRequestError> { 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")); + } +} |