Skip to content

Commit

Permalink
Add leveldb plugin system (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
sydp committed May 19, 2024
1 parent 0f9f677 commit d6cf6ff
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 88 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ include:
$ pip install dfindexeddb
```

To also install the dependencies for leveldb/indexeddb plugins, run
```
$ pip install 'dfindexeddb[plugins]'
```


## Installation from source

1. [Linux] Install the snappy compression development package
Expand All @@ -51,6 +57,11 @@ include:
$ pip install .
```

To also install the dependencies for leveldb/indexeddb plugins, run
```
$ pip install '.[plugins]'
```

## Usage

Two CLI tools for parsing IndexedDB/LevelDB files are available after
Expand Down Expand Up @@ -170,3 +181,13 @@ following command:
```
$ dfleveldb descriptor -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,versionedit} | -v]
```

#### Plugins

To apply a plugin parser for a leveldb file/folder, add the
`--plugin [Plugin Name]` argument. Currently, there is support for the
following artifacts:

| Plugin Name | Artifact Name |
| -------- | ------- |
| `ChromeNotificationRecord` | Chrome/Chromium Notifications |
3 changes: 2 additions & 1 deletion dfindexeddb/indexeddb/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import json
import pathlib

from dfindexeddb import utils
from dfindexeddb import version
from dfindexeddb.indexeddb.chromium import blink
from dfindexeddb.indexeddb.chromium import record as chromium_record
Expand All @@ -36,7 +37,7 @@ class Encoder(json.JSONEncoder):
"""A JSON encoder class for dfindexeddb fields."""
def default(self, o):
if dataclasses.is_dataclass(o):
o_dict = dataclasses.asdict(o)
o_dict = utils.asdict(o)
return o_dict
if isinstance(o, bytes):
out = []
Expand Down
62 changes: 58 additions & 4 deletions dfindexeddb/leveldb/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import json
import pathlib

from dfindexeddb import utils
from dfindexeddb import version
from dfindexeddb.leveldb import descriptor
from dfindexeddb.leveldb import ldb
from dfindexeddb.leveldb import log
from dfindexeddb.leveldb import record
from dfindexeddb.leveldb.plugins import manager


_VALID_PRINTABLE_CHARACTERS = (
Expand All @@ -37,7 +39,7 @@ class Encoder(json.JSONEncoder):
def default(self, o):
"""Returns a serializable object for o."""
if dataclasses.is_dataclass(o):
o_dict = dataclasses.asdict(o)
o_dict = utils.asdict(o)
return o_dict
if isinstance(o, bytes):
out = []
Expand Down Expand Up @@ -66,15 +68,39 @@ def _Output(structure, output):

def DbCommand(args):
"""The CLI for processing leveldb folders."""
if args.plugin and args.plugin == 'list':
for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
print(plugin)
return

if args.plugin:
plugin_class = manager.LeveldbPluginManager.GetPlugin(args.plugin)
else:
plugin_class = None

for leveldb_record in record.FolderReader(
args.source).GetRecords(
use_manifest=args.use_manifest,
use_sequence_number=args.use_sequence_number):
_Output(leveldb_record, output=args.output)
if plugin_class:
plugin_record = plugin_class.FromLevelDBRecord(leveldb_record)
_Output(plugin_record, output=args.output)
else:
_Output(leveldb_record, output=args.output)


def LdbCommand(args):
"""The CLI for processing ldb files."""
if args.plugin and args.plugin == 'list':
for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
print(plugin)
return

if args.plugin:
plugin_class = manager.LeveldbPluginManager.GetPlugin(args.plugin)
else:
plugin_class = None

ldb_file = ldb.FileReader(args.source)

if args.structure_type == 'blocks':
Expand All @@ -85,14 +111,28 @@ def LdbCommand(args):
elif args.structure_type == 'records' or not args.structure_type:
# Prints key value record information.
for key_value_record in ldb_file.GetKeyValueRecords():
_Output(key_value_record, output=args.output)
if plugin_class:
plugin_record = plugin_class.FromKeyValueRecord(key_value_record)
_Output(plugin_record, output=args.output)
else:
_Output(key_value_record, output=args.output)

else:
print(f'{args.structure_type} is not supported for ldb files.')


def LogCommand(args):
"""The CLI for processing log files."""
if args.plugin and args.plugin == 'list':
for plugin, _ in manager.LeveldbPluginManager.GetPlugins():
print(plugin)
return

if args.plugin:
plugin_class = manager.LeveldbPluginManager.GetPlugin(args.plugin)
else:
plugin_class = None

log_file = log.FileReader(args.source)

if args.structure_type == 'blocks':
Expand All @@ -114,7 +154,11 @@ def LogCommand(args):
or not args.structure_type):
# Prints key value record information.
for internal_key_record in log_file.GetParsedInternalKeys():
_Output(internal_key_record, output=args.output)
if plugin_class:
plugin_record = plugin_class.FromKeyValueRecord(internal_key_record)
_Output(plugin_record, output=args.output)
else:
_Output(internal_key_record, output=args.output)

else:
print(f'{args.structure_type} is not supported for log files.')
Expand Down Expand Up @@ -146,6 +190,7 @@ def DescriptorCommand(args):
else:
print(f'{args.structure_type} is not supported for descriptor files.')


def App():
"""The CLI app entrypoint for parsing leveldb files."""
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -182,6 +227,9 @@ def App():
'repr'],
default='json',
help='Output format. Default is json')
parser_db.add_argument(
'--plugin',
help='Use plugin to parse records.')
parser_db.set_defaults(func=DbCommand)

parser_log = subparsers.add_parser(
Expand All @@ -200,6 +248,9 @@ def App():
'repr'],
default='json',
help='Output format. Default is json')
parser_log.add_argument(
'--plugin',
help='Use plugin to parse records.')
parser_log.add_argument(
'-t',
'--structure_type',
Expand Down Expand Up @@ -227,6 +278,9 @@ def App():
'repr'],
default='json',
help='Output format. Default is json')
parser_ldb.add_argument(
'--plugin',
help='Use plugin to parse records.')
parser_ldb.add_argument(
'-t',
'--structure_type',
Expand Down
17 changes: 17 additions & 0 deletions dfindexeddb/leveldb/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Leveldb Plugin module."""

from dfindexeddb.leveldb.plugins import chrome_notifications
Loading

0 comments on commit d6cf6ff

Please sign in to comment.