Skip to content

Commit 28e12a9

Browse files
Fix crashes caused by regex characters in email signature (#3252)
* fix(issue_3232_fix_regex_usage): treat email signatures as literal text * fix(issue_3232_fix_regex_usage): handle alias signatures as literal text * Bump gradle-wrapper from 9.6.1 to 9.7.0 Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.1 to 9.7.0. - [Release notes](https://github.com/gradle/gradle/releases) - [Commits](gradle/gradle@v9.6.1...v9.7.0) --- updated-dependencies: - dependency-name: gradle-wrapper dependency-version: 9.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent 8cba588 commit 28e12a9

6 files changed

Lines changed: 113 additions & 22 deletions

File tree

‎FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DraftViewModel.kt‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,14 @@ class DraftViewModel(
156156

157157
val isTextTheSame =
158158
if (outgoingMessageInfo.signature != null && !draftFingerprint.skipCheckingSignature) {
159-
val textWithoutSignature = outgoingMessageInfo.msg?.replaceFirst(
160-
regex = ("\n\n" + outgoingMessageInfo.signature).toRegex(RegexOption.MULTILINE),
161-
replacement = ""
162-
) ?: ""
163-
textWithoutSignature == draftFingerprint.msgText
164-
} else {
165-
(outgoingMessageInfo.msg ?: "") == draftFingerprint.msgText
166-
}
159+
val textWithoutSignature = removeSignature(
160+
message = outgoingMessageInfo.msg,
161+
signature = outgoingMessageInfo.signature
162+
)
163+
textWithoutSignature == draftFingerprint.msgText
164+
} else {
165+
(outgoingMessageInfo.msg ?: "") == draftFingerprint.msgText
166+
}
167167

168168
if (!isTextTheSame
169169
|| outgoingMessageInfo.subject != draftFingerprint.msgSubject
@@ -343,5 +343,12 @@ class DraftViewModel(
343343

344344
companion object {
345345
val DELAY_TIMEOUT = TimeUnit.SECONDS.toMillis(30)
346+
347+
internal fun removeSignature(message: String?, signature: String): String {
348+
return message?.replaceFirst(
349+
oldValue = "\n\n$signature",
350+
newValue = ""
351+
) ?: ""
352+
}
346353
}
347354
}

‎FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ import com.flowcrypt.email.ui.adapter.AutoCompleteResultRecyclerViewAdapter
109109
import com.flowcrypt.email.ui.adapter.FromAddressesAdapter
110110
import com.flowcrypt.email.ui.adapter.RecipientChipRecyclerViewAdapter
111111
import com.flowcrypt.email.ui.adapter.recyclerview.itemdecoration.MarginItemDecoration
112+
import com.flowcrypt.email.util.EmailSignatureUtil
112113
import com.flowcrypt.email.util.FileAndDirectoryUtils
113114
import com.flowcrypt.email.util.GeneralUtil
114115
import com.flowcrypt.email.util.LogsUtil
@@ -2164,9 +2165,11 @@ class CreateMessageFragment : BaseFragment<FragmentCreateMessageBinding>(),
21642165
val oldSignature = composeMsgViewModel.outgoingMessageInfoStateFlow.value.signature
21652166
if (oldSignature == null && args.messageType == MessageType.DRAFT) {
21662167
val extractedSignature = aliases.firstOrNull { alias ->
2167-
alias.plainTextSignature != null && binding?.editTextEmailMessage?.text?.contains(
2168-
("^${alias.plainTextSignature}$").toRegex(RegexOption.MULTILINE)
2169-
) == true
2168+
val aliasSignature = alias.plainTextSignature
2169+
aliasSignature != null && EmailSignatureUtil.containsSignature(
2170+
message = binding?.editTextEmailMessage?.text,
2171+
signature = aliasSignature
2172+
)
21702173
}?.plainTextSignature
21712174

21722175
if (!extractedSignature.isNullOrEmpty()) {
@@ -2180,19 +2183,18 @@ class CreateMessageFragment : BaseFragment<FragmentCreateMessageBinding>(),
21802183
return
21812184
}
21822185

2183-
val messageHasOldSignature =
2184-
oldSignature != null && binding?.editTextEmailMessage?.text?.contains(
2185-
("^$oldSignature$").toRegex(RegexOption.MULTILINE)
2186-
) == true
2186+
val messageText = binding?.editTextEmailMessage?.text?.toString() ?: ""
2187+
val messageWithUpdatedSignature = oldSignature?.let {
2188+
EmailSignatureUtil.replaceSignature(
2189+
message = messageText,
2190+
oldSignature = it,
2191+
newSignature = newSignature
2192+
)
2193+
}
21872194

2188-
if (messageHasOldSignature) {
2195+
if (messageWithUpdatedSignature != null) {
21892196
useNewSignature = true
2190-
binding?.editTextEmailMessage?.setText(
2191-
binding?.editTextEmailMessage?.text?.replaceFirst(
2192-
regex = oldSignature.toRegex(RegexOption.MULTILINE),
2193-
replacement = newSignature
2194-
)
2195-
)
2197+
binding?.editTextEmailMessage?.setText(messageWithUpdatedSignature)
21962198
} else if (oldSignature == null) {
21972199
useNewSignature = true
21982200
if (binding?.editTextEmailMessage?.text?.isEmpty() == true) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
3+
* Contributors: denbond7
4+
*/
5+
6+
package com.flowcrypt.email.util
7+
8+
internal object EmailSignatureUtil {
9+
fun containsSignature(message: CharSequence?, signature: String): Boolean {
10+
return message != null && findSignature(message, signature) != null
11+
}
12+
13+
fun replaceSignature(
14+
message: String,
15+
oldSignature: String,
16+
newSignature: String
17+
): String? {
18+
val signatureMatch = findSignature(message, oldSignature) ?: return null
19+
return message.replaceRange(signatureMatch.range, newSignature)
20+
}
21+
22+
private fun findSignature(message: CharSequence, signature: String): MatchResult? {
23+
val signatureRegex = ("^${Regex.escape(signature)}$").toRegex(RegexOption.MULTILINE)
24+
return signatureRegex.find(message)
25+
}
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
3+
* Contributors: denbond7
4+
*/
5+
6+
package com.flowcrypt.email.jetpack.viewmodel
7+
8+
import org.junit.Assert.assertEquals
9+
import org.junit.Test
10+
11+
class DraftViewModelTest {
12+
@Test
13+
fun testRemoveSignatureWithRegexMetaCharacters() {
14+
val signature = """
15+
Kind regards
16+
17+
(123 456
18+
[example].*+?\
19+
""".trimIndent()
20+
val message = "Message body\n\n$signature"
21+
22+
assertEquals("Message body", DraftViewModel.removeSignature(message, signature))
23+
}
24+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com
3+
* Contributors: denbond7
4+
*/
5+
6+
package com.flowcrypt.email.util
7+
8+
import org.junit.Assert.assertEquals
9+
import org.junit.Assert.assertTrue
10+
import org.junit.Test
11+
12+
class EmailSignatureUtilTest {
13+
@Test
14+
fun testMatchingAndReplacementWithRegexMetaCharacters() {
15+
val oldSignature = """
16+
Best regards,
17+
(111 222 333).*+?\
18+
$100 \path
19+
""".trimIndent()
20+
val newSignature = """
21+
Sincerely,
22+
$500 & \path\to\file
23+
""".trimIndent()
24+
val message = "Hello World\n\n$oldSignature"
25+
26+
assertTrue(EmailSignatureUtil.containsSignature(message, oldSignature))
27+
assertEquals(
28+
"Hello World\n\n$newSignature",
29+
EmailSignatureUtil.replaceSignature(message, oldSignature, newSignature)
30+
)
31+
}
32+
}

‎gradle/wrapper/gradle-wrapper.jar‎

-957 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)