From 78738622ebf2a0fcf0472e63a3f5c5e47bb7b282 Mon Sep 17 00:00:00 2001 From: Cobra Date: Mon, 6 Apr 2026 11:44:44 -0400 Subject: [PATCH] Securely wipe state.db on exit (#216) - Add secure_wipe_file() to utils/crypto.py for file overwrites + deletion - Add secure_wipe parameter to StateManager.stop() method - Wipes all SQLite WAL files (.db, .db-wal, .db-shm) with 3 passes - Normal exit now calls state.stop(secure_wipe=True) to clear state database - Falls back to regular delete if secure wipe fails - Prevents state.db from persisting on disk after shutdown --- bigbrother.py | 2 +- core/state.py | 24 ++++++++++++++++++++++-- utils/crypto.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/bigbrother.py b/bigbrother.py index 59c8d45..73db95d 100755 --- a/bigbrother.py +++ b/bigbrother.py @@ -244,7 +244,7 @@ def start(daemon, passive_only): res_mon.stop() engine.stop_all() tool_manager.stop_all() - state.stop() + state.stop(secure_wipe=True) bus.stop() console.print("[green]SystemMonitor stopped.[/green]") diff --git a/core/state.py b/core/state.py index 16dddf5..bff5b56 100644 --- a/core/state.py +++ b/core/state.py @@ -63,8 +63,12 @@ class StateManager: self._writer_thread.start() logger.info("StateManager writer started (db=%s)", self._db_path) - def stop(self) -> None: - """Flush remaining writes and stop the writer thread.""" + def stop(self, secure_wipe: bool = False) -> None: + """Flush remaining writes and stop the writer thread. + + Args: + secure_wipe: If True, securely overwrite and delete the state database. + """ if not self._running: return self._running = False @@ -75,6 +79,22 @@ class StateManager: if self._write_conn: self._write_conn.close() self._write_conn = None + + # Secure wipe if requested + if secure_wipe: + from utils.crypto import secure_wipe_file + # SQLite creates multiple files in WAL mode: db, db-wal, db-shm + for suffix in ["", "-wal", "-shm"]: + filepath = self._db_path + suffix + if os.path.isfile(filepath): + try: + if secure_wipe_file(filepath, passes=3): + logger.info("Securely wiped %s", filepath) + else: + logger.warning("Secure wipe failed for %s (used regular delete)", filepath) + except Exception as e: + logger.error("Failed to wipe %s: %s", filepath, e) + logger.info("StateManager stopped") # ------------------------------------------------------------------ diff --git a/utils/crypto.py b/utils/crypto.py index 64e656f..b325cff 100644 --- a/utils/crypto.py +++ b/utils/crypto.py @@ -246,3 +246,39 @@ class LUKSContainer: @property def is_open(self) -> bool: return Path(self.device_path).exists() + + +def secure_wipe_file(filepath: str, passes: int = 1) -> bool: + """Securely wipe a file by overwriting with random data before deletion. + + Args: + filepath: Path to file to wipe + passes: Number of overwrite passes (default 1, use 3 for stronger security) + + Returns: + True if file was successfully wiped, False otherwise + """ + try: + file_size = os.path.getsize(filepath) + if file_size == 0: + # Empty file, just delete + os.unlink(filepath) + return True + + # Overwrite passes + for _ in range(passes): + with open(filepath, "wb") as f: + f.write(os.urandom(file_size)) + f.flush() + os.fsync(f.fileno()) + + # Delete the file + os.unlink(filepath) + return True + except Exception as e: + # Fallback: try regular delete if secure wipe fails + try: + os.unlink(filepath) + return False + except Exception: + return False