aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/test/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-02 09:51:18 +0100
committerDanilo M. <danix@danix.xyz>2026-11-02 09:51:18 +0100
commit34e29204a3720741b4b5373cae2afbdb0e17bad2 (patch)
tree56d5107c713035674fbdf0ad8f033f384d8d117d /app/src/test/java
parentfda98e40f68539e5e50d29158bb5b446e6c41d5d (diff)
downloadHardenedPass-34e29204a3720741b4b5373cae2afbdb0e17bad2.tar.gz
HardenedPass-34e29204a3720741b4b5373cae2afbdb0e17bad2.zip
fix: stop exporting DecryptActivity and validate decrypt paths
DecryptActivity was exported without an intent filter, so any installed app could start it with arbitrary FILE_PATH/REPO_PATH extras (and crash it by omitting them). Nothing outside the app needs to launch it: shortcuts and the decrypt action go through LaunchActivity. LaunchActivity stays exported, so it now treats FILE_PATH as untrusted: the path is forwarded only when it canonically resolves to a regular file inside the password store, and REPO_PATH is always the real store directory instead of the caller-supplied value. Anything else falls back to the main screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/test/java')
-rw-r--r--app/src/test/java/app/passwordstore/ui/main/LaunchActivityTest.kt45
1 files changed, 45 insertions, 0 deletions
diff --git a/app/src/test/java/app/passwordstore/ui/main/LaunchActivityTest.kt b/app/src/test/java/app/passwordstore/ui/main/LaunchActivityTest.kt
new file mode 100644
index 00000000..3c30071a
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/ui/main/LaunchActivityTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package app.passwordstore.ui.main
+
+import app.passwordstore.ui.main.LaunchActivity.Companion.isFileInRepo
+import java.io.File
+import java.nio.file.Files
+import kotlin.test.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class LaunchActivityTest {
+
+ @Test
+ fun onlyAcceptsFilesInsideTheRepository() {
+ val root = Files.createTempDirectory("aps").toFile()
+ val repo = File(root, "store").apply { mkdirs() }
+ val inside =
+ File(repo, "site/login.gpg").apply {
+ parentFile!!.mkdirs()
+ writeText("x")
+ }
+ val outside = File(root, "secret.gpg").apply { writeText("x") }
+ val sibling =
+ File(root, "store-evil/login.gpg").apply {
+ parentFile!!.mkdirs()
+ writeText("x")
+ }
+ val link = File(repo, "link.gpg").toPath()
+ Files.createSymbolicLink(link, outside.toPath())
+
+ assertTrue(isFileInRepo(inside.absolutePath, repo))
+ assertFalse(isFileInRepo(outside.absolutePath, repo))
+ assertFalse(isFileInRepo("${repo.absolutePath}/../secret.gpg", repo))
+ assertFalse(isFileInRepo(sibling.absolutePath, repo))
+ assertFalse(isFileInRepo(link.toString(), repo))
+ assertFalse(isFileInRepo(repo.absolutePath, repo))
+ assertFalse(isFileInRepo(File(repo, "missing.gpg").absolutePath, repo))
+ assertFalse(isFileInRepo(null, repo))
+ assertFalse(isFileInRepo("", repo))
+ root.deleteRecursively()
+ }
+}