tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_IDB_encryption_PBM.py (5477B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 import os
      6 import re
      7 import sys
      8 from pathlib import Path
      9 
     10 sys.path.append(os.fspath(Path(__file__).parents[3] / "quota/test/marionette"))
     11 
     12 from quota_test_case import QuotaTestCase
     13 
     14 INDEXED_DB_PBM_PREF = "dom.indexedDB.privateBrowsing.enabled"
     15 QM_TESTING_PREF = "dom.quotaManager.testing"
     16 
     17 
     18 class IDBEncryptionPBM(QuotaTestCase):
     19    """
     20    Bug1784966: Ensure IDB data gets encrypted in Private Browsing Mode.
     21    We need to ensure data inside both sqlite fields and blob files under
     22    *.sqllite gets encrypted.
     23    """
     24 
     25    def setUp(self):
     26        super().setUp()
     27 
     28        self.testHTML = "dom/indexedDB/basicIDB_PBM.html"
     29        self.IDBName = "IDBTest"
     30        self.IDBStoreName = "IDBTestStore"
     31        self.IDBVersion = 1
     32        self.IDBValue = "test_IDB_Encryption_PBM"
     33        self.idbStoragePath = None
     34 
     35        self.profilePath = self.marionette.instance.profile.profile
     36 
     37        self.defaultIDBPrefValue = self.marionette.get_pref(INDEXED_DB_PBM_PREF)
     38        self.marionette.set_pref(INDEXED_DB_PBM_PREF, True)
     39 
     40        self.defaultQMPrefValue = self.marionette.get_pref(QM_TESTING_PREF)
     41        self.marionette.set_pref(QM_TESTING_PREF, True)
     42 
     43    def tearDown(self):
     44        super().tearDown()
     45 
     46        self.marionette.set_pref(INDEXED_DB_PBM_PREF, self.defaultIDBPrefValue)
     47        self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
     48 
     49    def test_raw_IDB_data_ondisk(self):
     50        with self.using_new_window(self.testHTML, private=False) as (
     51            self.origin,
     52            self.persistenceType,
     53        ):
     54            self.runAndValidate(
     55                lambda exists: self.assertTrue(
     56                    exists, "Failed to find expected data on disk"
     57                )
     58            )
     59 
     60    def test_ensure_encrypted_IDB_data_ondisk(self):
     61        with self.using_new_window(self.testHTML, private=True) as (
     62            self.origin,
     63            self.persistenceType,
     64        ):
     65            self.runAndValidate(
     66                lambda exists: self.assertFalse(exists, "Data on disk is not encrypted")
     67            )
     68 
     69    def runAndValidate(self, validator):
     70        self.marionette.execute_async_script(
     71            """
     72                const [idb, store, key, value, resolve] = arguments;
     73                window.wrappedJSObject.addDataIntoIDB(idb, store, key, value).then(resolve);
     74            """,
     75            script_args=(self.IDBName, self.IDBStoreName, "textKey", self.IDBValue),
     76        )
     77        self.validateSqlite(validator)
     78 
     79        self.marionette.execute_async_script(
     80            """
     81                const [idb, store, key, value, resolve] = arguments;
     82                const blobValue = new Blob([value], {type:'text/plain'});
     83 
     84                window.wrappedJSObject.addDataIntoIDB(idb, store, key, blobValue).then(resolve);
     85            """,
     86            script_args=(self.IDBName, self.IDBStoreName, "blobKey", self.IDBValue),
     87        )
     88        self.validateBlob(validator)
     89 
     90    def validateBlob(self, validator):
     91        self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
     92        self.ensureInvariantHolds(
     93            lambda _: self.findDirObj(self.getIDBStoragePath(), ".files", False)
     94            is not None
     95        )
     96 
     97        idbBlobDir = self.findDirObj(self.getIDBStoragePath(), ".files", False)
     98 
     99        # seems like there's a timing issue here. There are sometimes no blob file
    100        # even after WAL is released. Allowing some buffer time and ensuring blob file
    101        # exists before validating it's contents
    102        idbBlobPath = os.path.join(idbBlobDir, "1")
    103        self.ensureInvariantHolds(lambda _: os.path.exists(idbBlobPath))
    104 
    105        foundRawValue = False
    106        with open(idbBlobPath, "rb") as f_binary:
    107            foundRawValue = (
    108                re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
    109            )
    110 
    111        validator(foundRawValue)
    112 
    113    def validateSqlite(self, validator):
    114        self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
    115        self.ensureInvariantHolds(
    116            lambda _: self.findDirObj(self.getIDBStoragePath(), ".sqlite", True)
    117            is not None
    118        )
    119 
    120        sqliteDBFile = self.findDirObj(self.getIDBStoragePath(), ".sqlite", True)
    121 
    122        foundRawValue = False
    123        with open(sqliteDBFile, "rb") as f_binary:
    124            foundRawValue = (
    125                re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
    126            )
    127 
    128        validator(foundRawValue)
    129 
    130    def getIDBStoragePath(self):
    131        if self.idbStoragePath is not None:
    132            return self.idbStoragePath
    133 
    134        assert self.origin is not None
    135        assert self.persistenceType is not None
    136 
    137        self.idbStoragePath = self.getStoragePath(
    138            self.profilePath, self.origin, self.persistenceType, "idb"
    139        )
    140 
    141        print("idb origin directory = " + self.idbStoragePath)
    142        return self.idbStoragePath
    143 
    144    def sqliteWALReleased(self):
    145        """
    146        checks if .sqlite-wal has been cleared or not.
    147        returns False if idbStoragePath does not exist
    148        """
    149        if not os.path.exists(self.getIDBStoragePath()):
    150            return False
    151        walPath = self.findDirObj(self.idbStoragePath, ".sqlite-wal", True)
    152        return walPath is None or os.stat(walPath).st_size == 0