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