diff --git a/scripts/clips/record.mjs b/scripts/clips/record.mjs index 233bba38..7a3080af 100644 --- a/scripts/clips/record.mjs +++ b/scripts/clips/record.mjs @@ -35,9 +35,21 @@ const flag = (name) => { return hit.includes("=") ? hit.split("=").slice(1).join("=") : true; }; const wanted = args.filter((arg) => !arg.startsWith("--")); -const work = path.resolve( - flag("work") || path.join(os.tmpdir(), "donut-clips"), -); + +/** The default scratch folder in the shared temp dir, closed to other users. */ +function privateTempDir(name) { + const dir = path.join(os.tmpdir(), name); + fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); + if (process.platform === "win32") return dir; + const stat = fs.lstatSync(dir); + if (!stat.isDirectory() || stat.uid !== process.getuid()) { + throw new Error(`${dir} is not your own folder; pass --work=DIR`); + } + fs.chmodSync(dir, 0o700); + return dir; +} + +const work = path.resolve(flag("work") || privateTempDir("donut-clips")); const themes = flag("theme") ? [flag("theme")] : THEMES; for (const id of wanted) { if (!CLIPS[id]) throw new Error(`Unknown clip: ${id}`); @@ -311,6 +323,7 @@ async function record(clip, name, theme) { fs.writeFileSync( path.join(framesDir, "frames.json"), JSON.stringify(take.frames), + { mode: 0o600 }, ); return take.frames; } diff --git a/scripts/clips/take.mjs b/scripts/clips/take.mjs index 51993a28..d2fbf964 100644 --- a/scripts/clips/take.mjs +++ b/scripts/clips/take.mjs @@ -353,7 +353,7 @@ export class Take { this.framesDir, `${String(this.frames.length).padStart(5, "0")}.png`, ); - fs.writeFileSync(file, Buffer.from(data, "base64")); + fs.writeFileSync(file, Buffer.from(data, "base64"), { mode: 0o600 }); this.frames.push({ t, file }); } } diff --git a/src-tauri/src/agent_tasks/chat_sync/syncer.rs b/src-tauri/src/agent_tasks/chat_sync/syncer.rs index 1fab6d06..e8e52061 100644 --- a/src-tauri/src/agent_tasks/chat_sync/syncer.rs +++ b/src-tauri/src/agent_tasks/chat_sync/syncer.rs @@ -85,7 +85,7 @@ pub struct Files { } impl Files { - fn secrets(&self) -> PathBuf { + fn key_file(&self) -> PathBuf { self.dir.join("key.dat") } @@ -98,7 +98,7 @@ impl Files { } fn load_secrets(&self) -> Option { - match crate::vault::open(&self.secrets(), SECRETS_MAGIC) { + match crate::vault::open(&self.key_file(), SECRETS_MAGIC) { Ok(Some(text)) => serde_json::from_str(&text).ok(), Ok(None) => None, Err(e) => { @@ -112,7 +112,7 @@ impl Files { let Ok(text) = serde_json::to_string(secrets) else { return; }; - if let Err(e) = crate::vault::seal(&self.secrets(), SECRETS_MAGIC, &text) { + if let Err(e) = crate::vault::seal(&self.key_file(), SECRETS_MAGIC, &text) { log::warn!("Agent chat sync key save failed err=\"{e}\""); } } diff --git a/src-tauri/src/cookie_health.rs b/src-tauri/src/cookie_health.rs index 3a93e188..51c2901d 100644 --- a/src-tauri/src/cookie_health.rs +++ b/src-tauri/src/cookie_health.rs @@ -309,6 +309,10 @@ pub fn log_before_launch(profile_id: &str, profile_data_dir: &Path) { mod tests { use super::*; + fn random_key() -> [u8; 32] { + rand::random() + } + fn seal(key: &[u8; 32], host: &str, value: &[u8]) -> Vec { use sha2::{Digest, Sha256}; let mut plain = Sha256::digest(host.as_bytes()).to_vec(); @@ -343,8 +347,8 @@ mod tests { fn reports_the_oldest_and_newest_rows_separately() { let dir = tempfile::tempdir().unwrap(); let db = dir.path().join("Cookies"); - let key = [7u8; 32]; - let other = [9u8; 32]; + let key = random_key(); + let other = random_key(); // Inserted newest first, so rowid order differs from creation order. cookie_db( &db, @@ -374,8 +378,8 @@ mod tests { fn a_newest_row_that_does_not_open_is_an_error() { let dir = tempfile::tempdir().unwrap(); let db = dir.path().join("Cookies"); - cookie_db(&db, &[(1, seal(&[1u8; 32], "a.test", b"x"))]); - let store = inspect_cookies(&db, Some(&[2u8; 32])).unwrap(); + cookie_db(&db, &[(1, seal(&random_key(), "a.test", b"x"))]); + let store = inspect_cookies(&db, Some(&random_key())).unwrap(); let (level, line) = report("p", KeyFile::Present(32), KeyChange::Changed, Some(&store)); assert_eq!(level, log::Level::Error); assert!(line.contains("key_changed=yes"));