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).
This commit is contained in:
Dylan M. Taylor
2026-10-24 05:16:20 +07:00
committed by bflatastic
parent cd043f650b
commit 42f8b513a2
2 changed files with 10 additions and 2 deletions
+6 -1
View File
@@ -144,7 +144,12 @@ fn copied_file(paths: Vec<PathBuf>, mimes: &[&'static str]) -> Option<Flavour> {
_ => 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)?;
+4 -1
View File
@@ -320,7 +320,10 @@ pub fn full_bytes_cached(p: &PlacedDocument, blob: &ImageBlob) -> Option<Arc<Vec
return Some(blob.bytes.clone());
}
let allowed = |path: &str| READ_CHECK.get().is_none_or(|ok| ok(path));
full().iter().rev().find(|(k, path, _)| *k == p.key && allowed(path)).map(|(_, _, b)| b.clone())
// Copy the candidates, newest first, so the filesystem checks run without the lock.
let candidates: Vec<(String, Arc<Vec<u8>>)> =
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`