dfindexeddb 20240501__py3-none-any.whl → 20241031__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 (28) hide show
  1. dfindexeddb/indexeddb/chromium/blink.py +5 -0
  2. dfindexeddb/indexeddb/chromium/record.py +11 -6
  3. dfindexeddb/indexeddb/chromium/v8.py +30 -10
  4. dfindexeddb/indexeddb/cli.py +52 -9
  5. dfindexeddb/indexeddb/firefox/definitions.py +143 -0
  6. dfindexeddb/indexeddb/firefox/gecko.py +600 -0
  7. dfindexeddb/indexeddb/firefox/record.py +180 -0
  8. dfindexeddb/indexeddb/safari/webkit.py +52 -20
  9. dfindexeddb/indexeddb/types.py +71 -0
  10. dfindexeddb/leveldb/cli.py +69 -6
  11. dfindexeddb/leveldb/descriptor.py +2 -1
  12. dfindexeddb/leveldb/log.py +9 -3
  13. dfindexeddb/leveldb/plugins/__init__.py +17 -0
  14. dfindexeddb/leveldb/plugins/chrome_notifications.py +135 -0
  15. dfindexeddb/leveldb/plugins/interface.py +36 -0
  16. dfindexeddb/leveldb/plugins/manager.py +75 -0
  17. dfindexeddb/leveldb/plugins/notification_database_data_pb2.py +38 -0
  18. dfindexeddb/leveldb/record.py +33 -1
  19. dfindexeddb/utils.py +35 -1
  20. dfindexeddb/version.py +1 -1
  21. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/METADATA +42 -15
  22. dfindexeddb-20241031.dist-info/RECORD +41 -0
  23. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/WHEEL +1 -1
  24. dfindexeddb-20240501.dist-info/RECORD +0 -32
  25. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/AUTHORS +0 -0
  26. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/LICENSE +0 -0
  27. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/entry_points.txt +0 -0
  28. {dfindexeddb-20240501.dist-info → dfindexeddb-20241031.dist-info}/top_level.txt +0 -0
@@ -780,6 +780,9 @@ class V8ScriptValueDecoder:
780
780
 
781
781
  Returns:
782
782
  A parsed CryptoKey.
783
+
784
+ Raises:
785
+ ParserError: if there is an unexpected CryptoKeySubTag.
783
786
  """
784
787
  _, raw_key_byte = self.deserializer.decoder.DecodeUint8()
785
788
  key_byte = definitions.CryptoKeySubTag(raw_key_byte)
@@ -795,6 +798,8 @@ class V8ScriptValueDecoder:
795
798
  key_type, algorithm_parameters = self._ReadED25519Key()
796
799
  elif key_byte == definitions.CryptoKeySubTag.NO_PARAMS_KEY:
797
800
  key_type, algorithm_parameters = self.ReadNoParamsKey()
801
+ else:
802
+ raise errors.ParserError('Unexpected CryptoKeySubTag')
798
803
 
799
804
  _, raw_usages = self.deserializer.decoder.DecodeUint32Varint()
800
805
  usages = definitions.CryptoKeyUsage(raw_usages)
@@ -1275,14 +1275,17 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1275
1275
  filename = None
1276
1276
  last_modified = None
1277
1277
  token = None
1278
- elif (object_type ==
1279
- definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE):
1278
+ else:
1279
+ if (object_type ==
1280
+ definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE):
1281
+ _, token = decoder.DecodeBlobWithLength()
1282
+ else:
1283
+ token = None
1280
1284
  blob_number = None
1281
1285
  mime_type = None
1282
1286
  size = None
1283
1287
  filename = None
1284
1288
  last_modified = None
1285
- _, token = decoder.DecodeBlobWithLength()
1286
1289
 
1287
1290
  return cls(offset=base_offset + offset, object_type=object_type,
1288
1291
  blob_number=blob_number, mime_type=mime_type, size=size,
@@ -1417,20 +1420,22 @@ class FolderReader:
1417
1420
 
1418
1421
  def GetRecords(
1419
1422
  self,
1420
- use_manifest: bool = False
1423
+ use_manifest: bool = False,
1424
+ use_sequence_number: bool = False
1421
1425
  ) -> Generator[IndexedDBRecord, None, None]:
1422
1426
  """Yield LevelDBRecords.
1423
1427
 
1424
1428
  Args:
1425
1429
  use_manifest: True to use the current manifest in the folder as a means to
1426
1430
  find the active file set.
1427
-
1431
+ use_sequence_number: True to use the sequence number to determine the
1428
1432
  Yields:
1429
1433
  IndexedDBRecord.
1430
1434
  """
1431
1435
  leveldb_folder_reader = record.FolderReader(self.foldername)
1432
1436
  for leveldb_record in leveldb_folder_reader.GetRecords(
1433
- use_manifest=use_manifest):
1437
+ use_manifest=use_manifest,
1438
+ use_sequence_number=use_sequence_number):
1434
1439
  try:
1435
1440
  yield IndexedDBRecord.FromLevelDBRecord(
1436
1441
  leveldb_record)
@@ -13,6 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  """Parsers for v8 javascript serialized objects."""
16
+ from __future__ import annotations
17
+
16
18
  from dataclasses import dataclass
17
19
  from datetime import datetime
18
20
  import io
@@ -25,8 +27,8 @@ from dfindexeddb.indexeddb.chromium import definitions
25
27
 
26
28
 
27
29
  @dataclass
28
- class BufferArrayView:
29
- """A parsed Javascript BufferArrayView."""
30
+ class ArrayBufferView:
31
+ """A parsed Javascript ArrayBufferView."""
30
32
  buffer: bytes
31
33
  tag: definitions.V8ArrayBufferViewTag
32
34
  offset: int
@@ -34,15 +36,23 @@ class BufferArrayView:
34
36
  flags: int
35
37
 
36
38
 
37
- class JSArray(list):
39
+ @dataclass
40
+ class JSArray:
38
41
  """A parsed Javascript array.
39
42
 
40
- This is a wrapper around a standard Python list to allow assigning arbitrary
41
- properties as is possible in the Javascript equivalent.
43
+ A Javascript array behaves like a Python list but allows assigning arbitrary
44
+ properties. The array is stored in the attribute __array__.
42
45
  """
46
+ def __init__(self):
47
+ self.__array__ = []
48
+
49
+ def Append(self, element: Any):
50
+ """Appends a new element to the array."""
51
+ self.__array__.append(element)
43
52
 
44
53
  def __repr__(self):
45
- array_entries = ", ".join([str(entry) for entry in list(self)])
54
+ array_entries = ", ".join(
55
+ [str(entry) for entry in list(self.__array__)])
46
56
  properties = ", ".join(
47
57
  f'{key}: {value}' for key, value in self.properties.items())
48
58
  return f'[{array_entries}, {properties}]'
@@ -52,6 +62,11 @@ class JSArray(list):
52
62
  """Returns the object properties."""
53
63
  return self.__dict__
54
64
 
65
+ def __eq__(self, other: JSArray):
66
+ return (
67
+ self.__array__ == other.__array__
68
+ and self.properties == other.properties)
69
+
55
70
  def __contains__(self, item):
56
71
  return item in self.__dict__
57
72
 
@@ -194,6 +209,7 @@ class ValueDeserializer:
194
209
  if tag and tag == definitions.V8SerializationTag.ARRAY_BUFFER_VIEW:
195
210
  self._ConsumeTag(tag)
196
211
  result = self._ReadJSArrayBufferView(result)
212
+ self.objects[self._GetNextId()] = result
197
213
  return result
198
214
 
199
215
  def _ReadObjectInternal(self) -> Tuple[definitions.V8SerializationTag, Any]:
@@ -385,7 +401,10 @@ class ValueDeserializer:
385
401
  while self._PeekTag() != end_tag:
386
402
  key = self._ReadObject()
387
403
  value = self._ReadObject()
388
- js_object[key] = value
404
+ if isinstance(js_object, dict):
405
+ js_object[key] = value
406
+ else:
407
+ js_object.properties[key] = value
389
408
  num_properties += 1
390
409
  self._ConsumeTag(end_tag)
391
410
  return num_properties
@@ -407,7 +426,7 @@ class ValueDeserializer:
407
426
  js_array = JSArray()
408
427
  _, length = self.decoder.DecodeUint32Varint()
409
428
  for _ in range(length):
410
- js_array.append(Undefined())
429
+ js_array.Append(Undefined())
411
430
 
412
431
  num_properties = self._ReadJSObjectProperties(
413
432
  js_array.__dict__, definitions.V8SerializationTag.END_SPARSE_JS_ARRAY)
@@ -440,7 +459,7 @@ class ValueDeserializer:
440
459
 
441
460
  if self.version < 11 and isinstance(array_object, Undefined):
442
461
  continue
443
- js_array.append(array_object)
462
+ js_array.Append(array_object)
444
463
 
445
464
  num_properties = self._ReadJSObjectProperties(
446
465
  js_array.__dict__, definitions.V8SerializationTag.END_DENSE_JS_ARRAY)
@@ -571,6 +590,7 @@ class ValueDeserializer:
571
590
  if is_resizable:
572
591
  _, max_byte_length = self.decoder.DecodeUint32Varint()
573
592
  if byte_length > max_byte_length:
593
+ self.objects[next_id] = array_buffer
574
594
  return array_buffer
575
595
  if byte_length:
576
596
  _, array_buffer = self.decoder.ReadBytes(byte_length)
@@ -589,7 +609,7 @@ class ValueDeserializer:
589
609
  else:
590
610
  flags = 0
591
611
 
592
- return BufferArrayView(
612
+ return ArrayBufferView(
593
613
  buffer=buffer,
594
614
  tag=definitions.V8ArrayBufferViewTag(tag[0]),
595
615
  offset=byte_offset,
@@ -20,10 +20,13 @@ from datetime import datetime
20
20
  import json
21
21
  import pathlib
22
22
 
23
+ from dfindexeddb import utils
23
24
  from dfindexeddb import version
24
25
  from dfindexeddb.indexeddb.chromium import blink
25
26
  from dfindexeddb.indexeddb.chromium import record as chromium_record
26
27
  from dfindexeddb.indexeddb.chromium import v8
28
+ from dfindexeddb.indexeddb.firefox import gecko
29
+ from dfindexeddb.indexeddb.firefox import record as firefox_record
27
30
  from dfindexeddb.indexeddb.safari import record as safari_record
28
31
 
29
32
 
@@ -36,9 +39,9 @@ class Encoder(json.JSONEncoder):
36
39
  """A JSON encoder class for dfindexeddb fields."""
37
40
  def default(self, o):
38
41
  if dataclasses.is_dataclass(o):
39
- o_dict = dataclasses.asdict(o)
42
+ o_dict = utils.asdict(o)
40
43
  return o_dict
41
- if isinstance(o, bytes):
44
+ if isinstance(o, (bytes, bytearray)):
42
45
  out = []
43
46
  for x in o:
44
47
  if chr(x) not in _VALID_PRINTABLE_CHARACTERS:
@@ -50,6 +53,8 @@ class Encoder(json.JSONEncoder):
50
53
  return o.isoformat()
51
54
  if isinstance(o, v8.Undefined):
52
55
  return "<undefined>"
56
+ if isinstance(o, v8.JSArray):
57
+ return o.__dict__
53
58
  if isinstance(o, v8.Null):
54
59
  return "<null>"
55
60
  if isinstance(o, set):
@@ -72,18 +77,31 @@ def _Output(structure, output):
72
77
 
73
78
 
74
79
  def BlinkCommand(args):
75
- """The CLI for processing a file as a blink value."""
80
+ """The CLI for processing a file as a blink-encoded value."""
76
81
  with open(args.source, 'rb') as fd:
77
82
  buffer = fd.read()
78
83
  blink_value = blink.V8ScriptValueDecoder.FromBytes(buffer)
79
84
  _Output(blink_value, output=args.output)
80
85
 
81
86
 
87
+ def GeckoCommand(args):
88
+ """The CLI for processing a file as a gecko-encoded value."""
89
+ with open(args.source, 'rb') as fd:
90
+ buffer = fd.read()
91
+ blink_value = gecko.JSStructuredCloneDecoder.FromBytes(buffer)
92
+ _Output(blink_value, output=args.output)
93
+
94
+
82
95
  def DbCommand(args):
83
96
  """The CLI for processing a directory as IndexedDB."""
84
97
  if args.format in ('chrome', 'chromium'):
85
98
  for db_record in chromium_record.FolderReader(
86
- args.source).GetRecords(use_manifest=args.use_manifest):
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():
87
105
  _Output(db_record, output=args.output)
88
106
  elif args.format == 'safari':
89
107
  for db_record in safari_record.FileReader(args.source).Records():
@@ -130,6 +148,24 @@ def App():
130
148
  help='Output format. Default is json')
131
149
  parser_blink.set_defaults(func=BlinkCommand)
132
150
 
151
+ parser_gecko = subparsers.add_parser(
152
+ 'gecko', help='Parse a file as a gecko-encoded value.')
153
+ parser_gecko.add_argument(
154
+ '-s', '--source',
155
+ required=True,
156
+ type=pathlib.Path,
157
+ help='The source file.')
158
+ 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')
167
+ parser_gecko.set_defaults(func=GeckoCommand)
168
+
133
169
  parser_db = subparsers.add_parser(
134
170
  'db', help='Parse a directory as IndexedDB.')
135
171
  parser_db.add_argument(
@@ -139,15 +175,22 @@ def App():
139
175
  help=(
140
176
  'The source IndexedDB folder (for chrome/chromium) '
141
177
  'or file (for safari).'))
178
+ recover_group = parser_db.add_mutually_exclusive_group()
179
+ recover_group.add_argument(
180
+ '--use_manifest',
181
+ action='store_true',
182
+ help='Use manifest file to determine active/deleted records.')
183
+ recover_group.add_argument(
184
+ '--use_sequence_number',
185
+ action='store_true',
186
+ help=(
187
+ 'Use sequence number and file offset to determine active/deleted '
188
+ 'records.'))
142
189
  parser_db.add_argument(
143
190
  '--format',
144
191
  required=True,
145
- choices=['chromium', 'chrome', 'safari'],
192
+ choices=['chromium', 'chrome', 'firefox', 'safari'],
146
193
  help='The type of IndexedDB to parse.')
147
- parser_db.add_argument(
148
- '--use_manifest',
149
- action='store_true',
150
- help='Use manifest file to determine active/deleted records.')
151
194
  parser_db.add_argument(
152
195
  '-o',
153
196
  '--output',
@@ -0,0 +1,143 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Definitions for Firefox IndexedDB."""
16
+ from enum import IntEnum
17
+
18
+
19
+ class IndexedDBKeyType(IntEnum):
20
+ """IndexedDB Key Types."""
21
+ TERMINATOR = 0
22
+ FLOAT = 0x10
23
+ DATE = 0x20
24
+ STRING = 0x30
25
+ BINARY = 0x40
26
+ ARRAY = 0x50
27
+
28
+
29
+ MAX_ARRAY_COLLAPSE = 3
30
+ MAX_RECURSION_DEPTH = 64
31
+ MAX_LENGTH = (1 << 30) - 2
32
+ ONE_BYTE_LIMIT = 0x7E
33
+ TWO_BYTE_LIMIT = 0x3FFF + 0x7F
34
+ ONE_BYTE_ADJUST = 1
35
+ TWO_BYTE_ADJUST = -0x7F
36
+ THREE_BYTE_SHIFT = 6
37
+
38
+
39
+ class StructuredDataType(IntEnum):
40
+ """Structured Data Types."""
41
+ FLOAT_MAX = 0xFFF00000
42
+ HEADER = 0xFFF10000
43
+ NULL = 0xFFFF0000
44
+ UNDEFINED = 0xFFFF0001
45
+ BOOLEAN = 0xFFFF0002
46
+ INT32 = 0xFFFF0003
47
+ STRING = 0xFFFF0004
48
+ DATE_OBJECT = 0xFFFF0005
49
+ REGEXP_OBJECT = 0xFFFF0006
50
+ ARRAY_OBJECT = 0xFFFF0007
51
+ OBJECT_OBJECT = 0xFFFF0008
52
+ ARRAY_BUFFER_OBJECT_V2 = 0xFFFF0009
53
+ BOOLEAN_OBJECT = 0xFFFF000A
54
+ STRING_OBJECT = 0xFFFF000B
55
+ NUMBER_OBJECT = 0xFFFF000C
56
+ BACK_REFERENCE_OBJECT = 0xFFFF000D
57
+ DO_NOT_USE_1 = 0xFFFF000E
58
+ DO_NOT_USE_2 = 0xFFFF000F
59
+ TYPED_ARRAY_OBJECT_V2 = 0xFFFF0010
60
+ MAP_OBJECT = 0xFFFF0011
61
+ SET_OBJECT = 0xFFFF0012
62
+ END_OF_KEYS = 0xFFFF0013
63
+ DO_NOT_USE_3 = 0xFFFF0014
64
+ DATA_VIEW_OBJECT_V2 = 0xFFFF0015
65
+ SAVED_FRAME_OBJECT = 0xFFFF0016
66
+ JSPRINCIPALS = 0xFFFF0017
67
+ NULL_JSPRINCIPALS = 0xFFFF0018
68
+ RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM = 0xFFFF0019
69
+ RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM = 0xFFFF001A
70
+ SHARED_ARRAY_BUFFER_OBJECT = 0xFFFF001B
71
+ SHARED_WASM_MEMORY_OBJECT = 0xFFFF001C
72
+ BIGINT = 0xFFFF001D
73
+ BIGINT_OBJECT = 0xFFFF001E
74
+ ARRAY_BUFFER_OBJECT = 0xFFFF001F
75
+ TYPED_ARRAY_OBJECT = 0xFFFF0020
76
+ DATA_VIEW_OBJECT = 0xFFFF0021
77
+ ERROR_OBJECT = 0xFFFF0022
78
+ RESIZABLE_ARRAY_BUFFER_OBJECT = 0xFFFF0023
79
+ GROWABLE_SHARED_ARRAY_BUFFER_OBJECT = 0xFFFF0024
80
+ TYPED_ARRAY_V1_INT8 = 0xFFFF0100
81
+ TYPED_ARRAY_V1_UINT8 = 0xFFFF0101
82
+ TYPED_ARRAY_V1_INT16 = 0xFFFF0102
83
+ TYPED_ARRAY_V1_UINT16 = 0xFFFF0103
84
+ TYPED_ARRAY_V1_INT32 = 0xFFFF0104
85
+ TYPED_ARRAY_V1_UINT32 = 0xFFFF0105
86
+ TYPED_ARRAY_V1_FLOAT32 = 0xFFFF0106
87
+ TYPED_ARRAY_V1_FLOAT64 = 0xFFFF0107
88
+ TYPED_ARRAY_V1_UINT8_CLAMPED = 0xFFFF0108
89
+ TRANSFER_MAP_HEADER = 0xFFFF0200
90
+ TRANSFER_MAP_PENDING_ENTRY = 0xFFFF0201
91
+ TRANSFER_MAP_ARRAY_BUFFER = 0xFFFF0202
92
+ TRANSFER_MAP_STORED_ARRAY_BUFFER = 0xFFFF0203
93
+ TRANSFER_MAP_END_OF_BUILTIN_TYPES = 0xFFFF0204
94
+
95
+
96
+ class StructuredCloneTags(IntEnum):
97
+ """Structured Clone Tags."""
98
+ BLOB = 0xFFFF8001
99
+ FILE_WITHOUT_LASTMODIFIEDDATE = 0xFFFF8002
100
+ FILELIST = 0xFFFF8003
101
+ MUTABLEFILE = 0xFFFF8004
102
+ FILE = 0xFFFF8005
103
+ WASM_MODULE = 0xFFFF8006
104
+ IMAGEDATA = 0xFFFF8007
105
+ DOMPOINT = 0xFFFF8008
106
+ DOMPOINTREADONLY = 0xFFFF8009
107
+ CRYPTOKEY = 0xFFFF800A
108
+ NULL_PRINCIPAL = 0xFFFF800B
109
+ SYSTEM_PRINCIPAL = 0xFFFF800C
110
+ CONTENT_PRINCIPAL = 0xFFFF800D
111
+ DOMQUAD = 0xFFFF800E
112
+ RTCCERTIFICATE = 0xFFFF800F
113
+ DOMRECT = 0xFFFF8010
114
+ DOMRECTREADONLY = 0xFFFF8011
115
+ EXPANDED_PRINCIPAL = 0xFFFF8012
116
+ DOMMATRIX = 0xFFFF8013
117
+ URLSEARCHPARAMS = 0xFFFF8014
118
+ DOMMATRIXREADONLY = 0xFFFF8015
119
+ DOMEXCEPTION = 0xFFFF80016
120
+ EMPTY_SLOT_9 = 0xFFFF8017
121
+ STRUCTUREDCLONETESTER = 0xFFFF8018
122
+ FILESYSTEMHANDLE = 0xFFFF8019
123
+ FILESYSTEMFILEHANDLE = 0xFFFF801A
124
+ FILESYSTEMDIRECTORYHANDLE = 0xFFFF801B
125
+ IMAGEBITMAP = 0xFFFF801C
126
+ MAP_MESSAGEPORT = 0xFFFF801D
127
+ FORMDATA = 0xFFFF801E
128
+ CANVAS = 0xFFFF801F # This tag is for OffscreenCanvas.
129
+ DIRECTORY = 0xFFFF8020
130
+ INPUTSTREAM = 0xFFFF8021
131
+ STRUCTURED_CLONE_HOLDER = 0xFFFF8022
132
+ BROWSING_CONTEXT = 0xFFFF8023
133
+ CLONED_ERROR_OBJECT = 0xFFFF8024
134
+ READABLESTREAM = 0xFFFF8025
135
+ WRITABLESTREAM = 0xFFFF8026
136
+ TRANSFORMSTREAM = 0xFFFF8027
137
+ VIDEOFRAME = 0xFFFF8028
138
+ ENCODEDVIDEOCHUNK = 0xFFFF8029
139
+ AUDIODATA = 0xFFFF8030
140
+ ENCODEDAUDIOCHUNK = 0xFFFF8031
141
+
142
+
143
+ FRAME_HEADER = b'\xff\x06\x00\x00sNaPpY'