From 5567dbf3a384f9bcb701b64224c2e1888e18a6bd Mon Sep 17 00:00:00 2001 From: Pim Sanders <36573021+PimSanders@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:29:49 +0100 Subject: [PATCH 1/2] Fix error when reading first page from WAL commit --- dissect/database/sqlite3/sqlite3.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dissect/database/sqlite3/sqlite3.py b/dissect/database/sqlite3/sqlite3.py index 3af3e97..38324de 100644 --- a/dissect/database/sqlite3/sqlite3.py +++ b/dissect/database/sqlite3/sqlite3.py @@ -204,14 +204,22 @@ def raw_page(self, num: int) -> bytes: # If a specific WAL checkpoint was provided, use it instead of the on-disk page. if self.checkpoint is not None and (frame := self.checkpoint.get(num)): - return frame.data + data = frame.data + # When reading page 1, strip the SQLite header bytes if present so the page parser sees the page header. + if num == 1 and data.startswith(SQLITE3_HEADER_MAGIC): + return data[len(c_sqlite3.header) :] + return data # Check if the latest valid instance of the page is committed (either the frame itself # is the commit frame or it is included in a commit's frames). If so, return that frame's data. if self.wal: for commit in reversed(self.wal.commits): if (frame := commit.get(num)) and frame.valid: - return frame.data + data = frame.data + # When reading page 1 from WAL, strip the SQLite header bytes if present. + if num == 1 and data.startswith(SQLITE3_HEADER_MAGIC): + return data[len(c_sqlite3.header) :] + return data # Else we read the page from the database file. if num == 1: # Page 1 is root From 6ab5bd393eb6ccac37f7be6fd071cce71aa75380 Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:42:45 +0100 Subject: [PATCH 2/2] Slight change --- dissect/database/sqlite3/sqlite3.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dissect/database/sqlite3/sqlite3.py b/dissect/database/sqlite3/sqlite3.py index 38324de..e009053 100644 --- a/dissect/database/sqlite3/sqlite3.py +++ b/dissect/database/sqlite3/sqlite3.py @@ -202,27 +202,27 @@ def raw_page(self, num: int) -> bytes: if (num < 1 or num > self.header.page_count) and self.header.page_count > 0: raise InvalidPageNumber("Page number exceeds boundaries") - # If a specific WAL checkpoint was provided, use it instead of the on-disk page. + data = None if self.checkpoint is not None and (frame := self.checkpoint.get(num)): + # If a specific WAL checkpoint was provided, use it instead of the on-disk page. data = frame.data - # When reading page 1, strip the SQLite header bytes if present so the page parser sees the page header. - if num == 1 and data.startswith(SQLITE3_HEADER_MAGIC): - return data[len(c_sqlite3.header) :] - return data - # Check if the latest valid instance of the page is committed (either the frame itself - # is the commit frame or it is included in a commit's frames). If so, return that frame's data. - if self.wal: + elif self.wal: + # Check if the latest valid instance of the page is committed (either the frame itself + # is the commit frame or it is included in a commit's frames). If so, return that frame's data. for commit in reversed(self.wal.commits): if (frame := commit.get(num)) and frame.valid: data = frame.data - # When reading page 1 from WAL, strip the SQLite header bytes if present. - if num == 1 and data.startswith(SQLITE3_HEADER_MAGIC): - return data[len(c_sqlite3.header) :] - return data + break + + if data is not None: + if num == 1: + # Page 1 has a database header that needs to be stripped + return data[len(c_sqlite3.header) :] + return data # Else we read the page from the database file. - if num == 1: # Page 1 is root + if num == 1: # Page 1 is root, skip header self.fh.seek(len(c_sqlite3.header)) else: self.fh.seek((num - 1) * self.page_size)