fix(storage): ST-02 — aggregate errors in recursive node deletion without swallowing
This commit is contained in:
+63
-2
@@ -6,7 +6,7 @@ use anyhow::{bail, Result};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use rusqlite::{params, Connection, OptionalExtension};
|
||||
use tracing::warn;
|
||||
use tracing::{error, warn};
|
||||
|
||||
use crate::crypto::{
|
||||
compute_metadata_mac, decrypt_node_name, derive_kek, derive_metadata_mac_key,
|
||||
@@ -1706,8 +1706,18 @@ impl Database {
|
||||
ids
|
||||
};
|
||||
|
||||
let mut errors = Vec::new();
|
||||
for child_id in child_ids {
|
||||
let _ = self.delete_node(child_id);
|
||||
if let Err(e) = self.delete_node(child_id) {
|
||||
error!("Konnte Kindknoten {} nicht löschen: {e}", child_id);
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
bail!(
|
||||
"Fehler beim rekursiven Löschen: {} Kindknoten konnten nicht gelöscht werden (ST-02)",
|
||||
errors.len()
|
||||
);
|
||||
}
|
||||
|
||||
let conn = self.conn();
|
||||
@@ -3111,4 +3121,55 @@ mod tests {
|
||||
assert!(valid_by_id.is_some());
|
||||
assert_eq!(valid_by_id.unwrap().name, "secret_valid.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_st02_recursive_delete_error_propagation_not_swallowed() {
|
||||
let db = Database::open_in_memory().unwrap();
|
||||
let (salt, kdf, wrapped_dek, nonce, tag) = (
|
||||
[1u8; 16],
|
||||
KdfParams::default(),
|
||||
vec![2u8; 40],
|
||||
[3u8; 12],
|
||||
[4u8; 16],
|
||||
);
|
||||
db.init_schema(&salt, &kdf, &wrapped_dek, &nonce, &tag)
|
||||
.unwrap();
|
||||
|
||||
// Verzeichnisstruktur erstellen:
|
||||
// /parent (id=3)
|
||||
// /parent/normal.txt (id=4)
|
||||
// /parent/protected.txt (id=5)
|
||||
let parent = db.create_node(1, "parent", true).unwrap();
|
||||
let _child_normal = db.create_node(parent.id, "normal.txt", false).unwrap();
|
||||
let _child_protected = db.create_node(parent.id, "protected.txt", false).unwrap();
|
||||
|
||||
// SQLite-Trigger einrichten, der das Löschen von 'protected.txt' blockiert
|
||||
{
|
||||
let conn = db.conn();
|
||||
conn.execute(
|
||||
"CREATE TRIGGER block_delete_protected BEFORE DELETE ON nodes
|
||||
WHEN OLD.name = 'protected.txt'
|
||||
BEGIN
|
||||
SELECT RAISE(FAIL, 'Löschen von protected.txt blockiert');
|
||||
END;",
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// ST-02: delete_node(parent.id) darf den Fehler nicht mehr mit `let _ =` verschlucken!
|
||||
let res = db.delete_node(parent.id);
|
||||
assert!(
|
||||
res.is_err(),
|
||||
"Löschen muss fehlschlagen, wenn ein Kindknoten nicht gelöscht werden kann"
|
||||
);
|
||||
let err_msg = res.unwrap_err().to_string();
|
||||
assert!(
|
||||
err_msg.contains(
|
||||
"Fehler beim rekursiven Löschen: 1 Kindknoten konnten nicht gelöscht werden"
|
||||
),
|
||||
"Fehlermeldung muss die Anzahl fehlgeschlagener Kindknoten exakt ausweisen: {}",
|
||||
err_msg
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user