dfindexeddb 20241105__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 +148 -125
- dfindexeddb/indexeddb/chromium/record.py +426 -324
- dfindexeddb/indexeddb/chromium/v8.py +100 -78
- dfindexeddb/indexeddb/cli.py +122 -118
- dfindexeddb/indexeddb/firefox/definitions.py +7 -4
- dfindexeddb/indexeddb/firefox/gecko.py +98 -74
- dfindexeddb/indexeddb/firefox/record.py +66 -24
- dfindexeddb/indexeddb/safari/definitions.py +5 -3
- dfindexeddb/indexeddb/safari/record.py +68 -51
- dfindexeddb/indexeddb/safari/webkit.py +85 -71
- dfindexeddb/indexeddb/types.py +4 -1
- dfindexeddb/leveldb/cli.py +146 -138
- dfindexeddb/leveldb/definitions.py +6 -2
- dfindexeddb/leveldb/descriptor.py +70 -56
- dfindexeddb/leveldb/ldb.py +39 -33
- dfindexeddb/leveldb/log.py +41 -30
- dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
- dfindexeddb/leveldb/plugins/interface.py +5 -6
- dfindexeddb/leveldb/plugins/manager.py +10 -9
- dfindexeddb/leveldb/record.py +71 -62
- dfindexeddb/leveldb/utils.py +21 -13
- dfindexeddb/utils.py +36 -31
- dfindexeddb/version.py +2 -2
- dfindexeddb-20251109.dist-info/METADATA +222 -0
- dfindexeddb-20251109.dist-info/RECORD +40 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20251109.dist-info}/WHEEL +1 -1
- dfindexeddb-20241105.dist-info/AUTHORS +0 -12
- dfindexeddb-20241105.dist-info/METADATA +0 -424
- dfindexeddb-20241105.dist-info/RECORD +0 -41
- {dfindexeddb-20241105.dist-info → dfindexeddb-20251109.dist-info}/entry_points.txt +0 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20251109.dist-info/licenses}/LICENSE +0 -0
- {dfindexeddb-20241105.dist-info → dfindexeddb-20251109.dist-info}/top_level.txt +0 -0
dfindexeddb/indexeddb/cli.py
CHANGED
|
@@ -16,12 +16,12 @@
|
|
|
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 version
|
|
24
|
+
from dfindexeddb import utils, version
|
|
25
25
|
from dfindexeddb.indexeddb import types
|
|
26
26
|
from dfindexeddb.indexeddb.chromium import blink
|
|
27
27
|
from dfindexeddb.indexeddb.chromium import record as chromium_record
|
|
@@ -29,15 +29,16 @@ 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,18 +46,18 @@ 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
55
|
if isinstance(o, types.Undefined):
|
|
55
|
-
return
|
|
56
|
+
return "<undefined>"
|
|
56
57
|
if isinstance(o, types.JSArray):
|
|
57
58
|
return o.__dict__
|
|
58
59
|
if isinstance(o, types.Null):
|
|
59
|
-
return
|
|
60
|
+
return "<null>"
|
|
60
61
|
if isinstance(o, set):
|
|
61
62
|
return list(o)
|
|
62
63
|
if isinstance(o, types.RegExp):
|
|
@@ -66,189 +67,192 @@ class Encoder(json.JSONEncoder):
|
|
|
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
|
-
|
|
136
|
-
|
|
140
|
+
"-s",
|
|
141
|
+
"--source",
|
|
137
142
|
required=True,
|
|
138
143
|
type=pathlib.Path,
|
|
139
|
-
help=
|
|
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
|
-
|
|
155
|
-
|
|
159
|
+
"-s",
|
|
160
|
+
"--source",
|
|
156
161
|
required=True,
|
|
157
162
|
type=pathlib.Path,
|
|
158
|
-
help=
|
|
163
|
+
help="The source file.",
|
|
164
|
+
)
|
|
159
165
|
parser_gecko.add_argument(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
choices=[
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
default='json',
|
|
167
|
-
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
|
+
)
|
|
168
172
|
parser_gecko.set_defaults(func=GeckoCommand)
|
|
169
173
|
|
|
170
174
|
parser_db = subparsers.add_parser(
|
|
171
|
-
|
|
175
|
+
"db", help="Parse a directory/file as IndexedDB."
|
|
176
|
+
)
|
|
172
177
|
parser_db.add_argument(
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
"-s",
|
|
179
|
+
"--source",
|
|
175
180
|
required=True,
|
|
176
181
|
type=pathlib.Path,
|
|
177
182
|
help=(
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
"The source IndexedDB folder (for chrome/chromium) "
|
|
184
|
+
"or sqlite3 file (for firefox/safari)."
|
|
185
|
+
),
|
|
186
|
+
)
|
|
180
187
|
recover_group = parser_db.add_mutually_exclusive_group()
|
|
181
188
|
recover_group.add_argument(
|
|
182
|
-
|
|
183
|
-
action=
|
|
184
|
-
help=
|
|
189
|
+
"--use_manifest",
|
|
190
|
+
action="store_true",
|
|
191
|
+
help="Use manifest file to determine active/deleted records.",
|
|
192
|
+
)
|
|
185
193
|
recover_group.add_argument(
|
|
186
|
-
|
|
187
|
-
action=
|
|
194
|
+
"--use_sequence_number",
|
|
195
|
+
action="store_true",
|
|
188
196
|
help=(
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
"Use sequence number and file offset to determine active/deleted "
|
|
198
|
+
"records."
|
|
199
|
+
),
|
|
200
|
+
)
|
|
191
201
|
parser_db.add_argument(
|
|
192
|
-
|
|
202
|
+
"--format",
|
|
193
203
|
required=True,
|
|
194
|
-
choices=[
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
'firefox',
|
|
198
|
-
'safari'],
|
|
199
|
-
help='The type of IndexedDB to parse.')
|
|
204
|
+
choices=["chromium", "chrome", "firefox", "safari"],
|
|
205
|
+
help="The type of IndexedDB to parse.",
|
|
206
|
+
)
|
|
200
207
|
parser_db.add_argument(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
choices=[
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
default='json',
|
|
208
|
-
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
|
+
)
|
|
209
214
|
parser_db.set_defaults(func=DbCommand)
|
|
210
215
|
|
|
211
216
|
parser_ldb = subparsers.add_parser(
|
|
212
|
-
|
|
213
|
-
|
|
217
|
+
"ldb", help="Parse a ldb file as IndexedDB."
|
|
218
|
+
)
|
|
214
219
|
parser_ldb.add_argument(
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
"-s",
|
|
221
|
+
"--source",
|
|
217
222
|
required=True,
|
|
218
223
|
type=pathlib.Path,
|
|
219
|
-
help=
|
|
224
|
+
help="The source .ldb file.",
|
|
225
|
+
)
|
|
220
226
|
parser_ldb.add_argument(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
choices=[
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
default='json',
|
|
228
|
-
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
|
+
)
|
|
229
233
|
parser_ldb.set_defaults(func=LdbCommand)
|
|
230
234
|
|
|
231
235
|
parser_log = subparsers.add_parser(
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
"log", help="Parse a log file as IndexedDB."
|
|
237
|
+
)
|
|
234
238
|
parser_log.add_argument(
|
|
235
|
-
|
|
239
|
+
"-s",
|
|
240
|
+
"--source",
|
|
236
241
|
required=True,
|
|
237
242
|
type=pathlib.Path,
|
|
238
|
-
help=
|
|
243
|
+
help="The source .log file.",
|
|
244
|
+
)
|
|
239
245
|
parser_log.add_argument(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
choices=[
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
default='json',
|
|
247
|
-
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
|
+
)
|
|
248
252
|
parser_log.set_defaults(func=LogCommand)
|
|
249
253
|
|
|
250
|
-
args = parser.parse_args()
|
|
251
|
-
if hasattr(args,
|
|
254
|
+
args: argparse.Namespace = parser.parse_args()
|
|
255
|
+
if hasattr(args, "func"):
|
|
252
256
|
args.func(args)
|
|
253
257
|
else:
|
|
254
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"
|