dfindexeddb 20241031__py3-none-any.whl → 20241105__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/definitions.py +5 -0
- dfindexeddb/indexeddb/chromium/record.py +181 -95
- dfindexeddb/indexeddb/chromium/v8.py +19 -70
- dfindexeddb/indexeddb/cli.py +30 -23
- dfindexeddb/indexeddb/firefox/gecko.py +8 -8
- dfindexeddb/indexeddb/safari/definitions.py +7 -7
- dfindexeddb/indexeddb/safari/webkit.py +31 -122
- dfindexeddb/indexeddb/types.py +1 -1
- dfindexeddb/leveldb/cli.py +18 -11
- dfindexeddb/leveldb/descriptor.py +22 -6
- dfindexeddb/leveldb/ldb.py +5 -2
- dfindexeddb/leveldb/log.py +11 -5
- dfindexeddb/leveldb/plugins/manager.py +2 -2
- dfindexeddb/utils.py +1 -1
- dfindexeddb/version.py +1 -1
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/METADATA +1 -1
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/RECORD +22 -22
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/AUTHORS +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/LICENSE +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/WHEEL +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/entry_points.txt +0 -0
- {dfindexeddb-20241031.dist-info → dfindexeddb-20241105.dist-info}/top_level.txt +0 -0
|
@@ -23,6 +23,7 @@ from typing import Any, BinaryIO, Dict, Optional, Set, Tuple, Union
|
|
|
23
23
|
|
|
24
24
|
from dfindexeddb import errors
|
|
25
25
|
from dfindexeddb import utils
|
|
26
|
+
from dfindexeddb.indexeddb import types
|
|
26
27
|
from dfindexeddb.indexeddb.chromium import definitions
|
|
27
28
|
|
|
28
29
|
|
|
@@ -36,61 +37,6 @@ class ArrayBufferView:
|
|
|
36
37
|
flags: int
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
@dataclass
|
|
40
|
-
class JSArray:
|
|
41
|
-
"""A parsed Javascript array.
|
|
42
|
-
|
|
43
|
-
A Javascript array behaves like a Python list but allows assigning arbitrary
|
|
44
|
-
properties. The array is stored in the attribute __array__.
|
|
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)
|
|
52
|
-
|
|
53
|
-
def __repr__(self):
|
|
54
|
-
array_entries = ", ".join(
|
|
55
|
-
[str(entry) for entry in list(self.__array__)])
|
|
56
|
-
properties = ", ".join(
|
|
57
|
-
f'{key}: {value}' for key, value in self.properties.items())
|
|
58
|
-
return f'[{array_entries}, {properties}]'
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def properties(self) -> Dict[str, Any]:
|
|
62
|
-
"""Returns the object properties."""
|
|
63
|
-
return self.__dict__
|
|
64
|
-
|
|
65
|
-
def __eq__(self, other: JSArray):
|
|
66
|
-
return (
|
|
67
|
-
self.__array__ == other.__array__
|
|
68
|
-
and self.properties == other.properties)
|
|
69
|
-
|
|
70
|
-
def __contains__(self, item):
|
|
71
|
-
return item in self.__dict__
|
|
72
|
-
|
|
73
|
-
def __getitem__(self, name):
|
|
74
|
-
return self.__dict__[name]
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@dataclass
|
|
78
|
-
class Null:
|
|
79
|
-
"""A parsed Javascript Null."""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@dataclass
|
|
83
|
-
class RegExp:
|
|
84
|
-
"""A parsed Javascript RegExp."""
|
|
85
|
-
pattern: str
|
|
86
|
-
flags: int
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
@dataclass(frozen=True)
|
|
90
|
-
class Undefined:
|
|
91
|
-
"""A parsed Javascript undef."""
|
|
92
|
-
|
|
93
|
-
|
|
94
40
|
class ValueDeserializer:
|
|
95
41
|
"""A class to deserialize v8 Javascript values/objects.
|
|
96
42
|
|
|
@@ -223,9 +169,9 @@ class ValueDeserializer:
|
|
|
223
169
|
_ = self.decoder.DecodeUint32Varint()
|
|
224
170
|
parsed_object = self._ReadObject()
|
|
225
171
|
elif tag == definitions.V8SerializationTag.UNDEFINED:
|
|
226
|
-
parsed_object = Undefined()
|
|
172
|
+
parsed_object = types.Undefined()
|
|
227
173
|
elif tag == definitions.V8SerializationTag.NULL:
|
|
228
|
-
parsed_object = Null()
|
|
174
|
+
parsed_object = types.Null()
|
|
229
175
|
elif tag == definitions.V8SerializationTag.TRUE:
|
|
230
176
|
parsed_object = True
|
|
231
177
|
elif tag == definitions.V8SerializationTag.FALSE:
|
|
@@ -385,7 +331,7 @@ class ValueDeserializer:
|
|
|
385
331
|
|
|
386
332
|
def _ReadJSObjectProperties(
|
|
387
333
|
self,
|
|
388
|
-
js_object: Union[Dict, JSArray],
|
|
334
|
+
js_object: Union[Dict, types.JSArray],
|
|
389
335
|
end_tag: definitions.V8SerializationTag
|
|
390
336
|
) -> int:
|
|
391
337
|
"""Reads key-value properties and sets them to the given js_object.
|
|
@@ -415,7 +361,7 @@ class ValueDeserializer:
|
|
|
415
361
|
self.next_id += 1
|
|
416
362
|
return next_id
|
|
417
363
|
|
|
418
|
-
def ReadSparseJSArray(self) -> JSArray:
|
|
364
|
+
def ReadSparseJSArray(self) -> types.JSArray:
|
|
419
365
|
"""Reads a sparse encoded JSArray from the current position.
|
|
420
366
|
|
|
421
367
|
Raises:
|
|
@@ -423,13 +369,13 @@ class ValueDeserializer:
|
|
|
423
369
|
"""
|
|
424
370
|
next_id = self._GetNextId()
|
|
425
371
|
|
|
426
|
-
js_array = JSArray()
|
|
372
|
+
js_array = types.JSArray()
|
|
427
373
|
_, length = self.decoder.DecodeUint32Varint()
|
|
428
374
|
for _ in range(length):
|
|
429
|
-
js_array.
|
|
375
|
+
js_array.values.append(types.Undefined())
|
|
430
376
|
|
|
431
377
|
num_properties = self._ReadJSObjectProperties(
|
|
432
|
-
js_array.
|
|
378
|
+
js_array.properties, definitions.V8SerializationTag.END_SPARSE_JS_ARRAY)
|
|
433
379
|
_, expected_num_properties = self.decoder.DecodeUint32Varint()
|
|
434
380
|
_, expected_length = self.decoder.DecodeUint32Varint()
|
|
435
381
|
|
|
@@ -440,7 +386,7 @@ class ValueDeserializer:
|
|
|
440
386
|
self.objects[next_id] = js_array
|
|
441
387
|
return js_array
|
|
442
388
|
|
|
443
|
-
def ReadDenseJSArray(self) -> JSArray:
|
|
389
|
+
def ReadDenseJSArray(self) -> types.JSArray:
|
|
444
390
|
"""Reads a dense encoded JSArray from the current position.
|
|
445
391
|
|
|
446
392
|
Raises:
|
|
@@ -448,7 +394,7 @@ class ValueDeserializer:
|
|
|
448
394
|
"""
|
|
449
395
|
next_id = self._GetNextId()
|
|
450
396
|
|
|
451
|
-
js_array = JSArray()
|
|
397
|
+
js_array = types.JSArray()
|
|
452
398
|
_, length = self.decoder.DecodeUint32Varint()
|
|
453
399
|
for _ in range(length):
|
|
454
400
|
tag = self._PeekTag()
|
|
@@ -457,12 +403,12 @@ class ValueDeserializer:
|
|
|
457
403
|
continue
|
|
458
404
|
array_object = self._ReadObject()
|
|
459
405
|
|
|
460
|
-
if self.version < 11 and isinstance(array_object, Undefined):
|
|
406
|
+
if self.version < 11 and isinstance(array_object, types.Undefined):
|
|
461
407
|
continue
|
|
462
|
-
js_array.
|
|
408
|
+
js_array.values.append(array_object)
|
|
463
409
|
|
|
464
410
|
num_properties = self._ReadJSObjectProperties(
|
|
465
|
-
js_array.
|
|
411
|
+
js_array.properties, definitions.V8SerializationTag.END_DENSE_JS_ARRAY)
|
|
466
412
|
_, expected_num_properties = self.decoder.DecodeUint32Varint()
|
|
467
413
|
_, expected_length = self.decoder.DecodeUint32Varint()
|
|
468
414
|
if num_properties != expected_num_properties:
|
|
@@ -515,12 +461,12 @@ class ValueDeserializer:
|
|
|
515
461
|
self.objects[next_id] = value
|
|
516
462
|
return value
|
|
517
463
|
|
|
518
|
-
def _ReadJSRegExp(self) -> RegExp:
|
|
464
|
+
def _ReadJSRegExp(self) -> types.RegExp:
|
|
519
465
|
"""Reads a Javascript regular expression from the current position."""
|
|
520
466
|
next_id = self._GetNextId()
|
|
521
467
|
pattern = self.ReadString()
|
|
522
468
|
_, flags = self.decoder.DecodeUint32Varint() # TODO: verify flags
|
|
523
|
-
regexp = RegExp(pattern=pattern, flags=flags)
|
|
469
|
+
regexp = types.RegExp(pattern=pattern, flags=str(flags))
|
|
524
470
|
self.objects[next_id] = regexp
|
|
525
471
|
return regexp
|
|
526
472
|
|
|
@@ -572,7 +518,10 @@ class ValueDeserializer:
|
|
|
572
518
|
return js_set
|
|
573
519
|
|
|
574
520
|
def _ReadJSArrayBuffer(
|
|
575
|
-
self,
|
|
521
|
+
self,
|
|
522
|
+
is_shared: bool,
|
|
523
|
+
is_resizable: bool
|
|
524
|
+
) -> bytes:
|
|
576
525
|
"""Reads a Javascript ArrayBuffer from the current position.
|
|
577
526
|
|
|
578
527
|
Args:
|
dfindexeddb/indexeddb/cli.py
CHANGED
|
@@ -22,9 +22,9 @@ import pathlib
|
|
|
22
22
|
|
|
23
23
|
from dfindexeddb import utils
|
|
24
24
|
from dfindexeddb import 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
|
|
@@ -51,15 +51,15 @@ class Encoder(json.JSONEncoder):
|
|
|
51
51
|
return ''.join(out)
|
|
52
52
|
if isinstance(o, datetime):
|
|
53
53
|
return o.isoformat()
|
|
54
|
-
if isinstance(o,
|
|
55
|
-
return
|
|
56
|
-
if isinstance(o,
|
|
54
|
+
if isinstance(o, types.Undefined):
|
|
55
|
+
return '<undefined>'
|
|
56
|
+
if isinstance(o, types.JSArray):
|
|
57
57
|
return o.__dict__
|
|
58
|
-
if isinstance(o,
|
|
59
|
-
return
|
|
58
|
+
if isinstance(o, types.Null):
|
|
59
|
+
return '<null>'
|
|
60
60
|
if isinstance(o, set):
|
|
61
61
|
return list(o)
|
|
62
|
-
if isinstance(o,
|
|
62
|
+
if isinstance(o, types.RegExp):
|
|
63
63
|
return str(o)
|
|
64
64
|
if isinstance(o, enum.Enum):
|
|
65
65
|
return o.name
|
|
@@ -130,13 +130,13 @@ def App():
|
|
|
130
130
|
subparsers = parser.add_subparsers()
|
|
131
131
|
|
|
132
132
|
parser_blink = subparsers.add_parser(
|
|
133
|
-
'blink', help='Parse a file as a blink value.')
|
|
133
|
+
'blink', help='Parse a file as a blink-encoded value.')
|
|
134
134
|
parser_blink.add_argument(
|
|
135
|
-
'-s',
|
|
135
|
+
'-s',
|
|
136
|
+
'--source',
|
|
136
137
|
required=True,
|
|
137
138
|
type=pathlib.Path,
|
|
138
|
-
help=
|
|
139
|
-
'The source file.'))
|
|
139
|
+
help='The source file.')
|
|
140
140
|
parser_blink.add_argument(
|
|
141
141
|
'-o',
|
|
142
142
|
'--output',
|
|
@@ -145,13 +145,14 @@ def App():
|
|
|
145
145
|
'jsonl',
|
|
146
146
|
'repr'],
|
|
147
147
|
default='json',
|
|
148
|
-
help='Output format. Default is json')
|
|
148
|
+
help='Output format. Default is json.')
|
|
149
149
|
parser_blink.set_defaults(func=BlinkCommand)
|
|
150
150
|
|
|
151
151
|
parser_gecko = subparsers.add_parser(
|
|
152
152
|
'gecko', help='Parse a file as a gecko-encoded value.')
|
|
153
153
|
parser_gecko.add_argument(
|
|
154
|
-
'-s',
|
|
154
|
+
'-s',
|
|
155
|
+
'--source',
|
|
155
156
|
required=True,
|
|
156
157
|
type=pathlib.Path,
|
|
157
158
|
help='The source file.')
|
|
@@ -163,18 +164,19 @@ def App():
|
|
|
163
164
|
'jsonl',
|
|
164
165
|
'repr'],
|
|
165
166
|
default='json',
|
|
166
|
-
help='Output format. Default is json')
|
|
167
|
+
help='Output format. Default is json.')
|
|
167
168
|
parser_gecko.set_defaults(func=GeckoCommand)
|
|
168
169
|
|
|
169
170
|
parser_db = subparsers.add_parser(
|
|
170
|
-
'db', help='Parse a directory as IndexedDB.')
|
|
171
|
+
'db', help='Parse a directory/file as IndexedDB.')
|
|
171
172
|
parser_db.add_argument(
|
|
172
|
-
'-s',
|
|
173
|
+
'-s',
|
|
174
|
+
'--source',
|
|
173
175
|
required=True,
|
|
174
176
|
type=pathlib.Path,
|
|
175
177
|
help=(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
'The source IndexedDB folder (for chrome/chromium) '
|
|
179
|
+
'or sqlite3 file (for firefox/safari).'))
|
|
178
180
|
recover_group = parser_db.add_mutually_exclusive_group()
|
|
179
181
|
recover_group.add_argument(
|
|
180
182
|
'--use_manifest',
|
|
@@ -189,7 +191,11 @@ def App():
|
|
|
189
191
|
parser_db.add_argument(
|
|
190
192
|
'--format',
|
|
191
193
|
required=True,
|
|
192
|
-
choices=[
|
|
194
|
+
choices=[
|
|
195
|
+
'chromium',
|
|
196
|
+
'chrome',
|
|
197
|
+
'firefox',
|
|
198
|
+
'safari'],
|
|
193
199
|
help='The type of IndexedDB to parse.')
|
|
194
200
|
parser_db.add_argument(
|
|
195
201
|
'-o',
|
|
@@ -199,14 +205,15 @@ def App():
|
|
|
199
205
|
'jsonl',
|
|
200
206
|
'repr'],
|
|
201
207
|
default='json',
|
|
202
|
-
help='Output format. Default is json')
|
|
208
|
+
help='Output format. Default is json.')
|
|
203
209
|
parser_db.set_defaults(func=DbCommand)
|
|
204
210
|
|
|
205
211
|
parser_ldb = subparsers.add_parser(
|
|
206
212
|
'ldb',
|
|
207
213
|
help='Parse a ldb file as IndexedDB.')
|
|
208
214
|
parser_ldb.add_argument(
|
|
209
|
-
'-s',
|
|
215
|
+
'-s',
|
|
216
|
+
'--source',
|
|
210
217
|
required=True,
|
|
211
218
|
type=pathlib.Path,
|
|
212
219
|
help='The source .ldb file.')
|
|
@@ -218,7 +225,7 @@ def App():
|
|
|
218
225
|
'jsonl',
|
|
219
226
|
'repr'],
|
|
220
227
|
default='json',
|
|
221
|
-
help='Output format. Default is json')
|
|
228
|
+
help='Output format. Default is json.')
|
|
222
229
|
parser_ldb.set_defaults(func=LdbCommand)
|
|
223
230
|
|
|
224
231
|
parser_log = subparsers.add_parser(
|
|
@@ -237,7 +244,7 @@ def App():
|
|
|
237
244
|
'jsonl',
|
|
238
245
|
'repr'],
|
|
239
246
|
default='json',
|
|
240
|
-
help='Output format. Default is json')
|
|
247
|
+
help='Output format. Default is json.')
|
|
241
248
|
parser_log.set_defaults(func=LogCommand)
|
|
242
249
|
|
|
243
250
|
args = parser.parse_args()
|
|
@@ -345,10 +345,10 @@ class JSStructuredCloneDecoder(utils.FromDecoderMixin):
|
|
|
345
345
|
|
|
346
346
|
if is_version1:
|
|
347
347
|
value = self._DecodeV1ArrayBuffer(array_type, number_elements)
|
|
348
|
-
|
|
348
|
+
byte_offset = 0 # pylint: disable=unused-variable
|
|
349
349
|
else:
|
|
350
350
|
value = self._StartRead()
|
|
351
|
-
|
|
351
|
+
byte_offset = self.decoder.DecodeUint64() # pylint: disable=unused-variable
|
|
352
352
|
|
|
353
353
|
self.all_objects[dummy_index] = value
|
|
354
354
|
return value
|
|
@@ -370,7 +370,7 @@ class JSStructuredCloneDecoder(utils.FromDecoderMixin):
|
|
|
370
370
|
elif (buffer_type ==
|
|
371
371
|
definitions.StructuredDataType.RESIZABLE_ARRAY_BUFFER_OBJECT):
|
|
372
372
|
_, number_bytes = self.decoder.DecodeUint64()
|
|
373
|
-
_,
|
|
373
|
+
_, max_bytes = self.decoder.DecodeUint64() # pylint: disable=unused-variable
|
|
374
374
|
else:
|
|
375
375
|
number_bytes = data
|
|
376
376
|
|
|
@@ -454,7 +454,7 @@ class JSStructuredCloneDecoder(utils.FromDecoderMixin):
|
|
|
454
454
|
raise errors.ParserError('String tag not found')
|
|
455
455
|
|
|
456
456
|
pattern = self._DecodeString(string_data)
|
|
457
|
-
return types.RegExp(pattern=pattern, flags=flags)
|
|
457
|
+
return types.RegExp(pattern=pattern, flags=str(flags))
|
|
458
458
|
|
|
459
459
|
def _StartRead(self) -> Any:
|
|
460
460
|
"""Reads the start of a serialized value.
|
|
@@ -550,7 +550,7 @@ class JSStructuredCloneDecoder(utils.FromDecoderMixin):
|
|
|
550
550
|
# align the stream to an 8 byte boundary
|
|
551
551
|
offset = self.decoder.stream.tell() % 8
|
|
552
552
|
if offset:
|
|
553
|
-
_,
|
|
553
|
+
_, slack_bytes = self.decoder.ReadBytes(8 - offset) # pylint: disable=unused-variable
|
|
554
554
|
|
|
555
555
|
return value
|
|
556
556
|
|
|
@@ -584,9 +584,9 @@ class JSStructuredCloneDecoder(utils.FromDecoderMixin):
|
|
|
584
584
|
while pos < len(raw_data):
|
|
585
585
|
is_uncompressed = raw_data[pos]
|
|
586
586
|
block_size = int.from_bytes(
|
|
587
|
-
raw_data[pos + 1:pos + 4], byteorder=
|
|
588
|
-
|
|
589
|
-
raw_data[pos + 4: pos + 9], byteorder=
|
|
587
|
+
raw_data[pos + 1:pos + 4], byteorder='little', signed=False)
|
|
588
|
+
masked_checksum = int.from_bytes( # pylint: disable=unused-variable
|
|
589
|
+
raw_data[pos + 4: pos + 9], byteorder='little', signed=False)
|
|
590
590
|
if is_uncompressed:
|
|
591
591
|
uncompressed_data += raw_data[pos + 8: pos + 8 + block_size - 4]
|
|
592
592
|
else:
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
from enum import IntEnum
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
CURRENT_VERSION = 0x0000000F # 15
|
|
20
|
+
TERMINATOR_TAG = 0xFFFFFFFF
|
|
21
|
+
STRING_POOL_TAG = 0xFFFFFFFE
|
|
22
|
+
NON_INDEX_PROPERTIES_TAG = 0xFFFFFFFD
|
|
23
|
+
IMAGE_DATA_POOL_TAG = 0xFFFFFFFE
|
|
24
|
+
STRING_DATA_IS_8BIT_FLAG = 0x80000000
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
SIDB_KEY_VERSION = 0x00
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class SIDBKeyType(IntEnum):
|
|
@@ -23,6 +23,7 @@ from typing import Any, Dict, List, Tuple, Union
|
|
|
23
23
|
|
|
24
24
|
from dfindexeddb import errors
|
|
25
25
|
from dfindexeddb import utils
|
|
26
|
+
from dfindexeddb.indexeddb import types
|
|
26
27
|
from dfindexeddb.indexeddb.safari import definitions
|
|
27
28
|
|
|
28
29
|
|
|
@@ -82,102 +83,6 @@ class FileList:
|
|
|
82
83
|
files: List[FileData]
|
|
83
84
|
|
|
84
85
|
|
|
85
|
-
class JSArray:
|
|
86
|
-
"""A parsed Javascript array.
|
|
87
|
-
|
|
88
|
-
A Javascript array behaves like a Python list but allows assigning arbitrary
|
|
89
|
-
properties. The array is stored in the attribute __array__.
|
|
90
|
-
"""
|
|
91
|
-
def __init__(self):
|
|
92
|
-
self.__array__ = []
|
|
93
|
-
|
|
94
|
-
def Append(self, element: Any):
|
|
95
|
-
"""Appends a new element to the array."""
|
|
96
|
-
self.__array__.append(element)
|
|
97
|
-
|
|
98
|
-
def __repr__(self):
|
|
99
|
-
array_entries = ", ".join(
|
|
100
|
-
[str(entry) for entry in list(self.__array__)])
|
|
101
|
-
properties = ", ".join(
|
|
102
|
-
f'{key}: {value}' for key, value in self.properties.items())
|
|
103
|
-
return f'[{array_entries}, {properties}]'
|
|
104
|
-
|
|
105
|
-
@property
|
|
106
|
-
def properties(self) -> Dict[str, Any]:
|
|
107
|
-
"""Returns the object properties."""
|
|
108
|
-
return self.__dict__
|
|
109
|
-
|
|
110
|
-
def __eq__(self, other: JSArray):
|
|
111
|
-
return (
|
|
112
|
-
self.__array__ == other.__array__
|
|
113
|
-
and self.properties == other.properties)
|
|
114
|
-
|
|
115
|
-
def __contains__(self, item):
|
|
116
|
-
return item in self.__dict__
|
|
117
|
-
|
|
118
|
-
def __getitem__(self, name):
|
|
119
|
-
return self.__dict__[name]
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class JSSet:
|
|
123
|
-
"""A parsed Javascript set.
|
|
124
|
-
|
|
125
|
-
A Javascript set behaves like a Python set but allows assigning arbitrary
|
|
126
|
-
properties. The array is stored in the attribute __set__.
|
|
127
|
-
"""
|
|
128
|
-
def __init__(self):
|
|
129
|
-
self.__set__ = set()
|
|
130
|
-
|
|
131
|
-
def Add(self, element: Any):
|
|
132
|
-
"""Adds a new element to the set."""
|
|
133
|
-
self.__set__.add(element)
|
|
134
|
-
|
|
135
|
-
def __repr__(self):
|
|
136
|
-
array_entries = ", ".join(
|
|
137
|
-
[str(entry) for entry in list(self.__set__)])
|
|
138
|
-
properties = ", ".join(
|
|
139
|
-
f'{key}: {value}' for key, value in self.properties.items())
|
|
140
|
-
return f'[{array_entries}, {properties}]'
|
|
141
|
-
|
|
142
|
-
@property
|
|
143
|
-
def properties(self) -> Dict[str, Any]:
|
|
144
|
-
"""Returns the object properties."""
|
|
145
|
-
return self.__dict__
|
|
146
|
-
|
|
147
|
-
def __eq__(self, other: JSSet):
|
|
148
|
-
return (
|
|
149
|
-
self.__set__ == other.__set__
|
|
150
|
-
and self.properties == other.properties)
|
|
151
|
-
|
|
152
|
-
def __contains__(self, item):
|
|
153
|
-
return item in self.__dict__
|
|
154
|
-
|
|
155
|
-
def __getitem__(self, name):
|
|
156
|
-
return self.__dict__[name]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@dataclass
|
|
160
|
-
class Null:
|
|
161
|
-
"""A parsed JavaScript Null."""
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
@dataclass
|
|
165
|
-
class RegExp:
|
|
166
|
-
"""A parsed JavaScript RegExp.
|
|
167
|
-
|
|
168
|
-
Attributes:
|
|
169
|
-
pattern: the pattern.
|
|
170
|
-
flags: the flags.
|
|
171
|
-
"""
|
|
172
|
-
pattern: str
|
|
173
|
-
flags: str
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
@dataclass(frozen=True)
|
|
177
|
-
class Undefined:
|
|
178
|
-
"""A parsed JavaScript undef."""
|
|
179
|
-
|
|
180
|
-
|
|
181
86
|
@dataclass
|
|
182
87
|
class IDBKeyData(utils.FromDecoderMixin):
|
|
183
88
|
"""An IDBKeyData.
|
|
@@ -193,7 +98,10 @@ class IDBKeyData(utils.FromDecoderMixin):
|
|
|
193
98
|
|
|
194
99
|
@classmethod
|
|
195
100
|
def FromDecoder(
|
|
196
|
-
cls,
|
|
101
|
+
cls,
|
|
102
|
+
decoder: utils.StreamDecoder,
|
|
103
|
+
base_offset: int = 0
|
|
104
|
+
) -> IDBKeyData:
|
|
197
105
|
"""Decodes an IDBKeyData from the current position of decoder.
|
|
198
106
|
|
|
199
107
|
Refer to IDBSerialization.cpp for the encoding scheme.
|
|
@@ -235,7 +143,7 @@ class IDBKeyData(utils.FromDecoderMixin):
|
|
|
235
143
|
return data
|
|
236
144
|
|
|
237
145
|
offset, version_header = decoder.DecodeUint8()
|
|
238
|
-
if version_header != definitions.
|
|
146
|
+
if version_header != definitions.SIDB_KEY_VERSION:
|
|
239
147
|
raise errors.ParserError('SIDBKeyVersion not found.')
|
|
240
148
|
|
|
241
149
|
_, raw_key_type = decoder.DecodeUint8()
|
|
@@ -303,7 +211,7 @@ class SerializedScriptValueDecoder():
|
|
|
303
211
|
raise errors.ParserError(
|
|
304
212
|
f'Invalid terminal {terminal_byte} at offset {offset}') from error
|
|
305
213
|
|
|
306
|
-
def DecodeArray(self) -> JSArray:
|
|
214
|
+
def DecodeArray(self) -> types.JSArray:
|
|
307
215
|
"""Decodes an Array value.
|
|
308
216
|
|
|
309
217
|
Returns:
|
|
@@ -313,25 +221,25 @@ class SerializedScriptValueDecoder():
|
|
|
313
221
|
ParserError if an invalid Terminator tag was found.
|
|
314
222
|
"""
|
|
315
223
|
_, length = self.decoder.DecodeUint32()
|
|
316
|
-
array = JSArray()
|
|
224
|
+
array = types.JSArray()
|
|
317
225
|
self.object_pool.append(array)
|
|
318
226
|
for _ in range(length):
|
|
319
227
|
_, _ = self.decoder.DecodeUint32()
|
|
320
228
|
_, value = self.DecodeValue()
|
|
321
|
-
array.
|
|
229
|
+
array.values.append(value)
|
|
322
230
|
|
|
323
231
|
offset, terminator_tag = self.decoder.DecodeUint32()
|
|
324
|
-
if terminator_tag != definitions.
|
|
232
|
+
if terminator_tag != definitions.TERMINATOR_TAG:
|
|
325
233
|
raise errors.ParserError(f'Terminator tag not found at offset {offset}.')
|
|
326
234
|
|
|
327
235
|
offset, tag = self.decoder.DecodeUint32()
|
|
328
|
-
if tag == definitions.
|
|
329
|
-
while tag != definitions.
|
|
236
|
+
if tag == definitions.NON_INDEX_PROPERTIES_TAG:
|
|
237
|
+
while tag != definitions.TERMINATOR_TAG:
|
|
330
238
|
name = self.DecodeStringData()
|
|
331
239
|
_, value = self.DecodeValue()
|
|
332
240
|
_, tag = self.decoder.DecodeUint32()
|
|
333
241
|
array.properties[name] = value
|
|
334
|
-
elif tag != definitions.
|
|
242
|
+
elif tag != definitions.TERMINATOR_TAG:
|
|
335
243
|
raise errors.ParserError(f'Terminator tag not found at offset {offset}.')
|
|
336
244
|
return array
|
|
337
245
|
|
|
@@ -340,7 +248,7 @@ class SerializedScriptValueDecoder():
|
|
|
340
248
|
tag = self.PeekTag()
|
|
341
249
|
js_object = {}
|
|
342
250
|
self.object_pool.append(js_object)
|
|
343
|
-
while tag != definitions.
|
|
251
|
+
while tag != definitions.TERMINATOR_TAG:
|
|
344
252
|
name = self.DecodeStringData()
|
|
345
253
|
_, value = self.DecodeValue()
|
|
346
254
|
js_object[name] = value
|
|
@@ -362,10 +270,10 @@ class SerializedScriptValueDecoder():
|
|
|
362
270
|
* unable to to decode a buffer as utf-16-le.
|
|
363
271
|
"""
|
|
364
272
|
peeked_tag = self.PeekTag()
|
|
365
|
-
if peeked_tag == definitions.
|
|
273
|
+
if peeked_tag == definitions.TERMINATOR_TAG:
|
|
366
274
|
raise errors.ParserError('Unexpected TerminatorTag found')
|
|
367
275
|
|
|
368
|
-
if peeked_tag == definitions.
|
|
276
|
+
if peeked_tag == definitions.STRING_POOL_TAG:
|
|
369
277
|
_ = self.decoder.DecodeUint32()
|
|
370
278
|
if len(self.constant_pool) <= 0xff:
|
|
371
279
|
_, cp_index = self.decoder.DecodeUint8()
|
|
@@ -378,11 +286,11 @@ class SerializedScriptValueDecoder():
|
|
|
378
286
|
return self.constant_pool[cp_index]
|
|
379
287
|
|
|
380
288
|
_, length_with_8bit_flag = self.decoder.DecodeUint32()
|
|
381
|
-
if length_with_8bit_flag == definitions.
|
|
289
|
+
if length_with_8bit_flag == definitions.TERMINATOR_TAG:
|
|
382
290
|
raise errors.ParserError('Disallowed string length found.')
|
|
383
291
|
|
|
384
292
|
length = length_with_8bit_flag & 0x7FFFFFFF
|
|
385
|
-
is_8bit = length_with_8bit_flag & definitions.
|
|
293
|
+
is_8bit = length_with_8bit_flag & definitions.STRING_DATA_IS_8BIT_FLAG
|
|
386
294
|
|
|
387
295
|
if is_8bit:
|
|
388
296
|
_, characters = self.decoder.ReadBytes(length)
|
|
@@ -391,9 +299,10 @@ class SerializedScriptValueDecoder():
|
|
|
391
299
|
_, characters = self.decoder.ReadBytes(2*length)
|
|
392
300
|
try:
|
|
393
301
|
value = characters.decode('utf-16-le')
|
|
394
|
-
except UnicodeDecodeError:
|
|
302
|
+
except UnicodeDecodeError as exc:
|
|
395
303
|
raise errors.ParserError(
|
|
396
|
-
f'Unable to decode {len(characters)} characters as utf-16-le'
|
|
304
|
+
f'Unable to decode {len(characters)} characters as utf-16-le'
|
|
305
|
+
) from exc
|
|
397
306
|
self.constant_pool.append(value)
|
|
398
307
|
return value
|
|
399
308
|
|
|
@@ -465,11 +374,11 @@ class SerializedScriptValueDecoder():
|
|
|
465
374
|
'memory_cost': memory_cost
|
|
466
375
|
}
|
|
467
376
|
|
|
468
|
-
def DecodeRegExp(self) -> RegExp:
|
|
377
|
+
def DecodeRegExp(self) -> types.RegExp:
|
|
469
378
|
"""Decodes a RegExp value."""
|
|
470
379
|
pattern = self.DecodeStringData()
|
|
471
380
|
flags = self.DecodeStringData()
|
|
472
|
-
return RegExp(pattern=pattern, flags=flags)
|
|
381
|
+
return types.RegExp(pattern=pattern, flags=flags)
|
|
473
382
|
|
|
474
383
|
def DecodeMapData(self) -> dict:
|
|
475
384
|
"""Decodes a Map value."""
|
|
@@ -487,7 +396,7 @@ class SerializedScriptValueDecoder():
|
|
|
487
396
|
_, tag = self.DecodeSerializationTag()
|
|
488
397
|
|
|
489
398
|
pool_tag = self.PeekTag()
|
|
490
|
-
while pool_tag != definitions.
|
|
399
|
+
while pool_tag != definitions.TERMINATOR_TAG:
|
|
491
400
|
name = self.DecodeStringData()
|
|
492
401
|
value = self.DecodeValue()
|
|
493
402
|
js_map[name] = value
|
|
@@ -496,22 +405,22 @@ class SerializedScriptValueDecoder():
|
|
|
496
405
|
_, tag = self.decoder.DecodeUint32()
|
|
497
406
|
return js_map
|
|
498
407
|
|
|
499
|
-
def DecodeSetData(self) -> JSSet:
|
|
408
|
+
def DecodeSetData(self) -> types.JSSet:
|
|
500
409
|
"""Decodes a SetData value."""
|
|
501
410
|
tag = self.PeekSerializationTag()
|
|
502
|
-
js_set = JSSet()
|
|
411
|
+
js_set = types.JSSet()
|
|
503
412
|
self.object_pool.append(js_set)
|
|
504
413
|
|
|
505
414
|
while tag != definitions.SerializationTag.NON_SET_PROPERTIES:
|
|
506
415
|
_, key = self.DecodeValue()
|
|
507
|
-
js_set.
|
|
416
|
+
js_set.values.add(key)
|
|
508
417
|
tag = self.PeekSerializationTag()
|
|
509
418
|
|
|
510
419
|
# consume the NonSetPropertiesTag
|
|
511
420
|
_, tag = self.DecodeSerializationTag()
|
|
512
421
|
|
|
513
422
|
pool_tag = self.PeekTag()
|
|
514
|
-
while pool_tag != definitions.
|
|
423
|
+
while pool_tag != definitions.TERMINATOR_TAG:
|
|
515
424
|
name = self.DecodeStringData()
|
|
516
425
|
value = self.DecodeValue()
|
|
517
426
|
js_set.properties[name] = value
|
|
@@ -614,7 +523,7 @@ class SerializedScriptValueDecoder():
|
|
|
614
523
|
ParserError when CurrentVersion is not found.
|
|
615
524
|
"""
|
|
616
525
|
_, current_version = self.decoder.DecodeUint32()
|
|
617
|
-
if current_version != definitions.
|
|
526
|
+
if current_version != definitions.CURRENT_VERSION:
|
|
618
527
|
raise errors.ParserError(
|
|
619
528
|
f'{current_version} is not the expected CurrentVersion')
|
|
620
529
|
_, value = self.DecodeValue()
|
|
@@ -635,9 +544,9 @@ class SerializedScriptValueDecoder():
|
|
|
635
544
|
elif tag == definitions.SerializationTag.OBJECT:
|
|
636
545
|
value = self.DecodeObject()
|
|
637
546
|
elif tag == definitions.SerializationTag.UNDEFINED:
|
|
638
|
-
value = Undefined()
|
|
547
|
+
value = types.Undefined()
|
|
639
548
|
elif tag == definitions.SerializationTag.NULL:
|
|
640
|
-
value = Null()
|
|
549
|
+
value = types.Null()
|
|
641
550
|
elif tag == definitions.SerializationTag.INT:
|
|
642
551
|
_, value = self.decoder.DecodeInt32()
|
|
643
552
|
elif tag == definitions.SerializationTag.ZERO:
|