diff --git a/codex-rs/windows-sandbox-rs/src/deny_read_state.rs b/codex-rs/windows-sandbox-rs/src/deny_read_state.rs index fd073a4d45..277d72ce9e 100644 --- a/codex-rs/windows-sandbox-rs/src/deny_read_state.rs +++ b/codex-rs/windows-sandbox-rs/src/deny_read_state.rs @@ -69,8 +69,13 @@ pub unsafe fn sync_persistent_deny_read_acls( fn load_state(path: &Path) -> Result { match std::fs::read(path) { - Ok(bytes) => serde_json::from_slice(&bytes) - .with_context(|| format!("parse deny-read ACL state {}", path.display())), + Ok(bytes) => match serde_json::from_slice(&bytes) { + Ok(state) => Ok(state), + Err(_) => { + quarantine_corrupt_state_file(path); + Ok(PersistentDenyReadAclState::default()) + } + }, Err(err) if err.kind() == std::io::ErrorKind::NotFound => { Ok(PersistentDenyReadAclState::default()) } @@ -85,3 +90,55 @@ fn store_state(path: &Path, state: &PersistentDenyReadAclState) -> Result<()> { std::fs::write(path, bytes) .with_context(|| format!("write deny-read ACL state {}", path.display())) } + +fn quarantine_corrupt_state_file(path: &Path) { + let corrupt_path = path.with_extension("json.corrupt"); + let _ = std::fs::remove_file(&corrupt_path); + let _ = std::fs::rename(path, &corrupt_path); +} + +#[cfg(test)] +mod tests { + use super::PersistentDenyReadAclState; + use super::load_state; + use std::fs; + use tempfile::TempDir; + + #[test] + fn load_state_recovers_from_corrupt_json() { + let tmp = TempDir::new().expect("tempdir"); + let state_path = tmp.path().join("deny_read_acl_state.json"); + fs::write(&state_path, [0_u8, 0, 0, 0]).expect("write corrupt state"); + + let state = load_state(&state_path).expect("load state"); + + assert!(state.principals.is_empty()); + assert!(!state_path.exists()); + assert!(state_path.with_extension("json.corrupt").exists()); + } + + #[test] + fn load_state_reads_valid_json() { + let tmp = TempDir::new().expect("tempdir"); + let state_path = tmp.path().join("deny_read_acl_state.json"); + fs::write( + &state_path, + "{\"principals\":{\"S-1-1-0\":[\"C:\\\\repo\"]}}", + ) + .expect("write valid state"); + + let state = load_state(&state_path).expect("load state"); + + assert_eq!( + state.principals, + PersistentDenyReadAclState { + principals: [("S-1-1-0".to_string(), vec!["C:\\repo".into()])] + .into_iter() + .collect(), + } + .principals + ); + assert!(state_path.exists()); + assert!(!state_path.with_extension("json.corrupt").exists()); + } +}