Expand description
sqlite-forensic — Tier-2 anomaly auditor over sqlite_core.
WS-C skeleton. The reader (sqlite-core) answers “what does this database
header show?”; this crate grades the forensically-notable observations into
severity-ranked forensicnomicon::report::Findings, so a SQLite
evidence database’s anomalies aggregate uniformly with the partition /
container / filesystem layers.
Each anomaly is an observation (“consistent with …”); the examiner draws the conclusions.
§Capabilities
carve_deleted_records— recover deleted rows from free (unallocated) pages, the headline capability rusqlite structurally cannot provide. Each recovered row is confidence-graded, flaggedallocated: false, and carries page/offset/rowid provenance.auditgrades header reserved-space, a non-empty freelist (prior deletions), an active WAL overlay (uncheckpointed state), and a header/file page-count mismatch into severity-rankedforensicnomicon::report::Findings.audit_journalgrades the rollback--journaldesign-§6 observations (hot journal, recoverable PERSIST pre-images, checksum mismatch, journaled schema page, duplicate page record, db-size delta) — additive toaudit.
Deferred: a full anomaly suite (overflow-chain integrity, schema-format / text-encoding checks) and a fuzz harness.
Modules§
- blob
- Recovered-BLOB enrichment (roadmap §4.5): make carved binary payloads addressable in a case by a magic-based media type and a SHA-256 content hash.
- carve
- Whole-database SQLite carver satisfying the fleet
forensic_carve::Carvercontract (ADR 0001 §4). - case_
uco - CASE/UCO JSON-LD export of recovered BLOBs (roadmap §4.5).
- interpret
- The blob-interpreter seam (roadmap §4.5 follow-up): a dependency-inversion
contract so consumers can decode opaque
BLOBvalues in schema context —WebKitLocal Storage UTF-16, or the generalblob-decodercrate (plist / gzip / JSON / …) — WITHOUT this reader library taking on any decode dependency or MSRV cost.
Structs§
- Anomaly
- A
SQLiteforensic anomaly: an observation graded by severity, with a stable code and note derived from itsAnomalyKindso they cannot drift. - Carve
Tiers - The two strictly-separated recovery tiers returned by
carve_with_fragments. - Carved
Fragment - A Tier-2 partial recovery: a freed cell whose full row could not be reconstructed but at least one distinctive cell (TEXT ≥ 4 bytes of valid UTF-8, or REAL) survived at a structural anchor.
- Carved
Record - A deleted record recovered from unallocated space — the headline capability rusqlite cannot provide. Carries the decoded row plus provenance so the examiner can weigh it as a “consistent with a deleted row” observation.
- Journal
Counts - Structured counts of what the rollback-journal recovery found — the NIST SFT-03 “number of deleted/modified records” report (design §5/§6.11).
- Journal
Recovery - The result of recovering deletions + modifications from a rollback journal (design §5). Deleted rows are full-fidelity prior allocated rows (NOT free-space carves); modified rows carry the prior and current values.
- Overflow
Provenance - Provenance for a record reassembled across an overflow-page chain (task
#73): the pages whose bytes were concatenated to recover the row. An examiner
citing the row as evidence can name exactly where its bytes came from. Present
only on chain-reassembled rows;
Nonefor every contiguous recovery. - Prior
Row - A row recovered from the prior (pre-transaction) snapshot of a rollback
journal (design §5). In the prior state it was a LIVE allocated row that the
last transaction DELETED — so it is recovered at full fidelity and is NOT a
free-space carve (
allocatedwas true in the prior state). Carries full journal provenance inRollbackJournalSource. - Prior
Version Record - A row present in BOTH the prior snapshot and the live db with DIFFERENT values
— the last transaction modified it (design §4). Carries the PRIOR (pre-edit)
values and the CURRENT values.
replaced_rowidis set because a delete + insert reusing the same rowid is indistinguishable from an in-place UPDATE here, so identity may differ — never asserted as “UPDATE”. - Recovered
Schema - A schema-table (
sqlite_master) row recovered from free space — a dropped or replaced table/index/view/trigger whose definition no longer appears in the live schema. Recovering it tells the examiner an object existed and its structure (theCREATEstatement), which in turn explains residual data on the freelist. A finding, not a certainty: it is “consistent with a dropped<name>” — the examiner draws the conclusion. - Rollback
Journal Source - Provenance for a record recovered from a rollback journal (design §5):
where in the journal its page image lived and how trustworthy the parse was.
Copy(all fields are scalar) so it threads through the existing attribution → output pipeline exactly likeWalProvenance; the journal’s path is supplied once at the API boundary, not duplicated per row. - WalProvenance
- Provenance for a record carved from a
-walframe: the(salt1, salt2, frame_index)log-sequence identity of the frame it came from (the LSN task #55 will formalize).
Enums§
- Anomaly
Kind - The classified
SQLiteforensic anomalies this auditor can grade. - Attribution
- How confidently a carved deleted row has been reattached to a live table.
- Journal
Header State - Whether a rollback-journal header was parsed intact (Tier A) or reconstructed from a zeroed (PERSIST post-commit) header (Tier B). A reconstructed header could not verify page checksums (the nonce was zeroed), so its recoveries rest on cross-record structural consistency — a graded distinction the examiner weighs.
- Recovery
Source - Which class of free space a deleted record was carved from. Records the recovery provenance so the examiner can weigh reliability by class.
- Table
Instance Risk - A non-overclaiming diagnostic HINT about a recovered record’s relationship
to the instance of the table it was attributed to — ORTHOGONAL to
Attribution(it never changes the tier, the routing, or the table claim).
Functions§
- attribute_
record - Attribute one carved record to a tier given the live tables and the page→table map. Pure (no DB access) so it is directly unit-testable.
- attribute_
records - Attribute every carved record, reading the live tables and page→table map
from
dbonce. The returned vector is parallel torecords. - audit
- Audit an opened
Databasefor forensically-notable anomalies. - audit_
carved_ findings - Carve deleted records and convert each to a canonical
Findingundersource. The per-record confidence is threaded into the finding’s context so downstream consumers can filter low-confidence recoveries. - audit_
findings - Audit an opened
Databaseand convert each anomaly to the canonicalFindingunder the suppliedSource, ready to merge into aReport. - audit_
journal - Audit a rollback
-journal(raw bytes) bound todbfor the design-§6 observations, additive toaudit(which covers main-db anomalies only). - audit_
journal_ findings - Audit a rollback
-journaland convert each design-§6 observation to the canonicalFindingundersource, ready to merge into aReport. The additive counterpart toaudit_findingsfor the journal sidecar. - carve_
all_ deleted_ records - Recover deleted records across every free-space class — the full-coverage carver. Drives, in order:
- carve_
at_ commit - Carve the deleted residue of one materialized commit snapshot of the
-wal, the per-commit temporal building block of the N-snapshot carve. - carve_
deleted_ records - Recover deleted records by carving the database’s free (unallocated) pages.
- carve_
rollback_ journal - Recover the last transaction’s deletions and modifications from a rollback
-journalby diffing the pre-transaction snapshot against the live database (design §4) — the temporal inverse of WAL recovery. - carve_
with_ fragments - Two-tier deleted-record recovery: Tier-1 full rows plus Tier-2 partial fragments, in one pass.
- matching_
tables - The names of every live table whose shape a record’s values match, in the order the tables were listed. Zero matches → unattributable shape; one → a clean guess; more than one → ambiguous.
- recover_
dropped_ schemas - Recover dropped or replaced schema definitions from free space: carved
sqlite_master-shaped rows whose(name, sql)no longer matches a live schema entry. ADROP TABLE/DROP INDEXdeletes the object’ssqlite_masterrow (its bytes survive in page-1 free space undersecure_delete=OFF); this surfaces that residue as a structuredRecoveredSchemarather than a raw 5-column carved record. - row_
histories_ with_ residue - The core per-rowid VERSION HISTORY (
Database::row_histories) augmented with free-space CARVED RESIDUE — the forensic-layer completion of Phase 1. - shape_
matches - Whether a carved record’s values match a live table’s shape: equal column
count AND every value storage-compatible with the corresponding column
affinity (
value_fits_affinity). - table_
instance_ risks - Per-record
TableInstanceRisk, parallel torecordsand theirattributions— the Detector-A diagnostic pass. - table_
instance_ risks_ with_ sidecar - Per-record
TableInstanceRiskover BOTH Detector A (bare AUTOINCREMENT high-water) AND Detector B (sidecar schema-change), the sidecar-aware pass the CLI composes when a-wal/-journalis in play (docs/design/drop-recreate-attribution.md). - value_
fits_ affinity - Whether a carved value is storage-compatible with a column of the given declared affinity — the per-cell test the shape matcher applies.