MCP: a failed request's error hides a file URL it sent that names another host

The error shows the URL's file name, as it does for a local path the request sent outside the read roots.
This commit is contained in:
dotCooCoo
2026-11-11 16:43:49 -08:00
parent db8726e94b
commit cce2a685bc
2 changed files with 14 additions and 6 deletions
+2 -1
View File
@@ -450,9 +450,10 @@ fn sent_paths(v: &Value, out: &mut Vec<(String, String)>) {
if !unreadable_path_like(s) {
return;
}
// A URL that names another host as the path after its scheme (`//host/share/…`).
let path = match file_url_path(s) {
Some(Some(path)) => path,
Some(None) => return,
Some(None) => percent_decoded(&s[5..]),
None => s.to_string(),
};
let Some(name) = path.rsplit(['/', '\\']).find(|part| !part.is_empty()).filter(|name| *name != path) else { return };
+12 -5
View File
@@ -120,9 +120,9 @@ fn a_sent_path_replaces_a_hidden_id_for_the_same_path() {
assert_eq!(hidden.hidden.len(), 3, "{hidden:?}");
}
/// The paths outside the read root that a request sends, as a path, a `file:` URL, under `~` or
/// starting with `../`, are hidden by their file names; a URL that names another host, plain text and
/// a path inside the root are not.
/// The paths outside the read root that a request sends, as a path, a `file:` URL (also one that names
/// another host), under `~` or starting with `../`, are hidden by their file names; plain text and a
/// path inside the root are not.
#[test]
fn a_request_sends_paths_that_an_error_hides() {
let base = std::env::temp_dir().join(format!("vc-sent-paths-{}", std::process::id()));
@@ -130,13 +130,20 @@ fn a_request_sends_paths_that_an_error_hides() {
std::fs::create_dir_all(&root).unwrap();
let inside = root.join("a.svg").to_string_lossy().into_owned();
let keep = json!(["a/b", inside, "file://host/g.svg"]);
let params = json!({"path": "/x/my lib/b.svg", "list": ["file:///y/c%20d.svg", "~/e.svg", "../f.svg"], "keep": keep});
let params = json!({"path": "/x/my lib/b.svg", "list": ["file:///y/c%20d.svg", "~/e.svg", "../f.svg"], "other": keep});
let mut sent = Vec::new();
let roots = vectorcraft_engine::file_access::AutomationRoots::new(&[&root], &[]).unwrap();
vectorcraft_engine::file_access::confine(roots.as_ref(), || super::sent_paths(&params, &mut sent));
let _ = std::fs::remove_dir_all(base);
let pair = |path: &str, name: &str| (format!("loaded/{path}"), format!("loaded/{name}"));
assert_eq!(sent, [pair("/y/c d.svg", "c d.svg"), pair("~/e.svg", "e.svg"), pair("../f.svg", "f.svg"), pair("/x/my lib/b.svg", "b.svg")]);
let want = [
pair("/y/c d.svg", "c d.svg"),
pair("~/e.svg", "e.svg"),
pair("../f.svg", "f.svg"),
pair("//host/g.svg", "g.svg"),
pair("/x/my lib/b.svg", "b.svg"),
];
assert_eq!(sent, want);
}
/// Under a read root, an absolute path or a `file:` URL outside it is unreadable; an empty string, a