Reject dot-only chat names to prevent zip-slip in plaintext export.

This commit is contained in:
Greyson Parrelli
2026-07-10 04:25:09 +00:00
parent 8bc31386a5
commit b60f53f4df
2 changed files with 22 additions and 1 deletions
@@ -435,7 +435,8 @@ object PlaintextExportRepository {
@VisibleForTesting
internal fun sanitizeFileName(name: String): String {
return name.replace(Regex("[\\\\/:*?\"<>|]"), "_").trim().take(100)
val sanitized = name.replace(Regex("[\\\\/:*?\"<>|]"), "_").trim().take(100)
return if (sanitized.isEmpty() || sanitized.all { it == '.' }) "chat" else sanitized
}
private fun <T> ExecutorService.submitTyped(callable: Callable<T>): Future<T> {
@@ -723,6 +723,26 @@ class PlaintextExportRepositoryTest {
assertEquals("My Chat Group 2024", PlaintextExportRepository.sanitizeFileName("My Chat Group 2024"))
}
@Test
fun `sanitizeFileName rejects dot-only names to prevent path traversal`() {
assertEquals("chat", PlaintextExportRepository.sanitizeFileName("."))
assertEquals("chat", PlaintextExportRepository.sanitizeFileName(".."))
assertEquals("chat", PlaintextExportRepository.sanitizeFileName("..."))
assertEquals("chat", PlaintextExportRepository.sanitizeFileName(" .. "))
}
@Test
fun `sanitizeFileName falls back for empty result`() {
assertEquals("chat", PlaintextExportRepository.sanitizeFileName(""))
assertEquals("chat", PlaintextExportRepository.sanitizeFileName(" "))
}
@Test
fun `sanitizeFileName preserves names that merely contain dots`() {
assertEquals("..hidden", PlaintextExportRepository.sanitizeFileName("..hidden"))
assertEquals("my.chat.group", PlaintextExportRepository.sanitizeFileName("my.chat.group"))
}
// ==================== getSenderName tests ====================
@Test