pub enum ConfinementError {
InvalidSegment {
segment: String,
},
SegmentIsSymlink {
path: String,
},
Escape {
path: String,
},
NotADirectory {
path: String,
},
WrongTargetKind {
path: String,
},
CreateDir {
path: String,
source: Error,
},
Io(Error),
}Expand description
Errors from walking and confining a path to a base directory.
Every variant is publicly constructible only by resolve_confined_path itself; callers map
this enum into their own crate-specific error type with a total From implementation rather
than matching on it directly, since two callers give the same failure different names (e.g.
ConfinementError::WrongTargetKind means “not a directory” for one caller and “not a file”
for the other).
§Examples
use mcp_execution_core::{ConfinementError, resolve_confined_path};
use std::path::Path;
// Segment validation runs before any filesystem access, so this fails synchronously.
let err = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
.block_on(resolve_confined_path(Path::new("/base"), "..", Path::new(""), None))
.unwrap_err();
assert!(matches!(err, ConfinementError::InvalidSegment { .. }));Variants§
InvalidSegment
The path segment pushed onto the base directory (e.g. a server_id) is empty or is not
a single plain path component.
Fields
segment: StringSanitized display form of the rejected segment (see
sanitize_untrusted_inline): control
characters, bidi-reordering characters, and other invisible/structural characters are
neutralized, and &/</> are entity-escaped, since this value is
attacker-controlled and reaches LLM-facing error text. The {segment:?} (Debug)
formatting above is a required second layer of defense on top of that sanitization,
not incidental — see ServerIdError’s doc comment for why it
must not be “simplified” to {segment} (Display).
This field is pub only because the enum itself is; resolve_confined_path is the
sole constructor of this variant, and it always passes an already-sanitized string
here. A caller building this variant directly (there are none in this workspace) must
sanitize the value the same way, or a raw value reaches every downstream consumer that
embeds this field (see mcp-execution-server’s OutputDirError::InvalidServerId and
mcp-execution-skill’s OutputPathError::InvalidServerId, both of which move this
field verbatim into their own server_id without re-sanitizing).
SegmentIsSymlink
The segment’s own directory already exists as a symlink, which is rejected outright regardless of where it points - including at a sibling directory that still resolves inside the base, which would otherwise pass a resolve-and-confine check (issue #217).
Escape
The resolved path escapes the segment directory, typically because a path component resolved through (or is itself) a symlink that points outside it.
NotADirectory
A path component that must be a directory already exists as something else (e.g. a regular file).
WrongTargetKind
The terminal component already exists as the kind of entry ConfinementTarget says it
isn’t (a file where ConfinementTarget::Directory was expected, or a directory where
ConfinementTarget::File was expected).
CreateDir
Creating a directory needed along the path failed.
Fields
Io(Error)
I/O error resolving the base directory or a path component.
Trait Implementations§
Source§impl Debug for ConfinementError
impl Debug for ConfinementError
Source§impl Display for ConfinementError
impl Display for ConfinementError
Source§impl Error for ConfinementError
impl Error for ConfinementError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()