Skip to content

Separate IndexedDB database from object store, and keep the value structured - #317

Merged
RyanDFIR merged 1 commit into
mainfrom
indexeddb-object-store
Sep 6, 2026
Merged

Separate IndexedDB database from object store, and keep the value structured#317
RyanDFIR merged 1 commit into
mainfrom
indexeddb-object-store

Conversation

@RyanDFIR

@RyanDFIR RyanDFIR commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Stacked on #316, because the Storage sheet column numbers here are computed on top of the File SHA256 column that PR adds. Review that one first.

The database and object store names were joined into one field as f"{database_name}.{obj_store_name}", which cannot reliably be split back apart. Not hypothetical: bf4sa_2025_bob-1 has 19 rows whose database name is Bitwarden's storage.bw.offline. They are separate fields now, and the key's IndexedDB type and raw bytes are recorded too, since str() of a number and of a numeric string look identical and a binary key had nowhere to go.

The value was stringified at parse time, so the structure a page stored was gone before anything could read it. The record now holds the deserialized object and renders the string on demand. Keeping both roughly doubles IndexedDB memory, measured on one profile at 21 MB of strings against 28 MB of objects and 49 MB for the pair, so only the object is kept. Nothing reads a rendered value more than once per run.

A record with no value used to render the string None. ccl returns None both for an entry carrying no value bytes, which is what a deletion tombstone looks like, and for a serialized JS null; the literal None read as recovered data and could not be told apart from a value that really was the string "None". Those cells are blank now.

Rendering is a per-browser override, so Chrome keeps its repr and Firefox its truncated JSON, and no existing output moved. New output is an Object Store column on the Storage sheet, plus object_store, key_type and key_raw in the sqlite storage table and JSONL records. key_raw is hex because the JSONL encoder renders bytes as UTF-8 with replacement, which would corrupt a binary key.

Checked against bf4sa_2025_bob-1 and its 35,235 IndexedDB rows: no row still carries a glued database name, every row carries a deserialized value, and no rendering differs from the old str() form. All six corpus baselines are unchanged. The blank-value fix was verified against cellebrite.ctf_2021, where all 238 deleted rows now render blank and no row contains the literal string None.

…uctured

The database and object store names were joined into a single field as
f"{database_name}.{obj_store_name}", which cannot reliably be split back apart:
real profiles hold database names containing dots, such as Bitwarden's
storage.bw.offline. They are now separate fields, and the key's IndexedDB type
and raw bytes are recorded alongside them, since str() of a number and of a
numeric string are identical and a binary key had nowhere to go.

The value was stringified at parse time, so the structure a page actually stored
was gone before anything could read it. The record now holds the deserialized
object and renders the string form on demand. Keeping both roughly doubles
IndexedDB memory, measured on one test profile at 21 MB of strings against 28 MB
of objects and 49 MB for the pair, so only the object is kept; nothing reads a
rendered value more than once per run.

Rendering is a per-browser override, so Chrome keeps its repr and Firefox its
truncated JSON, and neither output moved. A record with no value now renders
blank instead of the string None. ccl returns None both for an entry carrying no
value bytes, which is what a deletion tombstone looks like, and for a serialized
JS null; the literal None read as recovered data and could not be told apart
from a value that really was the string "None".

Adds an Object Store column to the Storage sheet, and object_store, key_type and
key_raw to the sqlite storage table and to JSONL records. key_raw is hex because
the JSONL encoder renders bytes as UTF-8 with replacement characters, which
would corrupt a binary key beyond recovery.
@RyanDFIR
RyanDFIR deleted the branch main September 5, 2026 23:56
@RyanDFIR RyanDFIR closed this Sep 5, 2026
@RyanDFIR RyanDFIR reopened this Sep 5, 2026
@RyanDFIR
RyanDFIR changed the base branch from hash-cache-and-filesystem-bodies to main September 5, 2026 23:58
@RyanDFIR
RyanDFIR merged commit 9e7030f into main Sep 6, 2026
30 checks passed
@RyanDFIR
RyanDFIR deleted the indexeddb-object-store branch September 6, 2026 00:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant