Skip to main content

max / audiofiles

security: remove dead session-restore, fix Rhai threat-model comment (ultra-fuzz) try_restore_session guarded its whole body on session_info().is_some(), which is always false at startup (the client is built with no session and nothing ever calls restore_session), so it was dead code implying a session persistence the app deliberately does not have — the auth JWT is memory-only, which is the security-positive property to preserve. Delete the method and its call site, documenting the intentional no-token-at-rest posture so the scaffolding can't invite a future token-at-rest regression. Also correct the Rhai wall-clock-limit comment: export/rename hooks run on the GUI thread, so the 5 s cap bounds UI-freeze impact, not a background worker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-19 20:49 UTC
Commit: a954d9f0c765f70c41aa276f5ae444864f3f818f
Parent: 71716cc
3 files changed, +16 insertions, -16 deletions
@@ -250,7 +250,8 @@ fn create_sync_manager(
250 250 let content_dir = data_dir.join("samples");
251 251 let manager = SyncManager::new(config, db_path, content_dir, runtime.clone());
252 252 manager.fetch_pricing();
253 - manager.try_restore_session();
253 + // No session restore: the auth JWT is memory-only by design, so the user
254 + // re-runs OAuth each launch (nothing is persisted to restore).
254 255 manager.start_scheduler();
255 256 Some(manager)
256 257 }
@@ -9,9 +9,11 @@ use crate::host_api;
9 9 use crate::types::{self, RhaiExportContext, RhaiSampleInfo};
10 10
11 11 /// Wall-clock ceiling for a single script run. `set_max_operations` already
12 - /// bounds interpreter ops, but this also caps total elapsed time so a script
13 - /// that spends a long time across many operations can't hang the rename/import
14 - /// thread.
12 + /// bounds interpreter ops; this also caps total elapsed time so a script that
13 + /// spends a long time across many operations can't hang the thread that drives
14 + /// the eval. Export/rename hooks currently run synchronously on the GUI thread,
15 + /// so this limit is what bounds a runaway plugin's impact on UI responsiveness
16 + /// (a worst case of ~5 s of frozen UI, not a frozen background worker).
15 17 const SCRIPT_WALL_CLOCK_LIMIT: Duration = Duration::from_secs(5);
16 18
17 19 thread_local! {
@@ -307,18 +307,15 @@ impl SyncManager {
307 307 s.device_id = None;
308 308 }
309 309
310 - /// Try to restore a previous session from keychain on startup.
311 - #[instrument(skip_all)]
312 - pub fn try_restore_session(&self) {
313 - if self.client.session_info().is_some() {
314 - let has_key = self.client.try_load_key_from_keychain().unwrap_or(false);
315 - if has_key {
316 - let mut s = self.status.lock();
317 - s.state = SyncState::Ready;
318 - load_sync_settings_into_status(&self.db_path, &mut s);
319 - }
320 - }
321 - }
310 + // Note: there is deliberately no startup session-restore. The auth JWT is
311 + // held only in process memory and never persisted, so there is nothing to
312 + // restore on launch — the user re-runs OAuth each session. This is a
313 + // security choice (no auth token at rest to steal or log), not an
314 + // oversight. A prior `try_restore_session` guarded its whole body on
315 + // `session_info().is_some()`, which is always false at startup, so it was
316 + // dead code that implied a persistence the app does not (and should not)
317 + // have; it was removed. If persistent sessions are ever wanted, restore the
318 + // token via the OS keychain only, never a file.
322 319
323 320 /// Fetch the pricing formula from the server (async, no JWT needed).
324 321 /// Stored on the status so UI code can quote a price for any cap locally.