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
+18 -2
View File
@@ -332,8 +332,8 @@ pub fn encrypt_chunk(
vec![COMPRESSION_NONE]
} else {
let compressed = lz4_flex::compress_prepend_size(plaintext);
// Nur komprimieren, wenn tatsächlich Bytes gespart werden (+1 Byte für das Flag)
if compressed.len() + 1 < plaintext.len() {
// Nur komprimieren, wenn mindestens 64 Bytes eingespart werden (+1 Byte für das Flag)
if compressed.len() + 64 <= plaintext.len() {
let mut buf = Vec::with_capacity(compressed.len() + 1);
buf.push(COMPRESSION_LZ4);
buf.extend_from_slice(&compressed);
@@ -493,6 +493,22 @@ mod tests {
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_lz4_chunk_compression_threshold() {
let dek = generate_dek();
// Unkomprimierbare Zufallsdaten (keine 64 Bytes Ersparnis)
let mut random_bytes = vec![0u8; 1000];
OsRng.fill_bytes(&mut random_bytes);
let (ct, nonce, tag) =
encrypt_chunk(&dek, 1, 0, &random_bytes, FORMAT_VERSION_V2).unwrap();
// Da Kompression keine 64 Bytes spart, wird COMPRESSION_NONE (1 Byte) + Plaintext gespeichert
assert_eq!(ct.len(), random_bytes.len() + 1);
let decrypted = decrypt_chunk(&dek, 1, 0, &ct, &nonce, &tag, FORMAT_VERSION_V2).unwrap();
assert_eq!(decrypted, random_bytes);
}
#[test]
fn test_v1_backward_compatibility() {
let dek = generate_dek();