From eef58503a87e4e65d2d313045373eae93830dd8b Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Sat, 16 Dec 2023 07:18:50 +0100 Subject: [PATCH] Changes after review --- .../sqlite_plugins/firefox_downloads.py | 194 +++++++++--------- 1 file changed, 96 insertions(+), 98 deletions(-) diff --git a/plaso/parsers/sqlite_plugins/firefox_downloads.py b/plaso/parsers/sqlite_plugins/firefox_downloads.py index 3834a92e63..15d31c567d 100644 --- a/plaso/parsers/sqlite_plugins/firefox_downloads.py +++ b/plaso/parsers/sqlite_plugins/firefox_downloads.py @@ -4,6 +4,7 @@ import json from dfdatetime import posix_time as dfdatetime_posix_time + from plaso.containers import events from plaso.parsers import sqlite from plaso.parsers.sqlite_plugins import interface @@ -53,12 +54,12 @@ class Firefox118DownloadEventData(events.EventData): """Firefox download event data. Attributes: - deleted (int): Firefox-tracked deleted state - download_state (int): Firefox State of the download + deleted (int): deleted state. + download_state (int): state of the download. end_time (dfdatetime.DateTimeValues): date and time the download was finished. - expiration (int): Firefox (potentially deprecated) expiration field - flags (int): Firefox flags associated with this download + expiration (int): expiration. + flags (int): flags associated with this download full_path (str): full path of the target of the download. mime_type (str): mime type of the download. name (str): name of the download. @@ -71,7 +72,7 @@ class Firefox118DownloadEventData(events.EventData): started. temporary_location (str): temporary location of the download. total_bytes (int): total number of bytes of the download. - type (int): Firefox (potentially deprecated) type field + type (int): type field. url (str): source URL of the download. """ @@ -95,55 +96,39 @@ def __init__(self): self.url = None -class Firefox118DownloadsPlugin(interface.SQLitePlugin): - """SQLite parser plugin for Mozilla Firefox file downloads as of version 118. - Likely, this will apply to versions earlier than 118, but this was built on - version 118. +class FirefoxDownloadsPlugin(interface.SQLitePlugin): + """SQLite parser plugin for Mozilla Firefox downloads database files. - In this version of Firefox Downloads, the files are in a table in places.sql + The Mozilla Firefox downloads database file is typically stored in: + downloads.sqlite """ - NAME = 'firefox_118_downloads' + + NAME = 'firefox_downloads' DATA_FORMAT = ( - 'Mozilla Firefox 118 downloads SQLite database (downloads.sqlite) file') + 'Mozilla Firefox downloads SQLite database (downloads.sqlite) file') REQUIRED_STRUCTURE = { - 'moz_annos': frozenset([ - 'id', 'place_id', 'anno_attribute_id', 'content', 'flags', - 'expiration', 'type', 'dateAdded', 'lastModified']), - 'moz_places': frozenset([ - 'id', 'title', 'url', 'last_visit_date']) - } + 'moz_downloads': frozenset([ + 'id', 'name', 'source', 'target', 'tempPath', 'startTime', 'endTime', + 'state', 'referrer', 'currBytes', 'maxBytes', 'mimeType'])} QUERIES = [ - (('SELECT annos1.content, annos2.flags, annos2.expiration, annos2.type, ' - 'annos2.dateAdded, annos2.lastModified, annos2.content as dest_fpath, ' - 'places.url, places.title, places.last_visit_date ' - 'from moz_annos annos1, moz_annos annos2, moz_places places ' - 'WHERE annos1.anno_attribute_id == annos2.anno_attribute_id+1 ' - 'AND annos1.place_id == annos2.place_id ' - 'AND annos1.place_id == places.id'), + (('SELECT moz_downloads.id, moz_downloads.name, moz_downloads.source, ' + 'moz_downloads.target, moz_downloads.tempPath, ' + 'moz_downloads.startTime, moz_downloads.endTime, moz_downloads.state, ' + 'moz_downloads.referrer, moz_downloads.currBytes, ' + 'moz_downloads.maxBytes, moz_downloads.mimeType ' + 'FROM moz_downloads'), 'ParseDownloadsRow')] SCHEMAS = [ - {'moz_annos': - 'CREATE TABLE moz_annos (id INTEGER PRIMARY KEY, ' - 'place_id INTEGER NOT NULL, anno_attribute_id INTEGER, ' - 'content LONGVARCHAR, flags INTEGER DEFAULT 0, ' - 'expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, ' - 'dateAdded INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0)'}, - {'moz_places': - 'CREATE TABLE moz_places (id INTEGER PRIMARY KEY, url LONGVARCHAR, ' - 'title LONGVARCHAR, rev_host LONGVARCHAR, ' - 'visit_count INTEGER DEFAULT 0, hidden INTEGER DEFAULT 0 NOT NULL, ' - 'typed INTEGER DEFAULT 0 NOT NULL, ' - 'frecency INTEGER DEFAULT -1 NOT NULL, last_visit_date INTEGER, ' - 'guid TEXT, foreign_count INTEGER DEFAULT 0 NOT NULL, ' - 'url_hash INTEGER DEFAULT 0 NOT NULL , description TEXT, ' - 'preview_image_url TEXT, site_name TEXT, ' - 'origin_id INTEGER REFERENCES moz_origins(id), ' - 'recalc_frecency INTEGER NOT NULL DEFAULT 0, alt_frecency INTEGER, ' - 'recalc_alt_frecency INTEGER NOT NULL DEFAULT 0)'} - ] + {'moz_downloads': + 'CREATE TABLE moz_downloads (id INTEGER PRIMARY KEY, name TEXT, ' + 'source TEXT, target TEXT, tempPath TEXT, startTime INTEGER, endTime ' + 'INTEGER, state INTEGER, referrer TEXT, entityID TEXT, currBytes ' + 'INTEGER NOT NULL DEFAULT 0, maxBytes INTEGER NOT NULL DEFAULT -1, ' + 'mimeType TEXT, preferredApplication TEXT, preferredAction INTEGER ' + 'NOT NULL DEFAULT 0, autoResume INTEGER NOT NULL DEFAULT 0)'}] def _GetDateTimeRowValue(self, query_hash, row, value_name): """Retrieves a date and time value from the row. @@ -176,62 +161,72 @@ def ParseDownloadsRow( """ query_hash = hash(query) - content = self._GetRowValue(query_hash, row, 'content') - content_data = json.loads(content) - - event_data = Firefox118DownloadEventData() - - event_data.deleted = content_data.get('deleted', None) - event_data.download_state = content_data.get('state', None) - event_data.end_time = content_data.get('endTime', None) - event_data.expiration = self._GetRowValue(query_hash, row, 'expiration') - event_data.flags = self._GetRowValue(query_hash, row, 'flags') - event_data.full_path = self._GetRowValue(query_hash, row, 'dest_fpath') - event_data.name = self._GetRowValue(query_hash, row, 'title') + event_data = FirefoxDownloadEventData() + event_data.end_time = self._GetDateTimeRowValue(query_hash, row, 'endTime') + event_data.full_path = self._GetRowValue(query_hash, row, 'target') + event_data.mime_type = self._GetRowValue(query_hash, row, 'mimeType') + event_data.name = self._GetRowValue(query_hash, row, 'name') + event_data.offset = self._GetRowValue(query_hash, row, 'id') event_data.query = query - event_data.received_bytes = content_data.get('fileSize', 0) + event_data.received_bytes = self._GetRowValue(query_hash, row, 'currBytes') + event_data.referrer = self._GetRowValue(query_hash, row, 'referrer') event_data.start_time = self._GetDateTimeRowValue( - query_hash, row, 'dateAdded') - event_data.total_bytes = content_data.get('fileSize', 0) - event_data.type = self._GetRowValue(query_hash, row, 'type') - event_data.url = self._GetRowValue(query_hash, row, 'url') + query_hash, row, 'startTime') + event_data.temporary_location = self._GetRowValue( + query_hash, row, 'tempPath') + event_data.total_bytes = self._GetRowValue(query_hash, row, 'maxBytes') + event_data.url = self._GetRowValue(query_hash, row, 'source') parser_mediator.ProduceEventData(event_data) -class FirefoxDownloadsPlugin(interface.SQLitePlugin): - """SQLite parser plugin for Mozilla Firefox downloads database files. +class Firefox118DownloadsPlugin(interface.SQLitePlugin): + """SQLite parser plugin for version 118 Firefox downloads database files. - The Mozilla Firefox downloads database file is typically stored in: - downloads.sqlite + The version 118 Firefox downloads database file is typically stored in: + places.sql """ - NAME = 'firefox_downloads' + NAME = 'firefox_118_downloads' DATA_FORMAT = ( - 'Mozilla Firefox downloads SQLite database (downloads.sqlite) file') + 'Mozilla Firefox 118 downloads SQLite database (downloads.sqlite) file') REQUIRED_STRUCTURE = { - 'moz_downloads': frozenset([ - 'id', 'name', 'source', 'target', 'tempPath', 'startTime', 'endTime', - 'state', 'referrer', 'currBytes', 'maxBytes', 'mimeType'])} + 'moz_annos': frozenset([ + 'id', 'place_id', 'anno_attribute_id', 'content', 'flags', + 'expiration', 'type', 'dateAdded', 'lastModified']), + 'moz_places': frozenset([ + 'id', 'title', 'url', 'last_visit_date'])} QUERIES = [ - (('SELECT moz_downloads.id, moz_downloads.name, moz_downloads.source, ' - 'moz_downloads.target, moz_downloads.tempPath, ' - 'moz_downloads.startTime, moz_downloads.endTime, moz_downloads.state, ' - 'moz_downloads.referrer, moz_downloads.currBytes, ' - 'moz_downloads.maxBytes, moz_downloads.mimeType ' - 'FROM moz_downloads'), + (('SELECT annos1.content, annos2.flags, annos2.expiration, annos2.type, ' + 'annos2.dateAdded, annos2.lastModified, annos2.content as dest_fpath, ' + 'places.url, places.title, places.last_visit_date ' + 'from moz_annos annos1, moz_annos annos2, moz_places places ' + 'WHERE annos1.anno_attribute_id == annos2.anno_attribute_id+1 ' + 'AND annos1.place_id == annos2.place_id ' + 'AND annos1.place_id == places.id'), 'ParseDownloadsRow')] SCHEMAS = [ - {'moz_downloads': - 'CREATE TABLE moz_downloads (id INTEGER PRIMARY KEY, name TEXT, ' - 'source TEXT, target TEXT, tempPath TEXT, startTime INTEGER, endTime ' - 'INTEGER, state INTEGER, referrer TEXT, entityID TEXT, currBytes ' - 'INTEGER NOT NULL DEFAULT 0, maxBytes INTEGER NOT NULL DEFAULT -1, ' - 'mimeType TEXT, preferredApplication TEXT, preferredAction INTEGER ' - 'NOT NULL DEFAULT 0, autoResume INTEGER NOT NULL DEFAULT 0)'}] + {'moz_annos': + 'CREATE TABLE moz_annos (id INTEGER PRIMARY KEY, ' + 'place_id INTEGER NOT NULL, anno_attribute_id INTEGER, ' + 'content LONGVARCHAR, flags INTEGER DEFAULT 0, ' + 'expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, ' + 'dateAdded INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0)'}, + {'moz_places': + 'CREATE TABLE moz_places (id INTEGER PRIMARY KEY, url LONGVARCHAR, ' + 'title LONGVARCHAR, rev_host LONGVARCHAR, ' + 'visit_count INTEGER DEFAULT 0, hidden INTEGER DEFAULT 0 NOT NULL, ' + 'typed INTEGER DEFAULT 0 NOT NULL, ' + 'frecency INTEGER DEFAULT -1 NOT NULL, last_visit_date INTEGER, ' + 'guid TEXT, foreign_count INTEGER DEFAULT 0 NOT NULL, ' + 'url_hash INTEGER DEFAULT 0 NOT NULL , description TEXT, ' + 'preview_image_url TEXT, site_name TEXT, ' + 'origin_id INTEGER REFERENCES moz_origins(id), ' + 'recalc_frecency INTEGER NOT NULL DEFAULT 0, alt_frecency INTEGER, ' + 'recalc_alt_frecency INTEGER NOT NULL DEFAULT 0)'}] def _GetDateTimeRowValue(self, query_hash, row, value_name): """Retrieves a date and time value from the row. @@ -252,8 +247,7 @@ def _GetDateTimeRowValue(self, query_hash, row, value_name): return dfdatetime_posix_time.PosixTimeInMicroseconds(timestamp=timestamp) - def ParseDownloadsRow( - self, parser_mediator, query, row, **unused_kwargs): + def ParseDownloadsRow(self, parser_mediator, query, row, **unused_kwargs): """Parses a downloads row. Args: @@ -264,21 +258,25 @@ def ParseDownloadsRow( """ query_hash = hash(query) - event_data = FirefoxDownloadEventData() - event_data.end_time = self._GetDateTimeRowValue(query_hash, row, 'endTime') - event_data.full_path = self._GetRowValue(query_hash, row, 'target') - event_data.mime_type = self._GetRowValue(query_hash, row, 'mimeType') - event_data.name = self._GetRowValue(query_hash, row, 'name') - event_data.offset = self._GetRowValue(query_hash, row, 'id') + content = self._GetRowValue(query_hash, row, 'content') + content_data = json.loads(content) + + event_data = Firefox118DownloadEventData() + + event_data.deleted = content_data.get('deleted', None) + event_data.download_state = content_data.get('state', None) + event_data.end_time = content_data.get('endTime', None) + event_data.expiration = self._GetRowValue(query_hash, row, 'expiration') + event_data.flags = self._GetRowValue(query_hash, row, 'flags') + event_data.full_path = self._GetRowValue(query_hash, row, 'dest_fpath') + event_data.name = self._GetRowValue(query_hash, row, 'title') event_data.query = query - event_data.received_bytes = self._GetRowValue(query_hash, row, 'currBytes') - event_data.referrer = self._GetRowValue(query_hash, row, 'referrer') + event_data.received_bytes = content_data.get('fileSize', 0) event_data.start_time = self._GetDateTimeRowValue( - query_hash, row, 'startTime') - event_data.temporary_location = self._GetRowValue( - query_hash, row, 'tempPath') - event_data.total_bytes = self._GetRowValue(query_hash, row, 'maxBytes') - event_data.url = self._GetRowValue(query_hash, row, 'source') + query_hash, row, 'dateAdded') + event_data.total_bytes = content_data.get('fileSize', 0) + event_data.type = self._GetRowValue(query_hash, row, 'type') + event_data.url = self._GetRowValue(query_hash, row, 'url') parser_mediator.ProduceEventData(event_data)