From f557a9ddfa0cc7bc1788f46f70e516d4edcde9d8 Mon Sep 17 00:00:00 2001 From: harald Date: Sat, 19 Sep 2026 08:57:34 +0200 Subject: [PATCH] =?UTF-8?q?fix(sync):=20S-04=20=E2=80=94=20eliminate=20TOC?= =?UTF-8?q?TOU=20by=20recording=20actual=20bytes=20written?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sync.rs | 7 +++++-- tests/sync_test.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/sync.rs b/src/sync.rs index c3fac9f..ef2b69c 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -336,9 +336,12 @@ pub fn sync_single_file_to_vault( // Bei Überschreiben einer ehemals größeren Datei überzählige alte Chunks entfernen db.truncate_chunks_after(node_id, chunk_idx.saturating_sub(1))?; - db.update_node_size_and_time(node_id, local_size, local_mtime)?; + // S-04: Verwende bytes_written statt local_size zur Vermeidung von TOCTOU-Diskrepanzen + db.update_node_size_and_time(node_id, bytes_written, local_mtime)?; - Ok(FileTransferResult::Transferred { size: local_size }) + Ok(FileTransferResult::Transferred { + size: bytes_written, + }) } /// Synchronisiert eine Datei aus dem Tresor auf die lokale Festplatte (Pull). diff --git a/tests/sync_test.rs b/tests/sync_test.rs index 5744aba..755dc51 100644 --- a/tests/sync_test.rs +++ b/tests/sync_test.rs @@ -643,3 +643,49 @@ fn test_s03_symlink_skipping_and_cycle_protection() { } let _ = fs::remove_dir_all(&temp_root); } + +#[test] +fn test_s04_toctou_file_size_uses_bytes_written() { + let temp_root = + std::env::temp_dir().join(format!("sanctum_s04_test_{}", rand::random::())); + let container_path = temp_root.join("test.sanctum"); + let file_path = temp_root.join("race_file.bin"); + + fs::create_dir_all(&temp_root).unwrap(); + let initial_data = vec![0xABu8; 12345]; + fs::write(&file_path, &initial_data).unwrap(); + + let (db, dek) = create_test_container(&container_path); + + // Sync file to vault + let res = sanctum::sync::sync_single_file_to_vault( + &db, + 0, + &dek, + FORMAT_VERSION, + &file_path, + 1, + "race_file.bin", + false, + false, + ) + .expect("Sync single file to vault"); + + match res { + sanctum::sync::FileTransferResult::Transferred { size } => { + assert_eq!(size, 12345); + } + _ => panic!("Expected FileTransferResult::Transferred"), + } + + let node = db + .resolve_path_in_vault("/race_file.bin", 0, &dek) + .unwrap() + .expect("Node must exist"); + assert_eq!( + node.size, 12345, + "Knotengröße muss exakt bytes_written entsprechen" + ); + + let _ = fs::remove_dir_all(&temp_root); +}