dfindexeddb 20240402__py3-none-any.whl → 20240501__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 +915 -18
- dfindexeddb/indexeddb/chromium/definitions.py +66 -0
- dfindexeddb/indexeddb/chromium/record.py +108 -22
- dfindexeddb/indexeddb/chromium/v8.py +8 -3
- dfindexeddb/indexeddb/cli.py +118 -26
- dfindexeddb/indexeddb/safari/definitions.py +123 -0
- dfindexeddb/indexeddb/safari/record.py +238 -0
- dfindexeddb/indexeddb/safari/webkit.py +693 -0
- dfindexeddb/leveldb/cli.py +8 -4
- dfindexeddb/leveldb/definitions.py +2 -0
- dfindexeddb/leveldb/record.py +245 -30
- dfindexeddb/version.py +1 -1
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/METADATA +69 -34
- dfindexeddb-20240501.dist-info/RECORD +32 -0
- dfindexeddb-20240402.dist-info/RECORD +0 -29
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/AUTHORS +0 -0
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/LICENSE +0 -0
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/WHEEL +0 -0
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/entry_points.txt +0 -0
- {dfindexeddb-20240402.dist-info → dfindexeddb-20240501.dist-info}/top_level.txt +0 -0
|
@@ -141,10 +141,12 @@ class BlinkSerializationTag(IntEnum):
|
|
|
141
141
|
ENCODED_AUDIO_CHUNK = ord('y')
|
|
142
142
|
ENCODED_VIDEO_CHUNK = ord('z')
|
|
143
143
|
CROP_TARGET = ord('c')
|
|
144
|
+
RESTRICTION_TARGET = ord('D')
|
|
144
145
|
MEDIA_SOURCE_HANDLE = ord('S')
|
|
145
146
|
DEPRECATED_DETECTED_BARCODE = ord('B')
|
|
146
147
|
DEPRECATED_DETECTED_FACE = ord('F')
|
|
147
148
|
DEPRECATED_DETECTED_TEXT = ord('t')
|
|
149
|
+
FENCED_FRAME_CONFIG = ord('C')
|
|
148
150
|
DOM_EXCEPTION = ord('x')
|
|
149
151
|
TRAILER_OFFSET = 0xFE
|
|
150
152
|
VERSION = 0xFF
|
|
@@ -304,3 +306,67 @@ class V8ErrorTag(IntEnum):
|
|
|
304
306
|
CAUSE = ord('c')
|
|
305
307
|
STACK = ord('s')
|
|
306
308
|
END = ord('.')
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class ImageSerializationTag(IntEnum):
|
|
312
|
+
"""Image Serialization tags."""
|
|
313
|
+
END = 0
|
|
314
|
+
PREDEFINED_COLOR_SPACE = 1
|
|
315
|
+
CANVAS_PIXEL_FORMAT = 2
|
|
316
|
+
IMAGE_DATA_STORAGE_FORMAT = 3
|
|
317
|
+
ORIGIN_CLEAN = 4
|
|
318
|
+
IS_PREMULTIPLIED = 5
|
|
319
|
+
CANVAS_OPACITY_MODE = 6
|
|
320
|
+
PARAMETRIC_COLOR_SPACE = 7
|
|
321
|
+
IMAGE_ORIENTATION = 8
|
|
322
|
+
LAST = IMAGE_ORIENTATION
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class SerializedPredefinedColorSpace(IntEnum):
|
|
326
|
+
"""Serialized Predefined Color Space enumeration."""
|
|
327
|
+
LEGACY_OBSOLETE = 0
|
|
328
|
+
SRGB = 1
|
|
329
|
+
REC2020 = 2
|
|
330
|
+
P3 = 3
|
|
331
|
+
REC2100HLG = 4
|
|
332
|
+
REC2100PQ = 5
|
|
333
|
+
SRGB_LINEAR = 6
|
|
334
|
+
LAST = SRGB_LINEAR
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class SerializedPixelFormat(IntEnum):
|
|
338
|
+
"""Serialized Pixel Format enumeration."""
|
|
339
|
+
NATIVE8_LEGACY_OBSOLETE = 0
|
|
340
|
+
F16 = 1
|
|
341
|
+
RGBA8 = 2
|
|
342
|
+
BGRA8 = 3
|
|
343
|
+
RGBX8 = 4
|
|
344
|
+
LAST = RGBX8
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class SerializedImageDataStorageFormat(IntEnum):
|
|
348
|
+
"""The Serialized Image Data Storage Format."""
|
|
349
|
+
UINT8CLAMPED = 0
|
|
350
|
+
UINT16 = 1
|
|
351
|
+
FLOAT32 = 2
|
|
352
|
+
LAST = FLOAT32
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class SerializedOpacityMode(IntEnum):
|
|
356
|
+
"""The Serialized Opacity Mode."""
|
|
357
|
+
KNONOPAQUE = 0
|
|
358
|
+
KOPAQUE = 1
|
|
359
|
+
KLAST = KOPAQUE
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class SerializedImageOrientation(IntEnum):
|
|
363
|
+
"""The Serialized Image Orientation."""
|
|
364
|
+
TOP_LEFT = 0
|
|
365
|
+
TOP_RIGHT = 1
|
|
366
|
+
BOTTOM_RIGHT = 2
|
|
367
|
+
BOTTOM_LEFT = 3
|
|
368
|
+
LEFT_TOP = 4
|
|
369
|
+
RIGHT_TOP = 5
|
|
370
|
+
RIGHT_BOTTOM = 6
|
|
371
|
+
LEFT_BOTTOM = 7
|
|
372
|
+
LAST = LEFT_BOTTOM
|
|
@@ -17,13 +17,16 @@ from __future__ import annotations
|
|
|
17
17
|
from dataclasses import dataclass, field
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
import io
|
|
20
|
-
|
|
20
|
+
import pathlib
|
|
21
|
+
import sys
|
|
22
|
+
import traceback
|
|
23
|
+
from typing import Any, BinaryIO, Generator, Optional, Tuple, Type, TypeVar, \
|
|
24
|
+
Union
|
|
21
25
|
|
|
22
26
|
from dfindexeddb import errors
|
|
23
27
|
from dfindexeddb.indexeddb.chromium import blink
|
|
24
28
|
from dfindexeddb.indexeddb.chromium import definitions
|
|
25
|
-
from dfindexeddb.leveldb import
|
|
26
|
-
from dfindexeddb.leveldb import log
|
|
29
|
+
from dfindexeddb.leveldb import record
|
|
27
30
|
from dfindexeddb.leveldb import utils
|
|
28
31
|
|
|
29
32
|
|
|
@@ -457,7 +460,7 @@ class MaxDatabaseIdKey(BaseIndexedDBKey):
|
|
|
457
460
|
cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
|
|
458
461
|
base_offset: int = 0
|
|
459
462
|
) -> MaxDatabaseIdKey:
|
|
460
|
-
"""Decodes the maximum
|
|
463
|
+
"""Decodes the maximum database key."""
|
|
461
464
|
offset, key_type = decoder.DecodeUint8()
|
|
462
465
|
if key_type != definitions.GlobalMetadataKeyType.MAX_DATABASE_ID:
|
|
463
466
|
raise errors.ParserError('Not a MaxDatabaseIdKey')
|
|
@@ -546,7 +549,7 @@ class EarliestSweepKey(BaseIndexedDBKey):
|
|
|
546
549
|
|
|
547
550
|
|
|
548
551
|
@dataclass
|
|
549
|
-
class
|
|
552
|
+
class EarliestCompactionTimeKey(BaseIndexedDBKey):
|
|
550
553
|
"""An earliest compaction time IndexedDB key."""
|
|
551
554
|
|
|
552
555
|
def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
|
|
@@ -558,11 +561,11 @@ class EarlistCompactionTimeKey(BaseIndexedDBKey):
|
|
|
558
561
|
def FromDecoder(
|
|
559
562
|
cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
|
|
560
563
|
base_offset: int = 0
|
|
561
|
-
) ->
|
|
564
|
+
) -> EarliestCompactionTimeKey:
|
|
562
565
|
"""Decodes the earliest compaction time key."""
|
|
563
566
|
offset, key_type = decoder.DecodeUint8()
|
|
564
567
|
if key_type != definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME:
|
|
565
|
-
raise errors.ParserError('Not a
|
|
568
|
+
raise errors.ParserError('Not a EarliestCompactionTimeKey')
|
|
566
569
|
return cls(offset=base_offset + offset, key_prefix=key_prefix)
|
|
567
570
|
|
|
568
571
|
|
|
@@ -668,7 +671,7 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
|
|
|
668
671
|
definitions.GlobalMetadataKeyType
|
|
669
672
|
.EARLIEST_SWEEP: EarliestSweepKey,
|
|
670
673
|
definitions.GlobalMetadataKeyType
|
|
671
|
-
.EARLIEST_COMPACTION_TIME:
|
|
674
|
+
.EARLIEST_COMPACTION_TIME: EarliestCompactionTimeKey,
|
|
672
675
|
definitions.GlobalMetadataKeyType
|
|
673
676
|
.SCOPES_PREFIX: ScopesPrefixKey,
|
|
674
677
|
definitions.GlobalMetadataKeyType
|
|
@@ -692,7 +695,7 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
|
|
|
692
695
|
Type[DatabaseFreeListKey],
|
|
693
696
|
Type[DatabaseNameKey],
|
|
694
697
|
Type[EarliestSweepKey],
|
|
695
|
-
Type[
|
|
698
|
+
Type[EarliestCompactionTimeKey],
|
|
696
699
|
Type[MaxDatabaseIdKey],
|
|
697
700
|
Type[RecoveryBlobJournalKey],
|
|
698
701
|
Type[SchemaVersionKey],
|
|
@@ -972,7 +975,7 @@ class ObjectStoreDataValue:
|
|
|
972
975
|
blob_offset: the blob offset, only valid if wrapped.
|
|
973
976
|
value: the blink serialized value, only valid if not wrapped.
|
|
974
977
|
"""
|
|
975
|
-
|
|
978
|
+
unknown: int
|
|
976
979
|
is_wrapped: bool
|
|
977
980
|
blob_size: Optional[int]
|
|
978
981
|
blob_offset: Optional[int]
|
|
@@ -1003,7 +1006,7 @@ class ObjectStoreDataKey(BaseIndexedDBKey):
|
|
|
1003
1006
|
_, blob_size = decoder.DecodeVarint()
|
|
1004
1007
|
_, blob_offset = decoder.DecodeVarint()
|
|
1005
1008
|
return ObjectStoreDataValue(
|
|
1006
|
-
|
|
1009
|
+
unknown=unknown_integer,
|
|
1007
1010
|
is_wrapped=True,
|
|
1008
1011
|
blob_size=blob_size,
|
|
1009
1012
|
blob_offset=blob_offset,
|
|
@@ -1011,7 +1014,7 @@ class ObjectStoreDataKey(BaseIndexedDBKey):
|
|
|
1011
1014
|
_, blink_bytes = decoder.ReadBytes()
|
|
1012
1015
|
blink_value = blink.V8ScriptValueDecoder.FromBytes(blink_bytes)
|
|
1013
1016
|
return ObjectStoreDataValue(
|
|
1014
|
-
|
|
1017
|
+
unknown=unknown_integer,
|
|
1015
1018
|
is_wrapped=False,
|
|
1016
1019
|
blob_size=None,
|
|
1017
1020
|
blob_offset=None,
|
|
@@ -1332,29 +1335,112 @@ class IndexedDBRecord:
|
|
|
1332
1335
|
"""An IndexedDB Record.
|
|
1333
1336
|
|
|
1334
1337
|
Attributes:
|
|
1338
|
+
path: the source file path
|
|
1335
1339
|
offset: the offset of the record.
|
|
1336
1340
|
key: the key of the record.
|
|
1337
1341
|
value: the value of the record.
|
|
1338
1342
|
sequence_number: if available, the sequence number of the record.
|
|
1339
1343
|
type: the type of the record.
|
|
1344
|
+
level: the leveldb level, if applicable, None can indicate the record
|
|
1345
|
+
originated from a log file or the level could not be determined.
|
|
1346
|
+
recovered: True if the record is a recovered record.
|
|
1340
1347
|
"""
|
|
1348
|
+
path: str
|
|
1341
1349
|
offset: int
|
|
1342
1350
|
key: Any
|
|
1343
1351
|
value: Any
|
|
1344
|
-
sequence_number: int
|
|
1352
|
+
sequence_number: Optional[int]
|
|
1345
1353
|
type: int
|
|
1354
|
+
level: Optional[int]
|
|
1355
|
+
recovered: Optional[bool]
|
|
1346
1356
|
|
|
1347
1357
|
@classmethod
|
|
1348
1358
|
def FromLevelDBRecord(
|
|
1349
|
-
cls,
|
|
1359
|
+
cls,
|
|
1360
|
+
db_record: record.LevelDBRecord
|
|
1350
1361
|
) -> IndexedDBRecord:
|
|
1351
1362
|
"""Returns an IndexedDBRecord from a ParsedInternalKey."""
|
|
1352
|
-
idb_key = IndexedDbKey.FromBytes(
|
|
1353
|
-
|
|
1363
|
+
idb_key = IndexedDbKey.FromBytes(
|
|
1364
|
+
db_record.record.key, base_offset=db_record.record.offset)
|
|
1365
|
+
idb_value = idb_key.ParseValue(db_record.record.value)
|
|
1354
1366
|
return cls(
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1367
|
+
path=db_record.path,
|
|
1368
|
+
offset=db_record.record.offset,
|
|
1369
|
+
key=idb_key,
|
|
1370
|
+
value=idb_value,
|
|
1371
|
+
sequence_number=db_record.record.sequence_number if hasattr(
|
|
1372
|
+
db_record.record, 'sequence_number') else None,
|
|
1373
|
+
type=db_record.record.record_type,
|
|
1374
|
+
level=db_record.level,
|
|
1375
|
+
recovered=db_record.recovered)
|
|
1376
|
+
|
|
1377
|
+
@classmethod
|
|
1378
|
+
def FromFile(
|
|
1379
|
+
cls,
|
|
1380
|
+
file_path: pathlib.Path
|
|
1381
|
+
) -> Generator[IndexedDBRecord, None, None]:
|
|
1382
|
+
"""Yields IndexedDBRecords from a file."""
|
|
1383
|
+
for db_record in record.LevelDBRecord.FromFile(file_path):
|
|
1384
|
+
try:
|
|
1385
|
+
yield cls.FromLevelDBRecord(db_record)
|
|
1386
|
+
except(
|
|
1387
|
+
errors.ParserError,
|
|
1388
|
+
errors.DecoderError,
|
|
1389
|
+
NotImplementedError) as err:
|
|
1390
|
+
print((
|
|
1391
|
+
'Error parsing Indexeddb record: '
|
|
1392
|
+
f'{err} at offset {db_record.record.offset} in '
|
|
1393
|
+
f'{db_record.path}'),
|
|
1394
|
+
file=sys.stderr)
|
|
1395
|
+
print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)
|
|
1396
|
+
|
|
1397
|
+
|
|
1398
|
+
class FolderReader:
|
|
1399
|
+
"""A IndexedDB folder reader for Chrome/Chromium.
|
|
1400
|
+
|
|
1401
|
+
Attributes:
|
|
1402
|
+
foldername (str): the source LevelDB folder.
|
|
1403
|
+
"""
|
|
1404
|
+
|
|
1405
|
+
def __init__(self, foldername: pathlib.Path):
|
|
1406
|
+
"""Initializes the FileReader.
|
|
1407
|
+
|
|
1408
|
+
Args:
|
|
1409
|
+
foldername: the source IndexedDB folder.
|
|
1410
|
+
|
|
1411
|
+
Raises:
|
|
1412
|
+
ValueError: if foldername is None or not a directory.
|
|
1413
|
+
"""
|
|
1414
|
+
if not foldername or not foldername.is_dir():
|
|
1415
|
+
raise ValueError(f'{foldername} is None or not a directory')
|
|
1416
|
+
self.foldername = foldername
|
|
1417
|
+
|
|
1418
|
+
def GetRecords(
|
|
1419
|
+
self,
|
|
1420
|
+
use_manifest: bool = False
|
|
1421
|
+
) -> Generator[IndexedDBRecord, None, None]:
|
|
1422
|
+
"""Yield LevelDBRecords.
|
|
1423
|
+
|
|
1424
|
+
Args:
|
|
1425
|
+
use_manifest: True to use the current manifest in the folder as a means to
|
|
1426
|
+
find the active file set.
|
|
1427
|
+
|
|
1428
|
+
Yields:
|
|
1429
|
+
IndexedDBRecord.
|
|
1430
|
+
"""
|
|
1431
|
+
leveldb_folder_reader = record.FolderReader(self.foldername)
|
|
1432
|
+
for leveldb_record in leveldb_folder_reader.GetRecords(
|
|
1433
|
+
use_manifest=use_manifest):
|
|
1434
|
+
try:
|
|
1435
|
+
yield IndexedDBRecord.FromLevelDBRecord(
|
|
1436
|
+
leveldb_record)
|
|
1437
|
+
except(
|
|
1438
|
+
errors.ParserError,
|
|
1439
|
+
errors.DecoderError,
|
|
1440
|
+
NotImplementedError) as err:
|
|
1441
|
+
print((
|
|
1442
|
+
'Error parsing Indexeddb record: '
|
|
1443
|
+
f'{err} at offset {leveldb_record.record.offset} in '
|
|
1444
|
+
f'{leveldb_record.path}'),
|
|
1445
|
+
file=sys.stderr)
|
|
1446
|
+
print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)
|
|
@@ -152,7 +152,12 @@ class ValueDeserializer:
|
|
|
152
152
|
_, tag_value = self.decoder.PeekBytes(1)
|
|
153
153
|
except errors.DecoderError:
|
|
154
154
|
return None
|
|
155
|
-
|
|
155
|
+
try:
|
|
156
|
+
return definitions.V8SerializationTag(tag_value[0])
|
|
157
|
+
except ValueError as error:
|
|
158
|
+
raise errors.ParserError(
|
|
159
|
+
f'Invalid v8 tag value {tag_value} at offset'
|
|
160
|
+
f' {self.decoder.stream.tell()}') from error
|
|
156
161
|
|
|
157
162
|
def _ReadTag(self) -> definitions.V8SerializationTag:
|
|
158
163
|
"""Returns the next non-padding serialization tag.
|
|
@@ -269,7 +274,7 @@ class ValueDeserializer:
|
|
|
269
274
|
self.version >= 15):
|
|
270
275
|
parsed_object = self.ReadSharedObject()
|
|
271
276
|
elif self.version < 13:
|
|
272
|
-
self.decoder.stream.seek(-1)
|
|
277
|
+
self.decoder.stream.seek(-1, os.SEEK_CUR)
|
|
273
278
|
parsed_object = self.ReadHostObject()
|
|
274
279
|
else:
|
|
275
280
|
parsed_object = None
|
|
@@ -492,7 +497,7 @@ class ValueDeserializer:
|
|
|
492
497
|
return value
|
|
493
498
|
|
|
494
499
|
def _ReadJSRegExp(self) -> RegExp:
|
|
495
|
-
"""Reads a
|
|
500
|
+
"""Reads a Javascript regular expression from the current position."""
|
|
496
501
|
next_id = self._GetNextId()
|
|
497
502
|
pattern = self.ReadString()
|
|
498
503
|
_, flags = self.decoder.DecodeUint32Varint() # TODO: verify flags
|
dfindexeddb/indexeddb/cli.py
CHANGED
|
@@ -15,17 +15,16 @@
|
|
|
15
15
|
"""A CLI tool for dfindexeddb."""
|
|
16
16
|
import argparse
|
|
17
17
|
import dataclasses
|
|
18
|
+
import enum
|
|
18
19
|
from datetime import datetime
|
|
19
20
|
import json
|
|
20
21
|
import pathlib
|
|
21
|
-
import sys
|
|
22
|
-
import traceback
|
|
23
22
|
|
|
24
|
-
from dfindexeddb import errors
|
|
25
23
|
from dfindexeddb import version
|
|
26
|
-
from dfindexeddb.
|
|
24
|
+
from dfindexeddb.indexeddb.chromium import blink
|
|
27
25
|
from dfindexeddb.indexeddb.chromium import record as chromium_record
|
|
28
26
|
from dfindexeddb.indexeddb.chromium import v8
|
|
27
|
+
from dfindexeddb.indexeddb.safari import record as safari_record
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
_VALID_PRINTABLE_CHARACTERS = (
|
|
@@ -57,6 +56,8 @@ class Encoder(json.JSONEncoder):
|
|
|
57
56
|
return list(o)
|
|
58
57
|
if isinstance(o, v8.RegExp):
|
|
59
58
|
return str(o)
|
|
59
|
+
if isinstance(o, enum.Enum):
|
|
60
|
+
return o.name
|
|
60
61
|
return json.JSONEncoder.default(self, o)
|
|
61
62
|
|
|
62
63
|
|
|
@@ -70,21 +71,34 @@ def _Output(structure, output):
|
|
|
70
71
|
print(structure)
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
def
|
|
74
|
-
"""The CLI for processing a
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
74
|
+
def BlinkCommand(args):
|
|
75
|
+
"""The CLI for processing a file as a blink value."""
|
|
76
|
+
with open(args.source, 'rb') as fd:
|
|
77
|
+
buffer = fd.read()
|
|
78
|
+
blink_value = blink.V8ScriptValueDecoder.FromBytes(buffer)
|
|
79
|
+
_Output(blink_value, output=args.output)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def DbCommand(args):
|
|
83
|
+
"""The CLI for processing a directory as IndexedDB."""
|
|
84
|
+
if args.format in ('chrome', 'chromium'):
|
|
85
|
+
for db_record in chromium_record.FolderReader(
|
|
86
|
+
args.source).GetRecords(use_manifest=args.use_manifest):
|
|
87
|
+
_Output(db_record, output=args.output)
|
|
88
|
+
elif args.format == 'safari':
|
|
89
|
+
for db_record in safari_record.FileReader(args.source).Records():
|
|
90
|
+
_Output(db_record, output=args.output)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def LdbCommand(args):
|
|
94
|
+
"""The CLI for processing a LevelDB table (.ldb) file as IndexedDB."""
|
|
95
|
+
for db_record in chromium_record.IndexedDBRecord.FromFile(args.source):
|
|
96
|
+
_Output(db_record, output=args.output)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def LogCommand(args):
|
|
100
|
+
"""The CLI for processing a LevelDB log file as IndexedDB."""
|
|
101
|
+
for db_record in chromium_record.IndexedDBRecord.FromFile(args.source):
|
|
88
102
|
_Output(db_record, output=args.output)
|
|
89
103
|
|
|
90
104
|
|
|
@@ -92,12 +106,87 @@ def App():
|
|
|
92
106
|
"""The CLI app entrypoint for dfindexeddb."""
|
|
93
107
|
parser = argparse.ArgumentParser(
|
|
94
108
|
prog='dfindexeddb',
|
|
95
|
-
description='A cli tool for parsing
|
|
109
|
+
description='A cli tool for parsing IndexedDB files',
|
|
96
110
|
epilog=f'Version {version.GetVersion()}')
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
|
|
112
|
+
subparsers = parser.add_subparsers()
|
|
113
|
+
|
|
114
|
+
parser_blink = subparsers.add_parser(
|
|
115
|
+
'blink', help='Parse a file as a blink value.')
|
|
116
|
+
parser_blink.add_argument(
|
|
117
|
+
'-s', '--source',
|
|
118
|
+
required=True,
|
|
119
|
+
type=pathlib.Path,
|
|
120
|
+
help=(
|
|
121
|
+
'The source file.'))
|
|
122
|
+
parser_blink.add_argument(
|
|
123
|
+
'-o',
|
|
124
|
+
'--output',
|
|
125
|
+
choices=[
|
|
126
|
+
'json',
|
|
127
|
+
'jsonl',
|
|
128
|
+
'repr'],
|
|
129
|
+
default='json',
|
|
130
|
+
help='Output format. Default is json')
|
|
131
|
+
parser_blink.set_defaults(func=BlinkCommand)
|
|
132
|
+
|
|
133
|
+
parser_db = subparsers.add_parser(
|
|
134
|
+
'db', help='Parse a directory as IndexedDB.')
|
|
135
|
+
parser_db.add_argument(
|
|
136
|
+
'-s', '--source',
|
|
137
|
+
required=True,
|
|
138
|
+
type=pathlib.Path,
|
|
139
|
+
help=(
|
|
140
|
+
'The source IndexedDB folder (for chrome/chromium) '
|
|
141
|
+
'or file (for safari).'))
|
|
142
|
+
parser_db.add_argument(
|
|
143
|
+
'--format',
|
|
144
|
+
required=True,
|
|
145
|
+
choices=['chromium', 'chrome', 'safari'],
|
|
146
|
+
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
|
+
parser_db.add_argument(
|
|
152
|
+
'-o',
|
|
153
|
+
'--output',
|
|
154
|
+
choices=[
|
|
155
|
+
'json',
|
|
156
|
+
'jsonl',
|
|
157
|
+
'repr'],
|
|
158
|
+
default='json',
|
|
159
|
+
help='Output format. Default is json')
|
|
160
|
+
parser_db.set_defaults(func=DbCommand)
|
|
161
|
+
|
|
162
|
+
parser_ldb = subparsers.add_parser(
|
|
163
|
+
'ldb',
|
|
164
|
+
help='Parse a ldb file as IndexedDB.')
|
|
165
|
+
parser_ldb.add_argument(
|
|
166
|
+
'-s', '--source',
|
|
167
|
+
required=True,
|
|
168
|
+
type=pathlib.Path,
|
|
169
|
+
help='The source .ldb file.')
|
|
170
|
+
parser_ldb.add_argument(
|
|
171
|
+
'-o',
|
|
172
|
+
'--output',
|
|
173
|
+
choices=[
|
|
174
|
+
'json',
|
|
175
|
+
'jsonl',
|
|
176
|
+
'repr'],
|
|
177
|
+
default='json',
|
|
178
|
+
help='Output format. Default is json')
|
|
179
|
+
parser_ldb.set_defaults(func=LdbCommand)
|
|
180
|
+
|
|
181
|
+
parser_log = subparsers.add_parser(
|
|
182
|
+
'log',
|
|
183
|
+
help='Parse a log file as IndexedDB.')
|
|
184
|
+
parser_log.add_argument(
|
|
185
|
+
'-s', '--source',
|
|
186
|
+
required=True,
|
|
187
|
+
type=pathlib.Path,
|
|
188
|
+
help='The source .log file.')
|
|
189
|
+
parser_log.add_argument(
|
|
101
190
|
'-o',
|
|
102
191
|
'--output',
|
|
103
192
|
choices=[
|
|
@@ -106,7 +195,10 @@ def App():
|
|
|
106
195
|
'repr'],
|
|
107
196
|
default='json',
|
|
108
197
|
help='Output format. Default is json')
|
|
109
|
-
|
|
198
|
+
parser_log.set_defaults(func=LogCommand)
|
|
110
199
|
|
|
111
200
|
args = parser.parse_args()
|
|
112
|
-
args
|
|
201
|
+
if hasattr(args, 'func'):
|
|
202
|
+
args.func(args)
|
|
203
|
+
else:
|
|
204
|
+
parser.print_help()
|
|
@@ -0,0 +1,123 @@
|
|
|
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 Webkit/Safari."""
|
|
16
|
+
from enum import IntEnum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
CurrentVersion = 0x0000000F # 15
|
|
20
|
+
TerminatorTag = 0xFFFFFFFF
|
|
21
|
+
StringPoolTag = 0xFFFFFFFE
|
|
22
|
+
NonIndexPropertiesTag = 0xFFFFFFFD
|
|
23
|
+
ImageDataPoolTag = 0xFFFFFFFE
|
|
24
|
+
StringDataIs8BitFlag = 0x80000000
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
SIDBKeyVersion = 0x00
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SIDBKeyType(IntEnum):
|
|
31
|
+
"""SIDBKeyType."""
|
|
32
|
+
MIN = 0x00
|
|
33
|
+
NUMBER = 0x20
|
|
34
|
+
DATE = 0x40
|
|
35
|
+
STRING = 0x60
|
|
36
|
+
BINARY = 0x80
|
|
37
|
+
ARRAY = 0xA0
|
|
38
|
+
MAX = 0xFF
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SerializationTag(IntEnum):
|
|
42
|
+
"""Database Metadata key types.
|
|
43
|
+
|
|
44
|
+
All tags are recorded as a single uint8_t.
|
|
45
|
+
"""
|
|
46
|
+
ARRAY = 1
|
|
47
|
+
OBJECT = 2
|
|
48
|
+
UNDEFINED = 3
|
|
49
|
+
NULL = 4
|
|
50
|
+
INT = 5
|
|
51
|
+
ZERO = 6
|
|
52
|
+
ONE = 7
|
|
53
|
+
FALSE = 8
|
|
54
|
+
TRUE = 9
|
|
55
|
+
DOUBLE = 10
|
|
56
|
+
DATE = 11
|
|
57
|
+
FILE = 12
|
|
58
|
+
FILE_LIST = 13
|
|
59
|
+
IMAGE_DATA = 14
|
|
60
|
+
BLOB = 15
|
|
61
|
+
STRING = 16
|
|
62
|
+
EMPTY_STRING = 17
|
|
63
|
+
REG_EXP = 18
|
|
64
|
+
OBJECT_REFERENCE = 19
|
|
65
|
+
MESSAGE_PORT_REFERENCE = 20
|
|
66
|
+
ARRAY_BUFFER = 21
|
|
67
|
+
ARRAY_BUFFER_VIEW = 22
|
|
68
|
+
ARRAY_BUFFER_TRANSFER = 23
|
|
69
|
+
TRUE_OBJECT = 24
|
|
70
|
+
FALSE_OBJECT = 25
|
|
71
|
+
STRING_OBJECT = 26
|
|
72
|
+
EMPTY_STRING_OBJECT = 27
|
|
73
|
+
NUMBER_OBJECT = 28
|
|
74
|
+
SET_OBJECT = 29
|
|
75
|
+
MAP_OBJECT = 30
|
|
76
|
+
NON_MAP_PROPERTIES = 31
|
|
77
|
+
NON_SET_PROPERTIES = 32
|
|
78
|
+
CRYPTO_KEY = 33
|
|
79
|
+
SHARED_ARRAY_BUFFER = 34
|
|
80
|
+
WASM_MODULE = 35
|
|
81
|
+
DOM_POINT_READONLY = 36
|
|
82
|
+
DOM_POINT = 37
|
|
83
|
+
DOM_RECT_READONLY = 38
|
|
84
|
+
DOM_RECT = 39
|
|
85
|
+
DOM_MATRIX_READONLY = 40
|
|
86
|
+
DOM_MATRIX = 41
|
|
87
|
+
DOM_QUAD = 42
|
|
88
|
+
IMAGE_BITMAP_TRANSFER = 43
|
|
89
|
+
RTC_CERTIFICATE = 44
|
|
90
|
+
IMAGE_BITMAP = 45
|
|
91
|
+
OFF_SCREEN_CANVAS_TRANSFER = 46
|
|
92
|
+
BIGINT = 47
|
|
93
|
+
BIGINT_OBJECT = 48
|
|
94
|
+
WASM_MEMORY = 49
|
|
95
|
+
RTC_DATA_CHANNEL_TRANSFER = 50
|
|
96
|
+
DOM_EXCEPTION = 51
|
|
97
|
+
WEB_CODECS_ENCODED_VIDEO_CHUNK = 52
|
|
98
|
+
WEB_CODECS_VIDEO_FRAME = 53
|
|
99
|
+
RESIZABLE_ARRAY_BUFFER = 54
|
|
100
|
+
ERROR_INSTANCE = 55
|
|
101
|
+
IN_MEMORY_OFFSCREEN_CANVAS = 56
|
|
102
|
+
IN_MEMORY_MESSAGE_PORT = 57
|
|
103
|
+
WEB_CODECS_ENCODED_AUDIO_CHUNK = 58
|
|
104
|
+
WEB_CODECS_AUDIO_DATA = 59
|
|
105
|
+
MEDIA_STREAM_TRACK = 60
|
|
106
|
+
MEDIA_SOURCE_HANDLE_TRANSFER = 61
|
|
107
|
+
ERROR = 255
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ArrayBufferViewSubtag(IntEnum):
|
|
111
|
+
"""ArrayBufferView sub tags."""
|
|
112
|
+
DATA_VIEW = 0
|
|
113
|
+
INT8_ARRAY = 1
|
|
114
|
+
UINT8_ARRAY = 2
|
|
115
|
+
UINT8_CLAMPED_ARRAY = 3
|
|
116
|
+
INT16_ARRAY = 4
|
|
117
|
+
UINT16_ARRAY = 5
|
|
118
|
+
INT32_ARRAY = 6
|
|
119
|
+
UINT32_ARRAY = 7
|
|
120
|
+
FLOAT32_ARRAY = 8
|
|
121
|
+
FLOAT64_ARRAY = 9
|
|
122
|
+
BIG_INT64_ARRAY = 10
|
|
123
|
+
BIG_UINT64_ARRAY = 11
|