fix(core): resolve all 12 adversarial review findings

- Zeroize passwords in CLI prompts, handlers, and mount authentication
- Preserve carrier_node_id when recovering Slot 0 via recovery key
- Add --slot parameter to restore-header for targeted slot recovery
- Implement online_backup and restore_from_backup using SQLite Online Backup API
- Expose 'sanctum backup' and 'sanctum restore' CLI subcommands
- Fix inactivity auto-lock by removing touch() from PROPFIND metadata/read_dir
- Support O_APPEND by setting file cursor to file size on handle creation
- Prevent data loss by implementing Drop for CarrierFile to flush dirty blocks
- Optimize CSPRNG padding to only fill unwritten slack space
- Batch carrier initialization in 500-block transactions to prevent UI/CLI freeze
- Enforce 64-byte savings threshold for LZ4 compression
- Add WebClient service diagnostic hint for Windows net use mount errors
- Add unit and integration tests covering all new features
This commit is contained in:
2026-09-09 21:02:21 +02:00
parent 030ce6a1e5
commit 98bfac718f
10 changed files with 571 additions and 91 deletions
+76
View File
@@ -974,6 +974,82 @@ async fn test_plausible_deniability_phase1_indistinguishability_and_safeguards()
let _ = std::fs::remove_file(&backup_path);
}
#[tokio::test]
async fn test_sanctum_online_backup_and_restore() {
let temp_dir = std::env::temp_dir();
let container_path = temp_dir.join(format!("test_backup_src_{}.sanctum", std::process::id()));
let backup_path = temp_dir.join(format!("test_backup_out_{}.sanctum.bak", std::process::id()));
let restored_path = temp_dir.join(format!("test_backup_restored_{}.sanctum", std::process::id()));
for p in [&container_path, &backup_path, &restored_path] {
if p.exists() {
let _ = std::fs::remove_file(p);
}
}
let password = "BackupTestPassword2026!";
let salt = generate_salt();
let kdf_params = KdfParams {
memory_cost: 1024,
time_cost: 1,
parallelism: 1,
};
let kek = derive_kek(password, &salt, &kdf_params).unwrap();
let dek = generate_dek();
let (wrapped_dek, header_nonce, header_tag) = wrap_dek(&kek, &dek).unwrap();
let db = Database::open(&container_path).unwrap();
db.init_schema(&salt, &kdf_params, &wrapped_dek, &header_nonce, &header_tag).unwrap();
db.checkpoint().unwrap();
// Datei schreiben
let fs = SanctumFs::new(db.clone(), dek.clone(), FORMAT_VERSION);
let test_file = DavPath::new("/important.txt").unwrap();
let mut file = fs.open(&test_file, OpenOptions { write: true, create_new: true, ..Default::default() }).await.unwrap();
file.write_bytes(Bytes::from_static(b"Sanctum Online Backup Test Data")).await.unwrap();
file.flush().await.unwrap();
drop(file);
// 1. Online-Live-Backup erstellen
db.online_backup(&backup_path).expect("Online backup should succeed");
assert!(backup_path.exists(), "Backup-Datei muss existieren");
// 2. Original-Container verändern (neue Datei hinzufügen)
let extra_file = DavPath::new("/extra_after_backup.txt").unwrap();
let mut file2 = fs.open(&extra_file, OpenOptions { write: true, create_new: true, ..Default::default() }).await.unwrap();
file2.write_bytes(Bytes::from_static(b"After Backup Data")).await.unwrap();
file2.flush().await.unwrap();
drop(file2);
// 3. Restore aus dem Backup in neuen Pfad
Database::restore_from_backup(&backup_path, &restored_path).expect("Restore should succeed");
assert!(restored_path.exists(), "Wiederhergestellter Container muss existieren");
// 4. Verifiziere den wiederhergestellten Container
let restored_db = Database::open(&restored_path).unwrap();
let meta = restored_db.read_meta().unwrap();
let auth = meta.authenticate(password).expect("Passwort muss den wiederhergestellten Container entsperren");
assert_eq!(*auth.0, *dek);
let restored_fs = SanctumFs::new(restored_db.clone(), auth.0, meta.version);
// /important.txt muss existieren und den korrekten Inhalt haben
let mut read_handle = restored_fs.open(&test_file, OpenOptions { read: true, ..Default::default() }).await.unwrap();
let content = read_handle.read_bytes(100).await.unwrap();
assert_eq!(&content[..], b"Sanctum Online Backup Test Data");
drop(read_handle);
// /extra_after_backup.txt darf im Backup-Zustand NICHT existieren
assert!(restored_fs.open(&extra_file, OpenOptions { read: true, ..Default::default() }).await.is_err());
// Integritätsprüfung (FSCK) auf wiederhergestelltem Container
let report = verify_container(&restored_path, Some(&dek), true).unwrap();
assert!(report.is_healthy());
for p in [&container_path, &backup_path, &restored_path] {
let _ = std::fs::remove_file(p);
}
}