fix(state): close the tracking leak and finish the audit of raw DB reads
Completeness pass over the previous commit. Registry leak (would have silently disabled the guard): the first version incremented on open but decremented only in SessionDB.close(). kanban's connect() hands raw connections to callers who close them directly (4 sites), so its counter only ever went up — after enough kanban operations every byte-probe on that path would be refused forever, disabling zeroed-file and header detection. Replaced manual track/untrack with a TrackedConnection subclass that untracks in close(), the one method every close path goes through. Verified across plain close, contextlib.closing, double close, nested lifetimes, and 100-cycle churn; `with conn:` (a transaction scope, not a close) correctly stays tracked. Also: a caller-supplied factory now wins instead of raising TypeError on a duplicate kwarg, since tracking is an optimisation for the probe guard, not a precondition for opening the database. Removed _apply_delete_for_wal_reset_bug, dead after the force-DELETE revert. Audit notes: - DELETE mode is still reachable via the NFS/SMB/FUSE fallback and remains correct there; those users are protected by the raw-read fix, not by the journal mode. - The remaining whole-file reads are on genuinely offline artifacts: _backup_db_file (DB won't open; bytes preserved for forensics), _backup_corrupt_db (quarantine path, now warns if a connection is live), and backup verification of snapshots. backup._safe_copy_db already uses the SQLite backup API rather than a byte copy. - The mechanism is POSIX-specific (Windows byte-range locks are handle-scoped, not process-scoped), so this is a Linux/macOS correctness fix; the change is platform-neutral and safe on Windows. Sibling tests updated: session recovery no longer expects a DELETE-mode recovered DB, and the kanban WAL-fallback test patches the connect site that actually runs now.
This commit is contained in:
@@ -126,8 +126,9 @@ def test_recovery_rebuilds_canonical_data_without_opening_source(
|
||||
source_hash = _sha256(source)
|
||||
source_stat = source.stat()
|
||||
|
||||
# Exercise the vulnerable-runtime fallback: a fresh recovered DB must be
|
||||
# born in DELETE mode instead of enabling WAL.
|
||||
# A recovered DB is born in WAL even on a WAL-reset-vulnerable runtime:
|
||||
# forcing DELETE there was reverted (DELETE is the mode that corrupts
|
||||
# under Hermes' concurrent writers -- see hermes_cli.sqlite_safe_read).
|
||||
monkeypatch.setattr(
|
||||
hermes_state,
|
||||
"is_sqlite_wal_reset_vulnerable",
|
||||
@@ -144,7 +145,7 @@ def test_recovery_rebuilds_canonical_data_without_opening_source(
|
||||
assert report["complete"] is True
|
||||
assert report["installed"] is False
|
||||
assert report["source_unchanged"] is True
|
||||
assert report["verification"]["journal_mode"] == "delete"
|
||||
assert report["verification"]["journal_mode"] == "wal"
|
||||
assert report["verification"]["integrity_check"] == ["ok"]
|
||||
assert report["verification"]["foreign_key_check"] == []
|
||||
assert report["verification"]["schema_version"] == SCHEMA_VERSION
|
||||
|
||||
Reference in New Issue
Block a user