forked from mirrors/gecko-dev
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:
parent
1eb7ddbd5d
commit
2cf947cbda
5 changed files with 134 additions and 68 deletions
|
|
@ -26,8 +26,8 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CacheAPIEncryptionPBM, self).setUp()
|
super(CacheAPIEncryptionPBM, self).setUp()
|
||||||
|
|
||||||
|
self.testHTML = "dom/cache/basicCacheAPI_PBM.html"
|
||||||
self.cacheName = "CachePBMTest"
|
self.cacheName = "CachePBMTest"
|
||||||
|
|
||||||
self.profilePath = self.marionette.instance.profile.profile
|
self.profilePath = self.marionette.instance.profile.profile
|
||||||
self.cacheAPIStoragePath = None
|
self.cacheAPIStoragePath = None
|
||||||
|
|
||||||
|
|
@ -45,27 +45,33 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
|
||||||
|
|
||||||
self.dbCheckpointThresholdBytes = 512 * 1024
|
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):
|
def tearDown(self):
|
||||||
super(CacheAPIEncryptionPBM, self).setUp()
|
super(CacheAPIEncryptionPBM, self).setUp()
|
||||||
|
|
||||||
self.marionette.set_pref(CACHEAPI_PBM_PREF, self.defaultCacheAPIPBMPref)
|
self.marionette.set_pref(CACHEAPI_PBM_PREF, self.defaultCacheAPIPBMPref)
|
||||||
self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
|
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'
|
def test_encrypted_request_response_ondisk(self):
|
||||||
self.marionette.close()
|
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(
|
self.marionette.execute_async_script(
|
||||||
"""
|
"""
|
||||||
const [name, requestStr, responseStr, resolve] = arguments;
|
const [name, requestStr, responseStr, resolve] = arguments;
|
||||||
|
|
@ -73,24 +79,32 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
|
||||||
const request = new Request(requestStr);
|
const request = new Request(requestStr);
|
||||||
const response = new Response(responseStr);
|
const response = new Response(responseStr);
|
||||||
window.wrappedJSObject.addDataIntoCache(name, request, response)
|
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(
|
self.ensureInvariantHolds(
|
||||||
lambda _: os.path.exists(self.getCacheAPIStoragePath())
|
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
|
# Ensure response bodies have been flushed to the disk
|
||||||
self.ensureInvariantHolds(
|
self.ensureInvariantHolds(
|
||||||
lambda _: self.findDirObj(self.cacheAPIStoragePath, "morgue", False)
|
lambda _: self.findDirObj(self.getCacheAPIStoragePath(), "morgue", False)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
cacheResponseDir = self.findDirObj(self.cacheAPIStoragePath, "morgue", False)
|
cacheResponseDir = self.findDirObj(
|
||||||
|
self.getCacheAPIStoragePath(), "morgue", False
|
||||||
|
)
|
||||||
|
|
||||||
self.ensureInvariantHolds(lambda _: any(os.listdir(cacheResponseDir)))
|
self.ensureInvariantHolds(lambda _: any(os.listdir(cacheResponseDir)))
|
||||||
|
|
||||||
|
|
@ -114,26 +128,28 @@ class CacheAPIEncryptionPBM(QuotaTestCase):
|
||||||
with open(cacheResponseBodyPath, "rb") as f_binary:
|
with open(cacheResponseBodyPath, "rb") as f_binary:
|
||||||
foundRawValue = re.search(b"sNaPpY", f_binary.read()) is not None
|
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(
|
self.ensureInvariantHolds(
|
||||||
lambda _: self.findDirObj(
|
lambda _: self.findDirObj(
|
||||||
self.cacheAPIStoragePath, self.cacheDBJournalFileName, True
|
self.getCacheAPIStoragePath(), self.cacheDBJournalFileName, True
|
||||||
)
|
)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
dbJournalFile = self.findDirObj(
|
dbJournalFile = self.findDirObj(
|
||||||
self.cacheAPIStoragePath, self.cacheDBJournalFileName, True
|
self.getCacheAPIStoragePath(), self.cacheDBJournalFileName, True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.ensureInvariantHolds(
|
self.ensureInvariantHolds(
|
||||||
lambda _: self.findDirObj(
|
lambda _: self.findDirObj(
|
||||||
self.cacheAPIStoragePath, self.cacheDBFileName, True
|
self.getCacheAPIStoragePath(), self.cacheDBFileName, True
|
||||||
)
|
)
|
||||||
is not None
|
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
|
# Confirm journal file size is less than 512KB which ensures that checkpoint
|
||||||
# has not happend yet (dom/cache/DBSchema.cpp::InitializeConnection, kWalAutoCheckpointPages)
|
# 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.
|
# Before checkpointing, journal file size should be greater than main sqlite db file.
|
||||||
self.assertTrue(os.path.getsize(dbJournalFile) > os.path.getsize(dbFile))
|
self.assertTrue(os.path.getsize(dbJournalFile) > os.path.getsize(dbFile))
|
||||||
|
|
||||||
self.assertFalse(
|
validator(
|
||||||
self.cacheRequestStr.encode("ascii") in open(dbJournalFile, "rb").read(),
|
self.cacheRequestStr.encode("ascii") in open(dbJournalFile, "rb").read()
|
||||||
"Sqlite journal file did not get encrypted",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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(os.path.getsize(dbJournalFile) > os.path.getsize(dbFile))
|
||||||
|
|
||||||
self.assertFalse(
|
validator(self.cacheRequestStr.encode("ascii") in open(dbFile, "rb").read())
|
||||||
self.cacheRequestStr.encode("ascii") in open(dbFile, "rb").read(),
|
|
||||||
"Sqlite main db file did not get encrypted",
|
|
||||||
)
|
|
||||||
|
|
||||||
def getCacheAPIStoragePath(self):
|
def getCacheAPIStoragePath(self):
|
||||||
if self.cacheAPIStoragePath is not None:
|
if self.cacheAPIStoragePath is not None:
|
||||||
return self.cacheAPIStoragePath
|
return self.cacheAPIStoragePath
|
||||||
|
|
||||||
|
assert self.origin is not None
|
||||||
|
assert self.persistenceType is not None
|
||||||
|
|
||||||
self.cacheAPIStoragePath = self.getStoragePath(
|
self.cacheAPIStoragePath = self.getStoragePath(
|
||||||
self.profilePath, self.origin, "private", "cache"
|
self.profilePath, self.origin, self.persistenceType, "cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
print("cacheAPI origin directory = " + self.cacheAPIStoragePath)
|
print("cacheAPI origin directory = " + self.cacheAPIStoragePath)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class IDBEncryptionPBM(QuotaTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(IDBEncryptionPBM, self).setUp()
|
super(IDBEncryptionPBM, self).setUp()
|
||||||
|
|
||||||
|
self.testHTML = "dom/indexedDB/basicIDB_PBM.html"
|
||||||
self.IDBName = "IDBTest"
|
self.IDBName = "IDBTest"
|
||||||
self.IDBStoreName = "IDBTestStore"
|
self.IDBStoreName = "IDBTestStore"
|
||||||
self.IDBVersion = 1
|
self.IDBVersion = 1
|
||||||
|
|
@ -40,41 +41,61 @@ class IDBEncryptionPBM(QuotaTestCase):
|
||||||
self.defaultQMPrefValue = self.marionette.get_pref(QM_TESTING_PREF)
|
self.defaultQMPrefValue = self.marionette.get_pref(QM_TESTING_PREF)
|
||||||
self.marionette.set_pref(QM_TESTING_PREF, True)
|
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):
|
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(INDEXED_DB_PBM_PREF, self.defaultIDBPrefValue)
|
||||||
self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
|
self.marionette.set_pref(QM_TESTING_PREF, self.defaultQMPrefValue)
|
||||||
|
|
||||||
# closes the new private window we opened in the setUp and referred by 'pbmWindowHandle'
|
def test_raw_IDB_data_ondisk(self):
|
||||||
self.marionette.close()
|
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):
|
def test_ensure_encrypted_IDB_data_ondisk(self):
|
||||||
self.marionette.execute_script(
|
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'});
|
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),
|
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.sqliteWALReleased())
|
||||||
self.ensureInvariantHolds(
|
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
|
# 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
|
# 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
|
re.search(self.IDBValue.encode("ascii"), f_binary.read()) is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertFalse(foundRawValue, "Blob file did not get encrypted")
|
validator(foundRawValue)
|
||||||
|
|
||||||
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),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def validateSqlite(self, validator):
|
||||||
self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
|
self.ensureInvariantHolds(lambda _: self.sqliteWALReleased())
|
||||||
self.ensureInvariantHolds(
|
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
|
foundRawValue = False
|
||||||
with open(sqliteDBFile, "rb") as f_binary:
|
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
|
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):
|
def getIDBStoragePath(self):
|
||||||
if self.idbStoragePath is not None:
|
if self.idbStoragePath is not None:
|
||||||
return self.idbStoragePath
|
return self.idbStoragePath
|
||||||
|
|
||||||
|
assert self.origin is not None
|
||||||
|
assert self.persistenceType is not None
|
||||||
|
|
||||||
self.idbStoragePath = self.getStoragePath(
|
self.idbStoragePath = self.getStoragePath(
|
||||||
self.profilePath, self.origin, "private", "idb"
|
self.profilePath, self.origin, self.persistenceType, "idb"
|
||||||
)
|
)
|
||||||
|
|
||||||
print("idb origin directory = " + self.idbStoragePath)
|
print("idb origin directory = " + self.idbStoragePath)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from marionette_driver import Wait
|
from marionette_driver import Wait
|
||||||
from marionette_harness import MarionetteTestCase
|
from marionette_harness import MarionetteTestCase
|
||||||
|
|
@ -88,3 +89,25 @@ class QuotaTestCase(MarionetteTestCase):
|
||||||
|
|
||||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||||
return self.marionette.execute_async_script(script)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
async function addDataIntoCache(name, request, response) {
|
async function addDataIntoCache(name, request, response) {
|
||||||
let cache = await ensureCache(name);
|
let cache = await ensureCache(name);
|
||||||
await cache.put(request, response);
|
return cache.put(request, response);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@
|
||||||
await (new Promise((resolve, reject) => {
|
await (new Promise((resolve, reject) => {
|
||||||
var transaction = db.transaction([store], "readwrite");
|
var transaction = db.transaction([store], "readwrite");
|
||||||
var put = transaction.objectStore(store).put(value, key);
|
var put = transaction.objectStore(store).put(value, key);
|
||||||
put.onsuccess = resolve();
|
put.onerror = reject;
|
||||||
|
put.onsuccess = resolve;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
closeIDB(db)
|
closeIDB(db)
|
||||||
|
|
@ -35,6 +36,14 @@
|
||||||
function closeIDB(db) {
|
function closeIDB(db) {
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteIDB(db) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let deleteReq = indexedDB.deleteDatabase(db);
|
||||||
|
deleteReq.onerror = reject;
|
||||||
|
deleteReq.onsuccess = resolve;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue