fix(sync): S-04 — eliminate TOCTOU by recording actual bytes written

This commit is contained in:
2026-09-19 08:57:34 +02:00
parent 888c86ca5e
commit f557a9ddfa
2 changed files with 51 additions and 2 deletions
+5 -2
View File
@@ -336,9 +336,12 @@ pub fn sync_single_file_to_vault(
// Bei Überschreiben einer ehemals größeren Datei überzählige alte Chunks entfernen // Bei Überschreiben einer ehemals größeren Datei überzählige alte Chunks entfernen
db.truncate_chunks_after(node_id, chunk_idx.saturating_sub(1))?; 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). /// Synchronisiert eine Datei aus dem Tresor auf die lokale Festplatte (Pull).
+46
View File
@@ -643,3 +643,49 @@ fn test_s03_symlink_skipping_and_cycle_protection() {
} }
let _ = fs::remove_dir_all(&temp_root); 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::<u64>()));
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);
}