Skip to main content

evaluate_path_safe

Function evaluate_path_safe 

Source
pub fn evaluate_path_safe(
    rules: &RuleSet,
    kind: PathKind,
    root: &Path,
    raw_path: &str,
    default: Decision,
) -> Decision
Expand description

SECURITY (CRITICAL fix, guarantor audit, traced to this file’s former evaluate_path doc comment claiming “no canonicalization … is involved here … no unparseable case to fail closed on” — the flawed assumption that let a traversal payload bypass protected_paths): the safe entry point for evaluating a RAW, model-supplied path tool argument (relative or absolute, exactly as it arrives in args["path"]) as a "read"/"write" pseudo-tool subject. Resolves raw_path against root through crate::safe_path::resolve_for_matching — the SAME dual lexical+symlink-resolved check crate::checkpoint’s P5-9 fix uses — and folds evaluate_path against the RAW subject, the lexically-normalized project-relative form, AND the symlink-resolved project-relative form down to the single strictest Decision (ties broken toward stricter, via Decision::stricter), so a rule can never be satisfied by matching only one of these three views.

This is what closes the CRITICAL bug: write_file path="x/../.git/config" with protected_paths=[".git/**"] — the raw subject "x/../.git/config" does not match, but the resolved subject ".git/config" does, so the fold still lands on Decision::Deny.

FAIL-CLOSED: if root/raw_path cannot be proven safe (a resolution error, or raw_path lexically looks contained but symlink-resolves OUTSIDE root — see crate::safe_path::PathForMatching::Unsafe), the result is escalated to at least Decision::Deny, never silently falling through to default. A path that legitimately resolves outside root entirely (e.g. an absolute write elsewhere under SandboxPolicy::DangerFullAccess) is NOT penalized for that alone — only the raw-subject match applies to it, exactly as before this fix (no over-block).