Compare commits

...
+13
View File
@@ -668,6 +668,8 @@ pub fn add_user_word(word: &str) -> std::io::Result<()> {
return Ok(()); return Ok(());
} }
let path = user_dictionary_path().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "no folder for the user dictionary"))?; let path = user_dictionary_path().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "no folder for the user dictionary"))?;
// A confined automation client may write only inside its write roots, this file included.
crate::file_access::check_write(&path.to_string_lossy()).map_err(|e| std::io::Error::new(std::io::ErrorKind::PermissionDenied, e))?;
if let Some(d) = path.parent() { if let Some(d) = path.parent() {
std::fs::create_dir_all(d)?; std::fs::create_dir_all(d)?;
} }
@@ -953,6 +955,17 @@ SFX D Y 1\nSFX D 0 ed/S .\n";
assert_eq!(w, vec!["check", "done"]); assert_eq!(w, vec!["check", "done"]);
} }
#[test]
fn a_confined_client_cannot_write_the_user_dictionary_outside_its_roots() {
let root = std::env::temp_dir().join(format!("deckcraft-dict-root-{}", std::process::id()));
std::fs::create_dir_all(&root).unwrap();
let roots = crate::file_access::AutomationRoots::new(std::slice::from_ref(&root), std::slice::from_ref(&root)).unwrap();
// The user dictionary lives in the home folder (or a test's own folder), never in `root`.
let r = crate::file_access::confine(roots.as_ref(), || add_user_word("zqxconfinedword"));
assert_eq!(r.map_err(|e| e.kind()), Err(std::io::ErrorKind::PermissionDenied));
let _ = std::fs::remove_dir_all(&root);
}
#[test] #[test]
fn long_and_numeric_flags_and_latin1() { fn long_and_numeric_flags_and_latin1() {
let d = Dictionary::parse(b"FLAG long\nSFX Aa Y 1\nSFX Aa 0 s .\n", b"1\ncat/Aa\n"); let d = Dictionary::parse(b"FLAG long\nSFX Aa Y 1\nSFX Aa 0 s .\n", b"1\ncat/Aa\n");