dfindexeddb 20241031__py3-none-any.whl → 20251109__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dfindexeddb/indexeddb/chromium/blink.py +116 -74
- dfindexeddb/indexeddb/chromium/definitions.py +152 -124
- dfindexeddb/indexeddb/chromium/record.py +536 -348
- dfindexeddb/indexeddb/chromium/v8.py +112 -141
- dfindexeddb/indexeddb/cli.py +125 -114
- dfindexeddb/indexeddb/firefox/definitions.py +7 -4
- dfindexeddb/indexeddb/firefox/gecko.py +103 -79
- dfindexeddb/indexeddb/firefox/record.py +66 -24
- dfindexeddb/indexeddb/safari/definitions.py +12 -10
- dfindexeddb/indexeddb/safari/record.py +68 -51
- dfindexeddb/indexeddb/safari/webkit.py +112 -189
- dfindexeddb/indexeddb/types.py +5 -2
- dfindexeddb/leveldb/cli.py +146 -131
- dfindexeddb/leveldb/definitions.py +6 -2
- dfindexeddb/leveldb/descriptor.py +75 -45
- dfindexeddb/leveldb/ldb.py +39 -30
- dfindexeddb/leveldb/log.py +44 -27
- dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
- dfindexeddb/leveldb/plugins/interface.py +5 -6
- dfindexeddb/leveldb/plugins/manager.py +11 -10
- dfindexeddb/leveldb/record.py +71 -62
- dfindexeddb/leveldb/utils.py +21 -13
- dfindexeddb/utils.py +35 -30
- dfindexeddb/version.py +2 -2
- dfindexeddb-20251109.dist-info/METADATA +222 -0
- dfindexeddb-20251109.dist-info/RECORD +40 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/WHEEL +1 -1
- dfindexeddb-20241031.dist-info/AUTHORS +0 -12
- dfindexeddb-20241031.dist-info/METADATA +0 -424
- dfindexeddb-20241031.dist-info/RECORD +0 -41
- {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/entry_points.txt +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info/licenses}/LICENSE +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/top_level.txt +0 -0
dfindexeddb/indexeddb/cli.py
CHANGED
|
@@ -16,28 +16,29 @@
|
|
|
16
16
|
import argparse
|
|
17
17
|
import dataclasses
|
|
18
18
|
import enum
|
|
19
|
-
from datetime import datetime
|
|
20
19
|
import json
|
|
21
20
|
import pathlib
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from typing import Any
|
|
22
23
|
|
|
23
|
-
from dfindexeddb import utils
|
|
24
|
-
from dfindexeddb import
|
|
24
|
+
from dfindexeddb import utils, version
|
|
25
|
+
from dfindexeddb.indexeddb import types
|
|
25
26
|
from dfindexeddb.indexeddb.chromium import blink
|
|
26
27
|
from dfindexeddb.indexeddb.chromium import record as chromium_record
|
|
27
|
-
from dfindexeddb.indexeddb.chromium import v8
|
|
28
28
|
from dfindexeddb.indexeddb.firefox import gecko
|
|
29
29
|
from dfindexeddb.indexeddb.firefox import record as firefox_record
|
|
30
30
|
from dfindexeddb.indexeddb.safari import record as safari_record
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
_VALID_PRINTABLE_CHARACTERS = (
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
34
|
+
+ "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~."
|
|
35
|
+
)
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class Encoder(json.JSONEncoder):
|
|
39
39
|
"""A JSON encoder class for dfindexeddb fields."""
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
def default(self, o): # type: ignore[no-untyped-def]
|
|
41
42
|
if dataclasses.is_dataclass(o):
|
|
42
43
|
o_dict = utils.asdict(o)
|
|
43
44
|
return o_dict
|
|
@@ -45,203 +46,213 @@ class Encoder(json.JSONEncoder):
|
|
|
45
46
|
out = []
|
|
46
47
|
for x in o:
|
|
47
48
|
if chr(x) not in _VALID_PRINTABLE_CHARACTERS:
|
|
48
|
-
out.append(f
|
|
49
|
+
out.append(f"\\x{x:02X}")
|
|
49
50
|
else:
|
|
50
51
|
out.append(chr(x))
|
|
51
|
-
return
|
|
52
|
+
return "".join(out)
|
|
52
53
|
if isinstance(o, datetime):
|
|
53
54
|
return o.isoformat()
|
|
54
|
-
if isinstance(o,
|
|
55
|
+
if isinstance(o, types.Undefined):
|
|
55
56
|
return "<undefined>"
|
|
56
|
-
if isinstance(o,
|
|
57
|
+
if isinstance(o, types.JSArray):
|
|
57
58
|
return o.__dict__
|
|
58
|
-
if isinstance(o,
|
|
59
|
+
if isinstance(o, types.Null):
|
|
59
60
|
return "<null>"
|
|
60
61
|
if isinstance(o, set):
|
|
61
62
|
return list(o)
|
|
62
|
-
if isinstance(o,
|
|
63
|
+
if isinstance(o, types.RegExp):
|
|
63
64
|
return str(o)
|
|
64
65
|
if isinstance(o, enum.Enum):
|
|
65
66
|
return o.name
|
|
66
67
|
return json.JSONEncoder.default(self, o)
|
|
67
68
|
|
|
68
69
|
|
|
69
|
-
def _Output(structure, output):
|
|
70
|
+
def _Output(structure: Any, output: str) -> None:
|
|
70
71
|
"""Helper method to output parsed structure to stdout."""
|
|
71
|
-
if output ==
|
|
72
|
+
if output == "json":
|
|
72
73
|
print(json.dumps(structure, indent=2, cls=Encoder))
|
|
73
|
-
elif output ==
|
|
74
|
+
elif output == "jsonl":
|
|
74
75
|
print(json.dumps(structure, cls=Encoder))
|
|
75
|
-
elif output ==
|
|
76
|
+
elif output == "repr":
|
|
76
77
|
print(structure)
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
def BlinkCommand(args):
|
|
80
|
+
def BlinkCommand(args: argparse.Namespace) -> None:
|
|
80
81
|
"""The CLI for processing a file as a blink-encoded value."""
|
|
81
|
-
with open(args.source,
|
|
82
|
+
with open(args.source, "rb") as fd:
|
|
82
83
|
buffer = fd.read()
|
|
83
84
|
blink_value = blink.V8ScriptValueDecoder.FromBytes(buffer)
|
|
84
85
|
_Output(blink_value, output=args.output)
|
|
85
86
|
|
|
86
87
|
|
|
87
|
-
def GeckoCommand(args):
|
|
88
|
+
def GeckoCommand(args: argparse.Namespace) -> None:
|
|
88
89
|
"""The CLI for processing a file as a gecko-encoded value."""
|
|
89
|
-
with open(args.source,
|
|
90
|
+
with open(args.source, "rb") as fd:
|
|
90
91
|
buffer = fd.read()
|
|
91
92
|
blink_value = gecko.JSStructuredCloneDecoder.FromBytes(buffer)
|
|
92
93
|
_Output(blink_value, output=args.output)
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
def DbCommand(args):
|
|
96
|
+
def DbCommand(args: argparse.Namespace) -> None:
|
|
96
97
|
"""The CLI for processing a directory as IndexedDB."""
|
|
97
|
-
if args.format in (
|
|
98
|
-
for
|
|
99
|
-
args.source
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
98
|
+
if args.format in ("chrome", "chromium"):
|
|
99
|
+
for chromium_db_record in chromium_record.FolderReader(
|
|
100
|
+
args.source
|
|
101
|
+
).GetRecords(
|
|
102
|
+
use_manifest=args.use_manifest,
|
|
103
|
+
use_sequence_number=args.use_sequence_number,
|
|
104
|
+
):
|
|
105
|
+
_Output(chromium_db_record, output=args.output)
|
|
106
|
+
elif args.format == "firefox":
|
|
107
|
+
for firefox_db_record in firefox_record.FileReader(args.source).Records():
|
|
108
|
+
_Output(firefox_db_record, output=args.output)
|
|
109
|
+
elif args.format == "safari":
|
|
110
|
+
for safari_db_record in safari_record.FileReader(args.source).Records():
|
|
111
|
+
_Output(safari_db_record, output=args.output)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def LdbCommand(args: argparse.Namespace) -> None:
|
|
112
115
|
"""The CLI for processing a LevelDB table (.ldb) file as IndexedDB."""
|
|
113
116
|
for db_record in chromium_record.IndexedDBRecord.FromFile(args.source):
|
|
114
117
|
_Output(db_record, output=args.output)
|
|
115
118
|
|
|
116
119
|
|
|
117
|
-
def LogCommand(args):
|
|
120
|
+
def LogCommand(args: argparse.Namespace) -> None:
|
|
118
121
|
"""The CLI for processing a LevelDB log file as IndexedDB."""
|
|
119
122
|
for db_record in chromium_record.IndexedDBRecord.FromFile(args.source):
|
|
120
123
|
_Output(db_record, output=args.output)
|
|
121
124
|
|
|
122
125
|
|
|
123
|
-
def App():
|
|
126
|
+
def App() -> None:
|
|
124
127
|
"""The CLI app entrypoint for dfindexeddb."""
|
|
125
128
|
parser = argparse.ArgumentParser(
|
|
126
|
-
prog=
|
|
127
|
-
description=
|
|
128
|
-
epilog=f
|
|
129
|
+
prog="dfindexeddb",
|
|
130
|
+
description="A cli tool for parsing IndexedDB files",
|
|
131
|
+
epilog=f"Version {version.GetVersion()}",
|
|
132
|
+
)
|
|
129
133
|
|
|
130
134
|
subparsers = parser.add_subparsers()
|
|
131
135
|
|
|
132
136
|
parser_blink = subparsers.add_parser(
|
|
133
|
-
|
|
137
|
+
"blink", help="Parse a file as a blink-encoded value."
|
|
138
|
+
)
|
|
134
139
|
parser_blink.add_argument(
|
|
135
|
-
|
|
140
|
+
"-s",
|
|
141
|
+
"--source",
|
|
136
142
|
required=True,
|
|
137
143
|
type=pathlib.Path,
|
|
138
|
-
help=
|
|
139
|
-
|
|
144
|
+
help="The source file.",
|
|
145
|
+
)
|
|
140
146
|
parser_blink.add_argument(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
choices=[
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
default='json',
|
|
148
|
-
help='Output format. Default is json')
|
|
147
|
+
"-o",
|
|
148
|
+
"--output",
|
|
149
|
+
choices=["json", "jsonl", "repr"],
|
|
150
|
+
default="json",
|
|
151
|
+
help="Output format. Default is json.",
|
|
152
|
+
)
|
|
149
153
|
parser_blink.set_defaults(func=BlinkCommand)
|
|
150
154
|
|
|
151
155
|
parser_gecko = subparsers.add_parser(
|
|
152
|
-
|
|
156
|
+
"gecko", help="Parse a file as a gecko-encoded value."
|
|
157
|
+
)
|
|
153
158
|
parser_gecko.add_argument(
|
|
154
|
-
|
|
159
|
+
"-s",
|
|
160
|
+
"--source",
|
|
155
161
|
required=True,
|
|
156
162
|
type=pathlib.Path,
|
|
157
|
-
help=
|
|
163
|
+
help="The source file.",
|
|
164
|
+
)
|
|
158
165
|
parser_gecko.add_argument(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
choices=[
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
default='json',
|
|
166
|
-
help='Output format. Default is json')
|
|
166
|
+
"-o",
|
|
167
|
+
"--output",
|
|
168
|
+
choices=["json", "jsonl", "repr"],
|
|
169
|
+
default="json",
|
|
170
|
+
help="Output format. Default is json.",
|
|
171
|
+
)
|
|
167
172
|
parser_gecko.set_defaults(func=GeckoCommand)
|
|
168
173
|
|
|
169
174
|
parser_db = subparsers.add_parser(
|
|
170
|
-
|
|
175
|
+
"db", help="Parse a directory/file as IndexedDB."
|
|
176
|
+
)
|
|
171
177
|
parser_db.add_argument(
|
|
172
|
-
|
|
178
|
+
"-s",
|
|
179
|
+
"--source",
|
|
173
180
|
required=True,
|
|
174
181
|
type=pathlib.Path,
|
|
175
182
|
help=(
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
"The source IndexedDB folder (for chrome/chromium) "
|
|
184
|
+
"or sqlite3 file (for firefox/safari)."
|
|
185
|
+
),
|
|
186
|
+
)
|
|
178
187
|
recover_group = parser_db.add_mutually_exclusive_group()
|
|
179
188
|
recover_group.add_argument(
|
|
180
|
-
|
|
181
|
-
action=
|
|
182
|
-
help=
|
|
189
|
+
"--use_manifest",
|
|
190
|
+
action="store_true",
|
|
191
|
+
help="Use manifest file to determine active/deleted records.",
|
|
192
|
+
)
|
|
183
193
|
recover_group.add_argument(
|
|
184
|
-
|
|
185
|
-
action=
|
|
194
|
+
"--use_sequence_number",
|
|
195
|
+
action="store_true",
|
|
186
196
|
help=(
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
"Use sequence number and file offset to determine active/deleted "
|
|
198
|
+
"records."
|
|
199
|
+
),
|
|
200
|
+
)
|
|
189
201
|
parser_db.add_argument(
|
|
190
|
-
|
|
202
|
+
"--format",
|
|
191
203
|
required=True,
|
|
192
|
-
choices=[
|
|
193
|
-
help=
|
|
204
|
+
choices=["chromium", "chrome", "firefox", "safari"],
|
|
205
|
+
help="The type of IndexedDB to parse.",
|
|
206
|
+
)
|
|
194
207
|
parser_db.add_argument(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
choices=[
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
default='json',
|
|
202
|
-
help='Output format. Default is json')
|
|
208
|
+
"-o",
|
|
209
|
+
"--output",
|
|
210
|
+
choices=["json", "jsonl", "repr"],
|
|
211
|
+
default="json",
|
|
212
|
+
help="Output format. Default is json.",
|
|
213
|
+
)
|
|
203
214
|
parser_db.set_defaults(func=DbCommand)
|
|
204
215
|
|
|
205
216
|
parser_ldb = subparsers.add_parser(
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
"ldb", help="Parse a ldb file as IndexedDB."
|
|
218
|
+
)
|
|
208
219
|
parser_ldb.add_argument(
|
|
209
|
-
|
|
220
|
+
"-s",
|
|
221
|
+
"--source",
|
|
210
222
|
required=True,
|
|
211
223
|
type=pathlib.Path,
|
|
212
|
-
help=
|
|
224
|
+
help="The source .ldb file.",
|
|
225
|
+
)
|
|
213
226
|
parser_ldb.add_argument(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
choices=[
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
default='json',
|
|
221
|
-
help='Output format. Default is json')
|
|
227
|
+
"-o",
|
|
228
|
+
"--output",
|
|
229
|
+
choices=["json", "jsonl", "repr"],
|
|
230
|
+
default="json",
|
|
231
|
+
help="Output format. Default is json.",
|
|
232
|
+
)
|
|
222
233
|
parser_ldb.set_defaults(func=LdbCommand)
|
|
223
234
|
|
|
224
235
|
parser_log = subparsers.add_parser(
|
|
225
|
-
|
|
226
|
-
|
|
236
|
+
"log", help="Parse a log file as IndexedDB."
|
|
237
|
+
)
|
|
227
238
|
parser_log.add_argument(
|
|
228
|
-
|
|
239
|
+
"-s",
|
|
240
|
+
"--source",
|
|
229
241
|
required=True,
|
|
230
242
|
type=pathlib.Path,
|
|
231
|
-
help=
|
|
243
|
+
help="The source .log file.",
|
|
244
|
+
)
|
|
232
245
|
parser_log.add_argument(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
choices=[
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
default='json',
|
|
240
|
-
help='Output format. Default is json')
|
|
246
|
+
"-o",
|
|
247
|
+
"--output",
|
|
248
|
+
choices=["json", "jsonl", "repr"],
|
|
249
|
+
default="json",
|
|
250
|
+
help="Output format. Default is json.",
|
|
251
|
+
)
|
|
241
252
|
parser_log.set_defaults(func=LogCommand)
|
|
242
253
|
|
|
243
|
-
args = parser.parse_args()
|
|
244
|
-
if hasattr(args,
|
|
254
|
+
args: argparse.Namespace = parser.parse_args()
|
|
255
|
+
if hasattr(args, "func"):
|
|
245
256
|
args.func(args)
|
|
246
257
|
else:
|
|
247
258
|
parser.print_help()
|
|
@@ -18,6 +18,7 @@ from enum import IntEnum
|
|
|
18
18
|
|
|
19
19
|
class IndexedDBKeyType(IntEnum):
|
|
20
20
|
"""IndexedDB Key Types."""
|
|
21
|
+
|
|
21
22
|
TERMINATOR = 0
|
|
22
23
|
FLOAT = 0x10
|
|
23
24
|
DATE = 0x20
|
|
@@ -38,6 +39,7 @@ THREE_BYTE_SHIFT = 6
|
|
|
38
39
|
|
|
39
40
|
class StructuredDataType(IntEnum):
|
|
40
41
|
"""Structured Data Types."""
|
|
42
|
+
|
|
41
43
|
FLOAT_MAX = 0xFFF00000
|
|
42
44
|
HEADER = 0xFFF10000
|
|
43
45
|
NULL = 0xFFFF0000
|
|
@@ -45,7 +47,7 @@ class StructuredDataType(IntEnum):
|
|
|
45
47
|
BOOLEAN = 0xFFFF0002
|
|
46
48
|
INT32 = 0xFFFF0003
|
|
47
49
|
STRING = 0xFFFF0004
|
|
48
|
-
DATE_OBJECT
|
|
50
|
+
DATE_OBJECT = 0xFFFF0005
|
|
49
51
|
REGEXP_OBJECT = 0xFFFF0006
|
|
50
52
|
ARRAY_OBJECT = 0xFFFF0007
|
|
51
53
|
OBJECT_OBJECT = 0xFFFF0008
|
|
@@ -53,8 +55,8 @@ class StructuredDataType(IntEnum):
|
|
|
53
55
|
BOOLEAN_OBJECT = 0xFFFF000A
|
|
54
56
|
STRING_OBJECT = 0xFFFF000B
|
|
55
57
|
NUMBER_OBJECT = 0xFFFF000C
|
|
56
|
-
BACK_REFERENCE_OBJECT
|
|
57
|
-
DO_NOT_USE_1
|
|
58
|
+
BACK_REFERENCE_OBJECT = 0xFFFF000D
|
|
59
|
+
DO_NOT_USE_1 = 0xFFFF000E
|
|
58
60
|
DO_NOT_USE_2 = 0xFFFF000F
|
|
59
61
|
TYPED_ARRAY_OBJECT_V2 = 0xFFFF0010
|
|
60
62
|
MAP_OBJECT = 0xFFFF0011
|
|
@@ -95,6 +97,7 @@ class StructuredDataType(IntEnum):
|
|
|
95
97
|
|
|
96
98
|
class StructuredCloneTags(IntEnum):
|
|
97
99
|
"""Structured Clone Tags."""
|
|
100
|
+
|
|
98
101
|
BLOB = 0xFFFF8001
|
|
99
102
|
FILE_WITHOUT_LASTMODIFIEDDATE = 0xFFFF8002
|
|
100
103
|
FILELIST = 0xFFFF8003
|
|
@@ -140,4 +143,4 @@ class StructuredCloneTags(IntEnum):
|
|
|
140
143
|
ENCODEDAUDIOCHUNK = 0xFFFF8031
|
|
141
144
|
|
|
142
145
|
|
|
143
|
-
FRAME_HEADER = b
|
|
146
|
+
FRAME_HEADER = b"\xff\x06\x00\x00sNaPpY"
|