improve profile isolation

This commit is contained in:
Fabian Freund
2026-02-17 17:24:53 +01:00
parent fcd3c0b38b
commit abe5ddf27c
6 changed files with 82 additions and 18 deletions
+66 -5
View File
@@ -87,14 +87,75 @@ class _Filesystem {
Future<void> _linkMozillaDir(Directory filesDir) async {
final mozillaDir = Directory(p.join(selectedProfileDir.path, 'mozilla'));
await mozillaDir.create();
await mozillaDir.create(recursive: true);
final mozillaLink = Link(p.join(filesDir.path, 'mozilla'));
if (await mozillaLink.exists()) {
await mozillaLink.delete();
final mozillaPath = p.join(filesDir.path, 'mozilla');
Future<void> moveAside(FileSystemEntityType type) async {
final backupPath = p.join(
filesDir.path,
'mozilla.backup.${DateTime.now().millisecondsSinceEpoch}',
);
if (type == FileSystemEntityType.directory) {
await Directory(mozillaPath).rename(backupPath);
} else {
await File(mozillaPath).rename(backupPath);
}
}
await mozillaLink.create(mozillaDir.path);
Future<void> createLink() async {
await Link(mozillaPath).create(mozillaDir.path);
}
final currentType = await FileSystemEntity.type(
mozillaPath,
followLinks: false,
);
switch (currentType) {
case FileSystemEntityType.notFound:
break;
case FileSystemEntityType.link:
final link = Link(mozillaPath);
try {
if (await link.target() == mozillaDir.path) {
return;
}
} on FileSystemException {
// Replace unreadable or broken links.
}
await link.delete();
case FileSystemEntityType.directory:
case FileSystemEntityType.file:
case FileSystemEntityType.unixDomainSock:
case FileSystemEntityType.pipe:
default:
await moveAside(currentType);
}
try {
await createLink();
} on PathExistsException {
final retryType = await FileSystemEntity.type(
mozillaPath,
followLinks: false,
);
if (retryType == FileSystemEntityType.link) {
final link = Link(mozillaPath);
if (await link.target() == mozillaDir.path) {
return;
}
await link.delete();
} else if (retryType != FileSystemEntityType.notFound) {
await moveAside(retryType);
}
await createLink();
}
}
Future<void> _setupSqliteCache() async {
@@ -150,7 +150,9 @@ object GlobalComponents {
//RustHttpConfig.setClient(lazy { newComponents.core.client })
newComponents.core.engine.warmUp()
if (mode == ComponentsMode.FULL) {
newComponents.core.engine.warmUp()
}
fun restorePreviousCustomTabs() {
if (previousCustomTabs.isEmpty()) return
@@ -57,6 +57,10 @@ class ProfileContext(private val base: Context, val relativePath: String) :
return customApplicationInfo
}
override fun getApplicationContext(): Context {
return this
}
override fun getFilesDir(): File {
return customFilesDir
}
@@ -303,15 +303,14 @@ class IntentReceiverActivity : Activity() {
contextId: String?,
) {
val message = "This PWA was originally installed in a different profile. " +
"Opening it here will use your current profile's data and settings, " +
"which means you won't see the same content, preferences, or saved data " +
"that you had in the original profile.\n\n" +
"Opening it here uses only your current profile's data and settings. " +
"The original profile's app state and saved data will not be used.\n\n" +
"Do you want to proceed anyway?"
AlertDialog.Builder(this)
.setTitle("PWA Profile Mismatch")
.setMessage(message)
.setPositiveButton("Open Anyway") { _, _ ->
.setPositiveButton("Open in Current Profile") { _, _ ->
Log.d(TAG, "User chose to open PWA despite profile mismatch")
launchPwaWithContext(url, contextId)
}
@@ -144,7 +144,7 @@ class GeckoPwaApiImpl(
val (iconBitmap, isMaskable) = loadPwaIcon(manifest)
val shortcutId = generateShortcutId(manifest.startUrl)
val shortcutId = generateShortcutId(manifest.startUrl, profileUuid)
val launchToken = resolveLaunchToken(
shortcutManager = shortcutManager,
shortcutId = shortcutId,
@@ -189,11 +189,11 @@ class GeckoPwaApiImpl(
}
/**
* Generates a collision-resistant shortcut ID from a URL using SHA-256.
* Generates a collision-resistant shortcut ID from URL + profile using SHA-256.
*/
private fun generateShortcutId(url: String): String {
private fun generateShortcutId(url: String, profileUuid: String): String {
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(url.toByteArray())
val hash = digest.digest("$url::$profileUuid".toByteArray())
val hex = hash.take(16).joinToString("") { "%02x".format(it) }
return "pwa_$hex"
}
@@ -232,9 +232,7 @@ class Core(
*/
val customTabsStore by lazy { CustomTabsServiceStore() }
// Must use the base application context (not ProfileContext) so the database
// matches what WebAppLauncherActivity (from the library) uses when loading manifests.
val webAppManifestStorage by lazy { ManifestStorage(context.applicationContext) }
val webAppManifestStorage by lazy { ManifestStorage(context) }
val webAppShortcutManager by lazy {
WebAppShortcutManager(context, client, webAppManifestStorage)
@@ -288,4 +286,4 @@ class Core(
}
}
}
}