From 8e91f52304faa0ede49c80e88198967408e7ede7 Mon Sep 17 00:00:00 2001 From: editinghero <69391940+editinghero@users.noreply.github.com> Date: Thu, 3 Sep 2026 17:23:39 +0000 Subject: [PATCH] Fix multiple locked vault loopholes in Android and Web apps - Fix Web app: Ensure locked notes are excluded from analytics word count and tag lists when the vault is locked. - Fix Android app: Prevent opening locked notes via intents (e.g. from widgets) if the vault is not unlocked. - Fix Android app: Prevent plaintext tags and image URLs from leaking to the local database when downloading a locked note from Firebase while the vault is locked. --- .../java/com/astralquarks/notes/MainActivity.kt | 11 ++++++++++- .../main/java/com/astralquarks/notes/model/Note.kt | 14 ++++++++++++++ web/src/main.ts | 6 +++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/astralquarks/notes/MainActivity.kt b/app/src/main/java/com/astralquarks/notes/MainActivity.kt index 3c3148b..b3730cd 100644 --- a/app/src/main/java/com/astralquarks/notes/MainActivity.kt +++ b/app/src/main/java/com/astralquarks/notes/MainActivity.kt @@ -136,7 +136,16 @@ fun MainAppContent( Intent.ACTION_VIEW -> { val noteId = current.getStringExtra(QuickNoteWidgetProvider.EXTRA_NOTE_ID) if (!noteId.isNullOrBlank()) { - currentScreen = Screen.EditNote(noteId, "") + scope.launch { + val n = viewModel.repository.getNoteById(noteId) + if (n != null) { + if (n.isLocked && !isVaultUnlocked) { + Toast.makeText(activity, "Note is locked. Please unlock the vault first.", Toast.LENGTH_SHORT).show() + } else { + currentScreen = Screen.EditNote(noteId, "") + } + } + } } } } diff --git a/app/src/main/java/com/astralquarks/notes/model/Note.kt b/app/src/main/java/com/astralquarks/notes/model/Note.kt index 6a1e149..e2cfbce 100644 --- a/app/src/main/java/com/astralquarks/notes/model/Note.kt +++ b/app/src/main/java/com/astralquarks/notes/model/Note.kt @@ -111,7 +111,21 @@ data class Note( // If decryption key does not match, retain placeholder without crashing title = "[Encrypted Note]" content = "Unable to decrypt content with current vault key." + tags = emptyList() + imageUrls = emptyList() } + } else if (isEncrypted) { + // If it is encrypted but no key was provided or payload is missing, clear tags to avoid leak + title = "[Locked Note]" + content = "Unlock your private vault to view this encrypted note." + tags = emptyList() + imageUrls = emptyList() + } else if (isLocked && secretKey == null) { + // Also protect tags for legacy format or unexpected states if vault is locked + title = "[Locked Note]" + content = "Unlock your private vault to view this encrypted note." + tags = emptyList() + imageUrls = emptyList() } return Note( diff --git a/web/src/main.ts b/web/src/main.ts index c8f7415..01ece21 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -708,7 +708,7 @@ class AstralNotesApp { const allTags = new Set(); this.notes.forEach(n => { - if (!n.isTrash && !n.isDeleted) { + if (!n.isTrash && !n.isDeleted && (!n.isLocked || vaultManager.isUnlocked())) { n.tags.forEach(t => allTags.add(t)); } }); @@ -1434,14 +1434,14 @@ class AstralNotesApp { let totalWords = 0; this.notes.forEach(n => { - if (!n.isTrash && !n.isDeleted) { + if (!n.isTrash && !n.isDeleted && (!n.isLocked || vaultManager.isUnlocked())) { totalWords += n.content.trim().split(/\s+/).filter(Boolean).length; } }); const allTags = new Set(); this.notes.forEach(n => { - if (!n.isTrash && !n.isDeleted) { + if (!n.isTrash && !n.isDeleted && (!n.isLocked || vaultManager.isUnlocked())) { n.tags.forEach(t => allTags.add(t)); } });