dfindexeddb 20240417__tar.gz → 20240519__tar.gz

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 (45) hide show
  1. {dfindexeddb-20240417/dfindexeddb.egg-info → dfindexeddb-20240519}/PKG-INFO +74 -80
  2. dfindexeddb-20240519/README.md +193 -0
  3. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/chromium/blink.py +5 -0
  4. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/chromium/record.py +90 -7
  5. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/cli.py +82 -80
  6. dfindexeddb-20240519/dfindexeddb/indexeddb/safari/definitions.py +123 -0
  7. dfindexeddb-20240519/dfindexeddb/indexeddb/safari/record.py +238 -0
  8. dfindexeddb-20240519/dfindexeddb/indexeddb/safari/webkit.py +701 -0
  9. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/cli.py +70 -11
  10. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/log.py +9 -3
  11. dfindexeddb-20240519/dfindexeddb/leveldb/plugins/__init__.py +17 -0
  12. dfindexeddb-20240519/dfindexeddb/leveldb/plugins/chrome_notifications.py +135 -0
  13. dfindexeddb-20240519/dfindexeddb/leveldb/plugins/interface.py +36 -0
  14. dfindexeddb-20240519/dfindexeddb/leveldb/plugins/manager.py +75 -0
  15. dfindexeddb-20240519/dfindexeddb/leveldb/plugins/notification_database_data_pb2.py +38 -0
  16. dfindexeddb-20240519/dfindexeddb/leveldb/record.py +349 -0
  17. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/utils.py +34 -0
  18. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/version.py +1 -1
  19. {dfindexeddb-20240417 → dfindexeddb-20240519/dfindexeddb.egg-info}/PKG-INFO +74 -80
  20. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb.egg-info/SOURCES.txt +9 -1
  21. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb.egg-info/requires.txt +4 -0
  22. {dfindexeddb-20240417 → dfindexeddb-20240519}/pyproject.toml +8 -4
  23. dfindexeddb-20240417/README.md +0 -202
  24. dfindexeddb-20240417/dfindexeddb/leveldb/record.py +0 -190
  25. {dfindexeddb-20240417 → dfindexeddb-20240519}/AUTHORS +0 -0
  26. {dfindexeddb-20240417 → dfindexeddb-20240519}/LICENSE +0 -0
  27. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/__init__.py +0 -0
  28. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/errors.py +0 -0
  29. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/__init__.py +0 -0
  30. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/chromium/__init__.py +0 -0
  31. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/chromium/definitions.py +0 -0
  32. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/chromium/v8.py +0 -0
  33. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/firefox/__init__.py +0 -0
  34. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/safari/__init__.py +0 -0
  35. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/indexeddb/utils.py +0 -0
  36. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/__init__.py +0 -0
  37. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/definitions.py +0 -0
  38. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/descriptor.py +0 -0
  39. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/ldb.py +0 -0
  40. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb/leveldb/utils.py +0 -0
  41. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb.egg-info/dependency_links.txt +0 -0
  42. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb.egg-info/entry_points.txt +0 -0
  43. {dfindexeddb-20240417 → dfindexeddb-20240519}/dfindexeddb.egg-info/top_level.txt +0 -0
  44. {dfindexeddb-20240417 → dfindexeddb-20240519}/setup.cfg +0 -0
  45. {dfindexeddb-20240417 → dfindexeddb-20240519}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dfindexeddb
3
- Version: 20240417
3
+ Version: 20240519
4
4
  Summary: dfindexeddb is an experimental Python tool for performing digital forensic analysis of IndexedDB and leveldb files.
5
5
  Author-email: Syd Pleno <sydp@google.com>
6
6
  Maintainer-email: dfIndexeddb Developers <dfindexeddb-dev@googlegroups.com>
@@ -219,16 +219,19 @@ License-File: LICENSE
219
219
  License-File: AUTHORS
220
220
  Requires-Dist: python-snappy==0.6.1
221
221
  Requires-Dist: zstd==1.5.5.1
222
+ Provides-Extra: plugins
223
+ Requires-Dist: protobuf; extra == "plugins"
224
+ Requires-Dist: dfdatetime; extra == "plugins"
222
225
 
223
226
  # dfIndexeddb
224
227
 
225
228
  dfindexeddb is an experimental Python tool for performing digital forensic
226
- analysis of IndexedDB and leveldb files.
229
+ analysis of IndexedDB and LevelDB files.
227
230
 
228
- It parses leveldb, IndexedDB and javascript structures from these files without
231
+ It parses LevelDB, IndexedDB and JavaScript structures from these files without
229
232
  requiring native libraries. (Note: only a subset of IndexedDB key types and
230
- Javascript types for Chromium-based browsers are currently supported. Safari
231
- and Firefox are under development).
233
+ JavaScript types for Safari and Chromium-based browsers are currently supported.
234
+ Firefox is under development).
232
235
 
233
236
  The content of IndexedDB files is dependent on what a web application stores
234
237
  locally/offline using the web browser's
@@ -255,6 +258,12 @@ include:
255
258
  $ pip install dfindexeddb
256
259
  ```
257
260
 
261
+ To also install the dependencies for leveldb/indexeddb plugins, run
262
+ ```
263
+ $ pip install 'dfindexeddb[plugins]'
264
+ ```
265
+
266
+
258
267
  ## Installation from source
259
268
 
260
269
  1. [Linux] Install the snappy compression development package
@@ -273,9 +282,14 @@ include:
273
282
  $ pip install .
274
283
  ```
275
284
 
285
+ To also install the dependencies for leveldb/indexeddb plugins, run
286
+ ```
287
+ $ pip install '.[plugins]'
288
+ ```
289
+
276
290
  ## Usage
277
291
 
278
- Two CLI tools for parsing IndexedDB/leveldb files are available after
292
+ Two CLI tools for parsing IndexedDB/LevelDB files are available after
279
293
  installation:
280
294
 
281
295
 
@@ -297,49 +311,42 @@ options:
297
311
  -h, --help show this help message and exit
298
312
  ```
299
313
 
300
- To parse Indexeddb records from a LevelDB folder, use the following command:
314
+ #### Examples:
301
315
 
302
- ```
303
- dfindexeddb db -h
304
- usage: dfindexeddb db [-h] -s SOURCE [--use_manifest] [-o {json,jsonl,repr}]
316
+ To parse IndexedDB records from an sqlite file for Safari and output the
317
+ results as JSON-L, use the following command:
305
318
 
306
- options:
307
- -h, --help show this help message and exit
308
- -s SOURCE, --source SOURCE
309
- The source leveldb folder
310
- --use_manifest Use manifest file to determine active/recovered records.
311
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
312
- Output format. Default is json
319
+ ```
320
+ dfindexeddb db -s SOURCE --format safari -o jsonl
313
321
  ```
314
322
 
315
- To parse Indexeddb records from a LevelDB ldb (.ldb) file, use the following
316
- command:
323
+ To parse IndexedDB records from a LevelDB folder for Chrome/Chromium, using the
324
+ manifest file to determine recovered records and output as JSON, use the
325
+ following command:
317
326
 
318
327
  ```
319
- dfindexeddb ldb -h
320
- usage: dfindexeddb ldb [-h] -s SOURCE [-o {json,jsonl,repr}]
328
+ dfindexeddb db -s SOURCE --format chrome --use_manifest
329
+ ```
330
+
331
+ To parse IndexedDB records from a LevelDB ldb (.ldb) file and output the
332
+ results as JSON-L, use the following command:
321
333
 
322
- options:
323
- -h, --help show this help message and exit
324
- -s SOURCE, --source SOURCE
325
- The source .ldb file.
326
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
327
- Output format. Default is json
334
+ ```
335
+ dfindexeddb ldb -s SOURCE -o jsonl
328
336
  ```
329
337
 
330
- To parse Indexeddb records from a LevelDB log (.log) file, use the following
331
- command:
338
+ To parse IndexedDB records from a LevelDB log (.log) file and output the
339
+ results as the Python printable representation, use the following command:
332
340
 
333
341
  ```
334
- dfindexeddb log -h
335
- usage: dfindexeddb log [-h] -s SOURCE [-o {json,jsonl,repr}]
342
+ dfindexeddb log -s SOURCE -o repr
343
+ ```
344
+
345
+ To parse a file as a Chrome/Chromium IndexedDB blink value and output the
346
+ results as JSON:
336
347
 
337
- options:
338
- -h, --help show this help message and exit
339
- -s SOURCE, --source SOURCE
340
- The source .log file.
341
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
342
- Output format. Default is json
348
+ ```
349
+ dfindexeddb blink -s SOURCE
343
350
  ```
344
351
 
345
352
  ### LevelDB
@@ -361,64 +368,51 @@ options:
361
368
  -h, --help show this help message and exit
362
369
  ```
363
370
 
371
+ #### Examples
372
+
364
373
  To parse records from a LevelDB folder, use the following command:
365
374
 
366
375
  ```
367
- dfindexeddb db -h
368
- usage: dfindexeddb db [-h] -s SOURCE [--use_manifest] [-o {json,jsonl,repr}]
369
-
370
- options:
371
- -h, --help show this help message and exit
372
- -s SOURCE, --source SOURCE
373
- The source leveldb folder
374
- --use_manifest Use manifest file to determine active/recovered records.
375
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
376
- Output format. Default is json
376
+ dfleveldb db -s SOURCE
377
377
  ```
378
378
 
379
- To parse records from a LevelDB log (.log) file, use the following command:
379
+ To parse records from a LevelDB folder, and use the sequence number to
380
+ determine recovered records and output as JSON, use the
381
+ following command:
380
382
 
381
383
  ```
382
- $ dfleveldb log -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,write_batches,parsed_internal_key}]
383
-
384
- options:
385
- -h, --help show this help message and exit
386
- -s SOURCE, --source SOURCE
387
- The source leveldb file
388
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
389
- Output format. Default is json
390
- -t {blocks,physical_records,write_batches,parsed_internal_key}, --structure_type {blocks,physical_records,write_batches,parsed_internal_key}
391
- Parses the specified structure. Default is parsed_internal_key.
384
+ dfleveldb db -s SOURCE --use_sequence_number
392
385
  ```
393
386
 
394
- To parse records from a LevelDB table (.ldb) file, use the following command:
387
+ To parse blocks / physical records/ write batches / internal key records from a
388
+ LevelDB log (.log) file, use the following command, specifying the type (block,
389
+ physical_records, etc) via the `-t` option. By default, internal key records are parsed:
395
390
 
396
391
  ```
397
- $ dfleveldb ldb -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,records}]
392
+ $ dfleveldb log -s SOURCE [-t {blocks,physical_records,write_batches,parsed_internal_key}]
393
+ ```
398
394
 
399
- options:
400
- -h, --help show this help message and exit
401
- -s SOURCE, --source SOURCE
402
- The source leveldb file
403
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
404
- Output format. Default is json
405
- -t {blocks,records}, --structure_type {blocks,records}
406
- Parses the specified structure. Default is records.
395
+ To parse blocks / records from a LevelDB table (.ldb) file, use the following
396
+ command, specifying the type (blocks, records) via the `-t` option. By
397
+ default, records are parsed:
398
+
399
+ ```
400
+ $ dfleveldb ldb -s SOURCE [-t {blocks,records}]
407
401
  ```
408
402
 
409
- To parse version edit records from a Descriptor (MANIFEST) file:
403
+ To parse version edit records from a Descriptor (MANIFEST) file, use the
404
+ following command:
410
405
 
411
406
  ```
412
407
  $ dfleveldb descriptor -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,versionedit} | -v]
413
-
414
- options:
415
- -h, --help show this help message and exit
416
- -s SOURCE, --source SOURCE
417
- The source leveldb file
418
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
419
- Output format. Default is json
420
- -t {blocks,physical_records,versionedit}, --structure_type {blocks,physical_records,versionedit}
421
- Parses the specified structure. Default is versionedit.
422
- -v, --version_history
423
- Parses the leveldb version history.
424
408
  ```
409
+
410
+ #### Plugins
411
+
412
+ To apply a plugin parser for a leveldb file/folder, add the
413
+ `--plugin [Plugin Name]` argument. Currently, there is support for the
414
+ following artifacts:
415
+
416
+ | Plugin Name | Artifact Name |
417
+ | -------- | ------- |
418
+ | `ChromeNotificationRecord` | Chrome/Chromium Notifications |
@@ -0,0 +1,193 @@
1
+ # dfIndexeddb
2
+
3
+ dfindexeddb is an experimental Python tool for performing digital forensic
4
+ analysis of IndexedDB and LevelDB files.
5
+
6
+ It parses LevelDB, IndexedDB and JavaScript structures from these files without
7
+ requiring native libraries. (Note: only a subset of IndexedDB key types and
8
+ JavaScript types for Safari and Chromium-based browsers are currently supported.
9
+ Firefox is under development).
10
+
11
+ The content of IndexedDB files is dependent on what a web application stores
12
+ locally/offline using the web browser's
13
+ [IndexedDB API](https://www.w3.org/TR/IndexedDB/). Examples of content might
14
+ include:
15
+ * text from a text/source-code editor application,
16
+ * emails and contact information from an e-mail application,
17
+ * images and metadata from a photo gallery application
18
+
19
+
20
+ ## Installation
21
+
22
+ 1. [Linux] Install the snappy compression development package
23
+
24
+ ```
25
+ $ sudo apt install libsnappy-dev
26
+ ```
27
+
28
+ 2. Create a virtual environment and install the package
29
+
30
+ ```
31
+ $ python3 -m venv .venv
32
+ $ source .venv/bin/activate
33
+ $ pip install dfindexeddb
34
+ ```
35
+
36
+ To also install the dependencies for leveldb/indexeddb plugins, run
37
+ ```
38
+ $ pip install 'dfindexeddb[plugins]'
39
+ ```
40
+
41
+
42
+ ## Installation from source
43
+
44
+ 1. [Linux] Install the snappy compression development package
45
+
46
+ ```
47
+ $ sudo apt install libsnappy-dev
48
+ ```
49
+
50
+ 2. Clone or download/unzip the repository to your local machine.
51
+
52
+ 3. Create a virtual environment and install the package
53
+
54
+ ```
55
+ $ python3 -m venv .venv
56
+ $ source .venv/bin/activate
57
+ $ pip install .
58
+ ```
59
+
60
+ To also install the dependencies for leveldb/indexeddb plugins, run
61
+ ```
62
+ $ pip install '.[plugins]'
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ Two CLI tools for parsing IndexedDB/LevelDB files are available after
68
+ installation:
69
+
70
+
71
+ ### IndexedDB
72
+
73
+ ```
74
+ $ dfindexeddb -h
75
+ usage: dfindexeddb [-h] {db,ldb,log} ...
76
+
77
+ A cli tool for parsing indexeddb files
78
+
79
+ positional arguments:
80
+ {db,ldb,log}
81
+ db Parse a directory as indexeddb.
82
+ ldb Parse a ldb file as indexeddb.
83
+ log Parse a log file as indexeddb.
84
+
85
+ options:
86
+ -h, --help show this help message and exit
87
+ ```
88
+
89
+ #### Examples:
90
+
91
+ To parse IndexedDB records from an sqlite file for Safari and output the
92
+ results as JSON-L, use the following command:
93
+
94
+ ```
95
+ dfindexeddb db -s SOURCE --format safari -o jsonl
96
+ ```
97
+
98
+ To parse IndexedDB records from a LevelDB folder for Chrome/Chromium, using the
99
+ manifest file to determine recovered records and output as JSON, use the
100
+ following command:
101
+
102
+ ```
103
+ dfindexeddb db -s SOURCE --format chrome --use_manifest
104
+ ```
105
+
106
+ To parse IndexedDB records from a LevelDB ldb (.ldb) file and output the
107
+ results as JSON-L, use the following command:
108
+
109
+ ```
110
+ dfindexeddb ldb -s SOURCE -o jsonl
111
+ ```
112
+
113
+ To parse IndexedDB records from a LevelDB log (.log) file and output the
114
+ results as the Python printable representation, use the following command:
115
+
116
+ ```
117
+ dfindexeddb log -s SOURCE -o repr
118
+ ```
119
+
120
+ To parse a file as a Chrome/Chromium IndexedDB blink value and output the
121
+ results as JSON:
122
+
123
+ ```
124
+ dfindexeddb blink -s SOURCE
125
+ ```
126
+
127
+ ### LevelDB
128
+
129
+ ```
130
+ $ dfleveldb -h
131
+ usage: dfleveldb [-h] {db,log,ldb,descriptor} ...
132
+
133
+ A cli tool for parsing leveldb files
134
+
135
+ positional arguments:
136
+ {db,log,ldb,descriptor}
137
+ db Parse a directory as leveldb.
138
+ log Parse a leveldb log file.
139
+ ldb Parse a leveldb table (.ldb) file.
140
+ descriptor Parse a leveldb descriptor (MANIFEST) file.
141
+
142
+ options:
143
+ -h, --help show this help message and exit
144
+ ```
145
+
146
+ #### Examples
147
+
148
+ To parse records from a LevelDB folder, use the following command:
149
+
150
+ ```
151
+ dfleveldb db -s SOURCE
152
+ ```
153
+
154
+ To parse records from a LevelDB folder, and use the sequence number to
155
+ determine recovered records and output as JSON, use the
156
+ following command:
157
+
158
+ ```
159
+ dfleveldb db -s SOURCE --use_sequence_number
160
+ ```
161
+
162
+ To parse blocks / physical records/ write batches / internal key records from a
163
+ LevelDB log (.log) file, use the following command, specifying the type (block,
164
+ physical_records, etc) via the `-t` option. By default, internal key records are parsed:
165
+
166
+ ```
167
+ $ dfleveldb log -s SOURCE [-t {blocks,physical_records,write_batches,parsed_internal_key}]
168
+ ```
169
+
170
+ To parse blocks / records from a LevelDB table (.ldb) file, use the following
171
+ command, specifying the type (blocks, records) via the `-t` option. By
172
+ default, records are parsed:
173
+
174
+ ```
175
+ $ dfleveldb ldb -s SOURCE [-t {blocks,records}]
176
+ ```
177
+
178
+ To parse version edit records from a Descriptor (MANIFEST) file, use the
179
+ following command:
180
+
181
+ ```
182
+ $ dfleveldb descriptor -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,versionedit} | -v]
183
+ ```
184
+
185
+ #### Plugins
186
+
187
+ To apply a plugin parser for a leveldb file/folder, add the
188
+ `--plugin [Plugin Name]` argument. Currently, there is support for the
189
+ following artifacts:
190
+
191
+ | Plugin Name | Artifact Name |
192
+ | -------- | ------- |
193
+ | `ChromeNotificationRecord` | Chrome/Chromium Notifications |
@@ -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)
@@ -17,7 +17,11 @@ from __future__ import annotations
17
17
  from dataclasses import dataclass, field
18
18
  from datetime import datetime
19
19
  import io
20
- from typing import Any, BinaryIO, Optional, Tuple, Type, TypeVar, Union
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
@@ -456,7 +460,7 @@ class MaxDatabaseIdKey(BaseIndexedDBKey):
456
460
  cls, decoder: utils.LevelDBDecoder, key_prefix: KeyPrefix,
457
461
  base_offset: int = 0
458
462
  ) -> MaxDatabaseIdKey:
459
- """Decodes the maximum databse key."""
463
+ """Decodes the maximum database key."""
460
464
  offset, key_type = decoder.DecodeUint8()
461
465
  if key_type != definitions.GlobalMetadataKeyType.MAX_DATABASE_ID:
462
466
  raise errors.ParserError('Not a MaxDatabaseIdKey')
@@ -1271,14 +1275,17 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1271
1275
  filename = None
1272
1276
  last_modified = None
1273
1277
  token = None
1274
- elif (object_type ==
1275
- 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
1276
1284
  blob_number = None
1277
1285
  mime_type = None
1278
1286
  size = None
1279
1287
  filename = None
1280
1288
  last_modified = None
1281
- _, token = decoder.DecodeBlobWithLength()
1282
1289
 
1283
1290
  return cls(offset=base_offset + offset, object_type=object_type,
1284
1291
  blob_number=blob_number, mime_type=mime_type, size=size,
@@ -1331,12 +1338,14 @@ class IndexedDBRecord:
1331
1338
  """An IndexedDB Record.
1332
1339
 
1333
1340
  Attributes:
1341
+ path: the source file path
1334
1342
  offset: the offset of the record.
1335
1343
  key: the key of the record.
1336
1344
  value: the value of the record.
1337
1345
  sequence_number: if available, the sequence number of the record.
1338
1346
  type: the type of the record.
1339
- level: the leveldb level, None indicates the record came from a log file.
1347
+ level: the leveldb level, if applicable, None can indicate the record
1348
+ originated from a log file or the level could not be determined.
1340
1349
  recovered: True if the record is a recovered record.
1341
1350
  """
1342
1351
  path: str
@@ -1350,7 +1359,8 @@ class IndexedDBRecord:
1350
1359
 
1351
1360
  @classmethod
1352
1361
  def FromLevelDBRecord(
1353
- cls, db_record: record.LevelDBRecord
1362
+ cls,
1363
+ db_record: record.LevelDBRecord
1354
1364
  ) -> IndexedDBRecord:
1355
1365
  """Returns an IndexedDBRecord from a ParsedInternalKey."""
1356
1366
  idb_key = IndexedDbKey.FromBytes(
@@ -1366,3 +1376,76 @@ class IndexedDBRecord:
1366
1376
  type=db_record.record.record_type,
1367
1377
  level=db_record.level,
1368
1378
  recovered=db_record.recovered)
1379
+
1380
+ @classmethod
1381
+ def FromFile(
1382
+ cls,
1383
+ file_path: pathlib.Path
1384
+ ) -> Generator[IndexedDBRecord, None, None]:
1385
+ """Yields IndexedDBRecords from a file."""
1386
+ for db_record in record.LevelDBRecord.FromFile(file_path):
1387
+ try:
1388
+ yield cls.FromLevelDBRecord(db_record)
1389
+ except(
1390
+ errors.ParserError,
1391
+ errors.DecoderError,
1392
+ NotImplementedError) as err:
1393
+ print((
1394
+ 'Error parsing Indexeddb record: '
1395
+ f'{err} at offset {db_record.record.offset} in '
1396
+ f'{db_record.path}'),
1397
+ file=sys.stderr)
1398
+ print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)
1399
+
1400
+
1401
+ class FolderReader:
1402
+ """A IndexedDB folder reader for Chrome/Chromium.
1403
+
1404
+ Attributes:
1405
+ foldername (str): the source LevelDB folder.
1406
+ """
1407
+
1408
+ def __init__(self, foldername: pathlib.Path):
1409
+ """Initializes the FileReader.
1410
+
1411
+ Args:
1412
+ foldername: the source IndexedDB folder.
1413
+
1414
+ Raises:
1415
+ ValueError: if foldername is None or not a directory.
1416
+ """
1417
+ if not foldername or not foldername.is_dir():
1418
+ raise ValueError(f'{foldername} is None or not a directory')
1419
+ self.foldername = foldername
1420
+
1421
+ def GetRecords(
1422
+ self,
1423
+ use_manifest: bool = False,
1424
+ use_sequence_number: bool = False
1425
+ ) -> Generator[IndexedDBRecord, None, None]:
1426
+ """Yield LevelDBRecords.
1427
+
1428
+ Args:
1429
+ use_manifest: True to use the current manifest in the folder as a means to
1430
+ find the active file set.
1431
+ use_sequence_number: True to use the sequence number to determine the
1432
+ Yields:
1433
+ IndexedDBRecord.
1434
+ """
1435
+ leveldb_folder_reader = record.FolderReader(self.foldername)
1436
+ for leveldb_record in leveldb_folder_reader.GetRecords(
1437
+ use_manifest=use_manifest,
1438
+ use_sequence_number=use_sequence_number):
1439
+ try:
1440
+ yield IndexedDBRecord.FromLevelDBRecord(
1441
+ leveldb_record)
1442
+ except(
1443
+ errors.ParserError,
1444
+ errors.DecoderError,
1445
+ NotImplementedError) as err:
1446
+ print((
1447
+ 'Error parsing Indexeddb record: '
1448
+ f'{err} at offset {leveldb_record.record.offset} in '
1449
+ f'{leveldb_record.path}'),
1450
+ file=sys.stderr)
1451
+ print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)