From 42f8b513a29407d972697e2e2602b75644172bb2 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 23 Oct 2026 17:52:44 -0400 Subject: [PATCH] Fix clipboard read-policy bypass and lock-held file checks - copied_file: check the read policy on the real path when it is valid UTF-8; skip non-UTF-8 paths while automation roots are active instead of checking a lossy name (path traversal via symlink, CodeRabbit #3872 thread 1). - full_bytes_cached: collect matching cache candidates under the FULL lock, then run the read checks after releasing it, so filesystem walks no longer stall other FULL users (thread 2). --- apps/vectorcraft/src/clipboard.rs | 7 ++++++- crates/doc/src/placed_document.rs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/vectorcraft/src/clipboard.rs b/apps/vectorcraft/src/clipboard.rs index c749cadcc..957d302d5 100644 --- a/apps/vectorcraft/src/clipboard.rs +++ b/apps/vectorcraft/src/clipboard.rs @@ -144,7 +144,12 @@ fn copied_file(paths: Vec, mimes: &[&'static str]) -> Option { _ => path, }; // A paste sent by automation reads only what its roots allow. - vectorcraft_engine::file_access::check_read(&path.to_string_lossy()).ok()?; + match path.to_str() { + Some(p) => vectorcraft_engine::file_access::check_read(p).ok()?, + // A lossy name would check another file than the one opened. + None if vectorcraft_engine::file_access::current().is_none() => {} + None => return None, + } let mut head = Vec::with_capacity(FILE_HEAD); std::fs::File::open(&path).ok()?.take(FILE_HEAD as u64).read_to_end(&mut head).ok()?; let mime = file_flavour(&path.to_string_lossy(), &head, mimes)?; diff --git a/crates/doc/src/placed_document.rs b/crates/doc/src/placed_document.rs index 8de94bdfd..a94edf42a 100644 --- a/crates/doc/src/placed_document.rs +++ b/crates/doc/src/placed_document.rs @@ -320,7 +320,10 @@ pub fn full_bytes_cached(p: &PlacedDocument, blob: &ImageBlob) -> Option>)> = + full().iter().rev().filter(|(k, _, _)| *k == p.key).map(|(_, path, b)| (path.clone(), b.clone())).collect(); + candidates.into_iter().find(|(path, _)| allowed(path)).map(|(_, b)| b) } /// The file's bytes: stored, read again already, or read again now ([`set_file_reader`]). `None`