diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-02 09:51:18 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-02 09:51:18 +0100 |
| commit | 34e29204a3720741b4b5373cae2afbdb0e17bad2 (patch) | |
| tree | 56d5107c713035674fbdf0ad8f033f384d8d117d /app/src/test | |
| parent | fda98e40f68539e5e50d29158bb5b446e6c41d5d (diff) | |
| download | HardenedPass-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')
| -rw-r--r-- | app/src/test/java/app/passwordstore/ui/main/LaunchActivityTest.kt | 45 |
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() + } +} |
