Bug 1858696: Created IDB and CacheAPI marionette tests to verify raw magic strings in on-disk storages.r=dom-storage-reviewers,webdriver-reviewers,whimboo,janv

Differential Revision: https://phabricator.services.mozilla.com/D192328
This commit is contained in:
hsingh 2024-02-02 15:14:24 +00:00
parent 1eb7ddbd5d
commit 2cf947cbda
5 changed files with 134 additions and 68 deletions

View file

@ -26,8 +26,8 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
def setUp(self):
super(CacheAPIEncryptionPBM, self).setUp()
self.testHTML = "dom/cache/basicCacheAPI_PBM.html"
self.cacheName = "CachePBMTest"
self.profilePath = self.marionette.instance.profile.profile
self.cacheAPIStoragePath = None
@ -45,27 +45,33 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
self.dbCheckpointThresholdBytes = 512 * 1024
# Navigate by opening a new private window
pbmWindowHandle = self.marionette.open(type="window", private=True)["handle"]
self.marionette.switch_to_window(pbmWindowHandle)
self.marionette.navigate(
self.marionette.absolute_url("dom/cache/basicCacheAPI_PBM.html")
)
self.origin = self.marionette.absolute_url("")[:-1] + "^privateBrowsingId=1"
def tearDown(self):
super(CacheAPIEncryptionPBM, self).setUp()
self.marionette.set_pref(CACHEAPI_PBM_PREF, self.defaultCacheAPIPBMPref)
self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
self.marionette.execute_script("window.wrappedJSObject.releaseCache()")
def test_request_response_ondisk(self):
with self.using_new_window(self.testHTML, private=False) as (
self.origin,
self.persistenceType,
):
self.runAndValidate(
lambda exists: self.assertTrue(
exists, "Failed to find expected data on disk"
)
)
# closes the new private window we opened in the setUp and referred by 'pbmWindowHandle'
self.marionette.close()
def test_encrypted_request_response_ondisk(self):
with self.using_new_window(self.testHTML, private=True) as (
self.origin,
self.persistenceType,
):
self.runAndValidate(
lambda exists: self.assertFalse(exists, "Data on disk is not encrypted")
)
def test_ensure_encrypted_request_response(self):
def runAndValidate(self, validator):
self.marionette.execute_async_script(
"""
const [name, requestStr, responseStr, resolve] = arguments;
@ -73,24 +79,32 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
const request = new Request(requestStr);
const response = new Response(responseStr);
window.wrappedJSObject.addDataIntoCache(name, request, response)
.then(()=> { resolve(); });
.then(resolve);
""",
script_args=(self.cacheName, self.cacheRequestStr, self.cacheResponseStr),
script_args=(
self.cacheName,
self.cacheRequestStr,
self.cacheResponseStr,
),
)
self.ensureInvariantHolds(
lambda _: os.path.exists(self.getCacheAPIStoragePath())
)
self.ensureSqliteIsEncrypted()
self.validateSqlite(validator)
self.validateBodyFile(validator)
def validateBodyFile(self, validator):
# Ensure response bodies have been flushed to the disk
self.ensureInvariantHolds(
lambda _: self.findDirObj(self.cacheAPIStoragePath, "morgue", False)
lambda _: self.findDirObj(self.getCacheAPIStoragePath(), "morgue", False)
is not None
)
cacheResponseDir = self.findDirObj(self.cacheAPIStoragePath, "morgue", False)
cacheResponseDir = self.findDirObj(
self.getCacheAPIStoragePath(), "morgue", False
)
self.ensureInvariantHolds(lambda _: any(os.listdir(cacheResponseDir)))
@ -114,26 +128,28 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
with open(cacheResponseBodyPath, "rb") as f_binary:
foundRawValue = re.search(b"sNaPpY", f_binary.read()) is not None
self.assertFalse(foundRawValue, "Response body did not get encrypted")
validator(foundRawValue)
def ensureSqliteIsEncrypted(self):
def validateSqlite(self, validator):
self.ensureInvariantHolds(
lambda _: self.findDirObj(
self.cacheAPIStoragePath, self.cacheDBJournalFileName, True
self.getCacheAPIStoragePath(), self.cacheDBJournalFileName, True
)
is not None
)
dbJournalFile = self.findDirObj(
self.cacheAPIStoragePath, self.cacheDBJournalFileName, True
self.getCacheAPIStoragePath(), self.cacheDBJournalFileName, True
)
self.ensureInvariantHolds(
lambda _: self.findDirObj(
self.cacheAPIStoragePath, self.cacheDBFileName, True
self.getCacheAPIStoragePath(), self.cacheDBFileName, True
)
is not None
)
dbFile = self.findDirObj(self.cacheAPIStoragePath, self.cacheDBFileName, True)
dbFile = self.findDirObj(
self.getCacheAPIStoragePath(), self.cacheDBFileName, True
)
# Confirm journal file size is less than 512KB which ensures that checkpoint
# has not happend yet (dom/cache/DBSchema.cpp::InitializeConnection, kWalAutoCheckpointPages)
@ -144,26 +160,27 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
# Before checkpointing, journal file size should be greater than main sqlite db file.
self.assertTrue(os.path.getsize(dbJournalFile) > os.path.getsize(dbFile))
self.assertFalse(
self.cacheRequestStr.encode("ascii") in open(dbJournalFile, "rb").read(),
"Sqlite journal file did not get encrypted",
validator(
self.cacheRequestStr.encode("ascii") in open(dbJournalFile, "rb").read()
)
self.assertTrue(self.resetStoragesForPrincipal(self.origin, "private", "cache"))
self.assertTrue(
self.resetStoragesForPrincipal(self.origin, self.persistenceType, "cache")
)
self.assertFalse(os.path.getsize(dbJournalFile) > os.path.getsize(dbFile))
self.assertFalse(
self.cacheRequestStr.encode("ascii") in open(dbFile, "rb").read(),
"Sqlite main db file did not get encrypted",
)
validator(self.cacheRequestStr.encode("ascii") in open(dbFile, "rb").read())
def getCacheAPIStoragePath(self):
if self.cacheAPIStoragePath is not None:
return self.cacheAPIStoragePath
assert self.origin is not None
assert self.persistenceType is not None
self.cacheAPIStoragePath = self.getStoragePath(
self.profilePath, self.origin, "private", "cache"
self.profilePath, self.origin, self.persistenceType, "cache"
)
print("cacheAPI origin directory = " + self.cacheAPIStoragePath)

View file

@ -26,6 +26,7 @@ class IDBEncryptionPBM(QuotaTestCase):
def setUp(self):
super(IDBEncryptionPBM, self).setUp()
self.testHTML = "dom/indexedDB/basicIDB_PBM.html"
self.IDBName = "IDBTest"
self.IDBStoreName = "IDBTestStore"
self.IDBVersion = 1
@ -40,41 +41,61 @@ class IDBEncryptionPBM(QuotaTestCase):
self.defaultQMPrefValue = self.marionette.get_pref(QM_TESTING_PREF)
self.marionette.set_pref(QM_TESTING_PREF, True)
# Navigate by opening a new private window
pbmWindowHandle = self.marionette.open(type="window", private=True)["handle"]
self.marionette.switch_to_window(pbmWindowHandle)
self.marionette.navigate(
self.marionette.absolute_url("dom/indexedDB/basicIDB_PBM.html")
)
self.origin = self.marionette.absolute_url("")[:-1] + "^privateBrowsingId=1"
def tearDown(self):
super(IDBEncryptionPBM, self).setUp()
super(IDBEncryptionPBM, self).tearDown()
self.marionette.set_pref(INDEXED_DB_PBM_PREF, self.defaultIDBPrefValue)
self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
# closes the new private window we opened in the setUp and referred by 'pbmWindowHandle'
self.marionette.close()
def test_raw_IDB_data_ondisk(self):
with self.using_new_window(self.testHTML, private=False) as (
self.origin,
self.persistenceType,
):
self.runAndValidate(
lambda exists: self.assertTrue(
exists, "Failed to find expected data on disk"
)
)
def test_ensure_encrypted_blob(self):
self.marionette.execute_script(
def test_ensure_encrypted_IDB_data_ondisk(self):
with self.using_new_window(self.testHTML, private=True) as (
self.origin,
self.persistenceType,
):
self.runAndValidate(
lambda exists: self.assertFalse(exists, "Data on disk is not encrypted")
)
def runAndValidate(self, validator):
self.marionette.execute_async_script(
"""
const [idb, store, key, value] = arguments;
const [idb, store, key, value, resolve] = arguments;
window.wrappedJSObject.addDataIntoIDB(idb, store, key, value).then(resolve);
""",
script_args=(self.IDBName, self.IDBStoreName, "textKey", self.IDBValue),
)
self.validateSqlite(validator)
self.marionette.execute_async_script(
"""
const [idb, store, key, value, resolve] = arguments;
const blobValue = new Blob([value], {type:'text/plain'});
window.wrappedJSObject.addDataIntoIDB(idb, store, key, blobValue);
window.wrappedJSObject.addDataIntoIDB(idb, store, key, blobValue).then(resolve);
""",
script_args=(self.IDBName, self.IDBStoreName, "blobKey", self.IDBValue),
)
self.validateBlob(validator)
def validateBlob(self, validator):
self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
self.ensureInvariantHolds(
lambda _: self.findDirObj(self.idbStoragePath, ".files", False) is not None
lambda _: self.findDirObj(self.getIDBStoragePath(), ".files", False)
is not None
)
idbBlobDir = self.findDirObj(self.idbStoragePath, ".files", False)
idbBlobDir = self.findDirObj(self.getIDBStoragePath(), ".files", False)
# seems like there's a timing issue here. There are sometimes no blob file
# even after WAL is released. Allowing some buffer time and ensuring blob file
@ -88,23 +109,16 @@ class IDBEncryptionPBM(QuotaTestCase):
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
)
self.assertFalse(foundRawValue, "Blob file did not get encrypted")
def test_ensure_encrpted_sqlite_data(self):
self.marionette.execute_script(
"""
const [idb, store, key, value] = arguments;
window.wrappedJSObject.addDataIntoIDB(idb, store, key, value);
""",
script_args=(self.IDBName, self.IDBStoreName, "textKey", self.IDBValue),
)
validator(foundRawValue)
def validateSqlite(self, validator):
self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
self.ensureInvariantHolds(
lambda _: self.findDirObj(self.idbStoragePath, ".sqlite", True) is not None
lambda _: self.findDirObj(self.getIDBStoragePath(), ".sqlite", True)
is not None
)
sqliteDBFile = self.findDirObj(self.idbStoragePath, ".sqlite", True)
sqliteDBFile = self.findDirObj(self.getIDBStoragePath(), ".sqlite", True)
foundRawValue = False
with open(sqliteDBFile, "rb") as f_binary:
@ -112,14 +126,17 @@ class IDBEncryptionPBM(QuotaTestCase):
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
)
self.assertFalse(foundRawValue, "sqlite data did not get encrypted")
validator(foundRawValue)
def getIDBStoragePath(self):
if self.idbStoragePath is not None:
return self.idbStoragePath
assert self.origin is not None
assert self.persistenceType is not None
self.idbStoragePath = self.getStoragePath(
self.profilePath, self.origin, "private", "idb"
self.profilePath, self.origin, self.persistenceType, "idb"
)
print("idb origin directory = " + self.idbStoragePath)

View file

@ -3,6 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from contextlib import contextmanager
from marionette_driver import Wait
from marionette_harness import MarionetteTestCase
@ -88,3 +89,25 @@ class QuotaTestCase(MarionetteTestCase):
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
return self.marionette.execute_async_script(script)
@contextmanager
def using_new_window(self, path, private=False):
"""
This helper method is created to ensure that a temporary
window required inside the test scope is lifetime'd properly
"""
oldWindow = self.marionette.current_window_handle
try:
newWindow = self.marionette.open(type="window", private=private)
self.marionette.switch_to_window(newWindow["handle"])
self.marionette.navigate(self.marionette.absolute_url(path))
origin = self.marionette.absolute_url("")[:-1]
if private:
origin += "^privateBrowsingId=1"
yield (origin, "private" if private else "default")
finally:
self.marionette.close()
self.marionette.switch_to_window(oldWindow)

View file

@ -14,7 +14,7 @@
async function addDataIntoCache(name, request, response) {
let cache = await ensureCache(name);
await cache.put(request, response);
return cache.put(request, response);
};
</script>
</head>

View file

@ -26,7 +26,8 @@
await (new Promise((resolve, reject) => {
var transaction = db.transaction([store], "readwrite");
var put = transaction.objectStore(store).put(value, key);
put.onsuccess = resolve();
put.onerror = reject;
put.onsuccess = resolve;
}));
closeIDB(db)
@ -35,6 +36,14 @@
function closeIDB(db) {
db.close();
}
function deleteIDB(db) {
return new Promise((resolve, reject) => {
let deleteReq = indexedDB.deleteDatabase(db);
deleteReq.onerror = reject;
deleteReq.onsuccess = resolve;
});
}
</script>
</head>
</html>