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.
Files changed (33) hide show
  1. dfindexeddb/indexeddb/chromium/blink.py +116 -74
  2. dfindexeddb/indexeddb/chromium/definitions.py +152 -124
  3. dfindexeddb/indexeddb/chromium/record.py +536 -348
  4. dfindexeddb/indexeddb/chromium/v8.py +112 -141
  5. dfindexeddb/indexeddb/cli.py +125 -114
  6. dfindexeddb/indexeddb/firefox/definitions.py +7 -4
  7. dfindexeddb/indexeddb/firefox/gecko.py +103 -79
  8. dfindexeddb/indexeddb/firefox/record.py +66 -24
  9. dfindexeddb/indexeddb/safari/definitions.py +12 -10
  10. dfindexeddb/indexeddb/safari/record.py +68 -51
  11. dfindexeddb/indexeddb/safari/webkit.py +112 -189
  12. dfindexeddb/indexeddb/types.py +5 -2
  13. dfindexeddb/leveldb/cli.py +146 -131
  14. dfindexeddb/leveldb/definitions.py +6 -2
  15. dfindexeddb/leveldb/descriptor.py +75 -45
  16. dfindexeddb/leveldb/ldb.py +39 -30
  17. dfindexeddb/leveldb/log.py +44 -27
  18. dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
  19. dfindexeddb/leveldb/plugins/interface.py +5 -6
  20. dfindexeddb/leveldb/plugins/manager.py +11 -10
  21. dfindexeddb/leveldb/record.py +71 -62
  22. dfindexeddb/leveldb/utils.py +21 -13
  23. dfindexeddb/utils.py +35 -30
  24. dfindexeddb/version.py +2 -2
  25. dfindexeddb-20251109.dist-info/METADATA +222 -0
  26. dfindexeddb-20251109.dist-info/RECORD +40 -0
  27. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/WHEEL +1 -1
  28. dfindexeddb-20241031.dist-info/AUTHORS +0 -12
  29. dfindexeddb-20241031.dist-info/METADATA +0 -424
  30. dfindexeddb-20241031.dist-info/RECORD +0 -41
  31. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/entry_points.txt +0 -0
  32. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info/licenses}/LICENSE +0 -0
  33. {dfindexeddb-20241031.dist-info → dfindexeddb-20251109.dist-info}/top_level.txt +0 -0
@@ -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 version
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
- ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' +
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
- def default(self, o):
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'\\x{x:02X}')
49
+ out.append(f"\\x{x:02X}")
49
50
  else:
50
51
  out.append(chr(x))
51
- return ''.join(out)
52
+ return "".join(out)
52
53
  if isinstance(o, datetime):
53
54
  return o.isoformat()
54
- if isinstance(o, v8.Undefined):
55
+ if isinstance(o, types.Undefined):
55
56
  return "<undefined>"
56
- if isinstance(o, v8.JSArray):
57
+ if isinstance(o, types.JSArray):
57
58
  return o.__dict__
58
- if isinstance(o, v8.Null):
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, v8.RegExp):
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 == 'json':
72
+ if output == "json":
72
73
  print(json.dumps(structure, indent=2, cls=Encoder))
73
- elif output == 'jsonl':
74
+ elif output == "jsonl":
74
75
  print(json.dumps(structure, cls=Encoder))
75
- elif output == 'repr':
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, 'rb') as fd:
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, 'rb') as fd:
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 ('chrome', 'chromium'):
98
- for db_record in chromium_record.FolderReader(
99
- args.source).GetRecords(
100
- use_manifest=args.use_manifest,
101
- use_sequence_number=args.use_sequence_number):
102
- _Output(db_record, output=args.output)
103
- elif args.format == 'firefox':
104
- for db_record in firefox_record.FileReader(args.source).Records():
105
- _Output(db_record, output=args.output)
106
- elif args.format == 'safari':
107
- for db_record in safari_record.FileReader(args.source).Records():
108
- _Output(db_record, output=args.output)
109
-
110
-
111
- def LdbCommand(args):
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='dfindexeddb',
127
- description='A cli tool for parsing IndexedDB files',
128
- epilog=f'Version {version.GetVersion()}')
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
- 'blink', help='Parse a file as a blink value.')
137
+ "blink", help="Parse a file as a blink-encoded value."
138
+ )
134
139
  parser_blink.add_argument(
135
- '-s', '--source',
140
+ "-s",
141
+ "--source",
136
142
  required=True,
137
143
  type=pathlib.Path,
138
- help=(
139
- 'The source file.'))
144
+ help="The source file.",
145
+ )
140
146
  parser_blink.add_argument(
141
- '-o',
142
- '--output',
143
- choices=[
144
- 'json',
145
- 'jsonl',
146
- 'repr'],
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
- 'gecko', help='Parse a file as a gecko-encoded value.')
156
+ "gecko", help="Parse a file as a gecko-encoded value."
157
+ )
153
158
  parser_gecko.add_argument(
154
- '-s', '--source',
159
+ "-s",
160
+ "--source",
155
161
  required=True,
156
162
  type=pathlib.Path,
157
- help='The source file.')
163
+ help="The source file.",
164
+ )
158
165
  parser_gecko.add_argument(
159
- '-o',
160
- '--output',
161
- choices=[
162
- 'json',
163
- 'jsonl',
164
- 'repr'],
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
- 'db', help='Parse a directory as IndexedDB.')
175
+ "db", help="Parse a directory/file as IndexedDB."
176
+ )
171
177
  parser_db.add_argument(
172
- '-s', '--source',
178
+ "-s",
179
+ "--source",
173
180
  required=True,
174
181
  type=pathlib.Path,
175
182
  help=(
176
- 'The source IndexedDB folder (for chrome/chromium) '
177
- 'or file (for safari).'))
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
- '--use_manifest',
181
- action='store_true',
182
- help='Use manifest file to determine active/deleted records.')
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
- '--use_sequence_number',
185
- action='store_true',
194
+ "--use_sequence_number",
195
+ action="store_true",
186
196
  help=(
187
- 'Use sequence number and file offset to determine active/deleted '
188
- 'records.'))
197
+ "Use sequence number and file offset to determine active/deleted "
198
+ "records."
199
+ ),
200
+ )
189
201
  parser_db.add_argument(
190
- '--format',
202
+ "--format",
191
203
  required=True,
192
- choices=['chromium', 'chrome', 'firefox', 'safari'],
193
- help='The type of IndexedDB to parse.')
204
+ choices=["chromium", "chrome", "firefox", "safari"],
205
+ help="The type of IndexedDB to parse.",
206
+ )
194
207
  parser_db.add_argument(
195
- '-o',
196
- '--output',
197
- choices=[
198
- 'json',
199
- 'jsonl',
200
- 'repr'],
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
- 'ldb',
207
- help='Parse a ldb file as IndexedDB.')
217
+ "ldb", help="Parse a ldb file as IndexedDB."
218
+ )
208
219
  parser_ldb.add_argument(
209
- '-s', '--source',
220
+ "-s",
221
+ "--source",
210
222
  required=True,
211
223
  type=pathlib.Path,
212
- help='The source .ldb file.')
224
+ help="The source .ldb file.",
225
+ )
213
226
  parser_ldb.add_argument(
214
- '-o',
215
- '--output',
216
- choices=[
217
- 'json',
218
- 'jsonl',
219
- 'repr'],
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
- 'log',
226
- help='Parse a log file as IndexedDB.')
236
+ "log", help="Parse a log file as IndexedDB."
237
+ )
227
238
  parser_log.add_argument(
228
- '-s', '--source',
239
+ "-s",
240
+ "--source",
229
241
  required=True,
230
242
  type=pathlib.Path,
231
- help='The source .log file.')
243
+ help="The source .log file.",
244
+ )
232
245
  parser_log.add_argument(
233
- '-o',
234
- '--output',
235
- choices=[
236
- 'json',
237
- 'jsonl',
238
- 'repr'],
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, 'func'):
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 = 0xFFFF0005
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 = 0xFFFF000D
57
- DO_NOT_USE_1 = 0xFFFF000E
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'\xff\x06\x00\x00sNaPpY'
146
+ FRAME_HEADER = b"\xff\x06\x00\x00sNaPpY"