From 2be829425b6dfe93da2cfbf08cd86e296aa70943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Forlini?= Date: Fri, 23 Jun 2023 12:11:49 +0200 Subject: [PATCH] Add HTML sanitization UI tests 189699 + 189700 and minor gardening. MAILANDR-605 --- ...ConversationDetailHtmlSanitizationTests.kt | 85 +++++++++++++++++++ .../ConversationDetailRemoteContentTests.kt} | 4 +- .../MessageDetailHtmlSanitizationTests.kt | 82 ++++++++++++++++++ .../MessageDetailRemoteContentTests.kt} | 4 +- .../detail/section/MessageBodySection.kt | 15 +++- scripts/uitests/AssetsFile.lock | 2 +- 6 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailHtmlSanitizationTests.kt rename app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/{conversation/ConversationDetailRemoteContentTest.kt => bodycontent/ConversationDetailRemoteContentTests.kt} (99%) create mode 100644 app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailHtmlSanitizationTests.kt rename app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/{message/MessageDetailDetailRemoteContentTest.kt => bodycontent/MessageDetailRemoteContentTests.kt} (97%) diff --git a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailHtmlSanitizationTests.kt b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailHtmlSanitizationTests.kt new file mode 100644 index 0000000000..edc6a10ccc --- /dev/null +++ b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailHtmlSanitizationTests.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Proton Technologies AG + * This file is part of Proton Technologies AG and Proton Mail. + * + * Proton Mail is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Proton Mail is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Proton Mail. If not, see . + */ + +package ch.protonmail.android.uitest.e2e.mailbox.detail.bodycontent + +import ch.protonmail.android.di.ServerProofModule +import ch.protonmail.android.networkmocks.mockwebserver.requests.ignoreQueryParams +import ch.protonmail.android.networkmocks.mockwebserver.requests.matchWildcards +import ch.protonmail.android.networkmocks.mockwebserver.requests.respondWith +import ch.protonmail.android.networkmocks.mockwebserver.requests.serveOnce +import ch.protonmail.android.networkmocks.mockwebserver.requests.withStatusCode +import ch.protonmail.android.test.annotations.suite.SmokeTest +import ch.protonmail.android.uitest.MockedNetworkTest +import ch.protonmail.android.uitest.helpers.core.TestId +import ch.protonmail.android.uitest.helpers.core.navigation.Destination +import ch.protonmail.android.uitest.helpers.core.navigation.navigator +import ch.protonmail.android.uitest.helpers.login.LoginStrategy +import ch.protonmail.android.uitest.helpers.network.mockNetworkDispatcher +import ch.protonmail.android.uitest.robot.detail.conversationDetailRobot +import ch.protonmail.android.uitest.robot.detail.section.messageBodySection +import ch.protonmail.android.uitest.robot.detail.section.verify +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import io.mockk.mockk +import me.proton.core.auth.domain.usecase.ValidateServerProof +import org.junit.Test + +@SmokeTest +@HiltAndroidTest +@UninstallModules(ServerProofModule::class) +internal class ConversationDetailHtmlSanitizationTests : MockedNetworkTest(loginStrategy = LoginStrategy.LoggedOut) { + + @JvmField + @BindValue + val serverProofValidation: ValidateServerProof = mockk(relaxUnitFun = true) + + @Test + @TestId("189699") + fun checkHtmlSanitizationInConversationMode() { + mockWebServer.dispatcher = mockNetworkDispatcher(useDefaultMailSettings = false) { + addMockRequests( + "/mail/v4/settings" + respondWith "/mail/v4/settings/mail-v4-settings_placeholder_conversation.json" + withStatusCode 200, + "/mail/v4/conversations" + respondWith "/mail/v4/conversations/conversations_189699.json" + withStatusCode 200 matchWildcards true ignoreQueryParams true, + "/mail/v4/conversations/*" + respondWith "/mail/v4/conversations/conversation-id/conversation-id_189699.json" + withStatusCode 200 matchWildcards true serveOnce true, + "/mail/v4/messages/*" + respondWith "/mail/v4/messages/message-id/message-id_189699.json" + withStatusCode 200 matchWildcards true serveOnce true + ) + } + + navigator { + navigateTo(Destination.MailDetail(0)) + } + + conversationDetailRobot { + messageBodySection { + waitUntilMessageIsShown() + + verify { hasHtmlContentSanitised() } + } + } + } +} diff --git a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/conversation/ConversationDetailRemoteContentTest.kt b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailRemoteContentTests.kt similarity index 99% rename from app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/conversation/ConversationDetailRemoteContentTest.kt rename to app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailRemoteContentTests.kt index 3a8ffa16ab..3cd76cacc9 100644 --- a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/conversation/ConversationDetailRemoteContentTest.kt +++ b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/ConversationDetailRemoteContentTests.kt @@ -16,7 +16,7 @@ * along with Proton Mail. If not, see . */ -package ch.protonmail.android.uitest.e2e.mailbox.detail.conversation +package ch.protonmail.android.uitest.e2e.mailbox.detail.bodycontent import arrow.core.Either import ch.protonmail.android.di.ServerProofModule @@ -52,7 +52,7 @@ import org.junit.Test @RegressionTest @HiltAndroidTest @UninstallModules(ServerProofModule::class) -internal class ConversationDetailRemoteContentTest : +internal class ConversationDetailRemoteContentTests : MockedNetworkTest(loginStrategy = LoginStrategy.LoggedOut), DetailRemoteContentTest { diff --git a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailHtmlSanitizationTests.kt b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailHtmlSanitizationTests.kt new file mode 100644 index 0000000000..557a1ede10 --- /dev/null +++ b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailHtmlSanitizationTests.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Proton Technologies AG + * This file is part of Proton Technologies AG and Proton Mail. + * + * Proton Mail is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Proton Mail is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Proton Mail. If not, see . + */ + +package ch.protonmail.android.uitest.e2e.mailbox.detail.bodycontent + +import ch.protonmail.android.di.ServerProofModule +import ch.protonmail.android.networkmocks.mockwebserver.requests.ignoreQueryParams +import ch.protonmail.android.networkmocks.mockwebserver.requests.matchWildcards +import ch.protonmail.android.networkmocks.mockwebserver.requests.respondWith +import ch.protonmail.android.networkmocks.mockwebserver.requests.serveOnce +import ch.protonmail.android.networkmocks.mockwebserver.requests.withStatusCode +import ch.protonmail.android.test.annotations.suite.SmokeTest +import ch.protonmail.android.uitest.MockedNetworkTest +import ch.protonmail.android.uitest.helpers.core.TestId +import ch.protonmail.android.uitest.helpers.core.navigation.Destination +import ch.protonmail.android.uitest.helpers.core.navigation.navigator +import ch.protonmail.android.uitest.helpers.login.LoginStrategy +import ch.protonmail.android.uitest.helpers.network.mockNetworkDispatcher +import ch.protonmail.android.uitest.robot.detail.messageDetailRobot +import ch.protonmail.android.uitest.robot.detail.section.messageBodySection +import ch.protonmail.android.uitest.robot.detail.section.verify +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import io.mockk.mockk +import me.proton.core.auth.domain.usecase.ValidateServerProof +import org.junit.Test + +@SmokeTest +@HiltAndroidTest +@UninstallModules(ServerProofModule::class) +internal class MessageDetailHtmlSanitizationTests : MockedNetworkTest(loginStrategy = LoginStrategy.LoggedOut) { + + @JvmField + @BindValue + val serverProofValidation: ValidateServerProof = mockk(relaxUnitFun = true) + + @Test + @TestId("189700") + fun checkHtmlSanitizationInMessageMode() { + mockWebServer.dispatcher = mockNetworkDispatcher(useDefaultMailSettings = false) { + addMockRequests( + "/mail/v4/settings" + respondWith "/mail/v4/settings/mail-v4-settings_placeholder_messages.json" + withStatusCode 200, + "/mail/v4/messages" + respondWith "/mail/v4/messages/messages_189700.json" + withStatusCode 200 matchWildcards true ignoreQueryParams true, + "/mail/v4/messages/*" + respondWith "/mail/v4/messages/message-id/message-id_189700.json" + withStatusCode 200 matchWildcards true serveOnce true + ) + } + + navigator { + navigateTo(Destination.MailDetail(0)) + } + + messageDetailRobot { + messageBodySection { + waitUntilMessageIsShown() + + verify { hasHtmlContentSanitised() } + } + } + } +} diff --git a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/message/MessageDetailDetailRemoteContentTest.kt b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailRemoteContentTests.kt similarity index 97% rename from app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/message/MessageDetailDetailRemoteContentTest.kt rename to app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailRemoteContentTests.kt index 1b3d54efea..82598b0a00 100644 --- a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/message/MessageDetailDetailRemoteContentTest.kt +++ b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/e2e/mailbox/detail/bodycontent/MessageDetailRemoteContentTests.kt @@ -16,7 +16,7 @@ * along with Proton Mail. If not, see . */ -package ch.protonmail.android.uitest.e2e.mailbox.detail.message +package ch.protonmail.android.uitest.e2e.mailbox.detail.bodycontent import arrow.core.Either import ch.protonmail.android.di.ServerProofModule @@ -49,7 +49,7 @@ import org.junit.Test @SmokeTest @HiltAndroidTest @UninstallModules(ServerProofModule::class) -internal class MessageDetailDetailRemoteContentTest : +internal class MessageDetailRemoteContentTests : MockedNetworkTest(loginStrategy = LoginStrategy.LoggedOut), DetailRemoteContentTest { diff --git a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/robot/detail/section/MessageBodySection.kt b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/robot/detail/section/MessageBodySection.kt index e37941d49b..2210e46018 100644 --- a/app/src/uiTest/kotlin/ch/protonmail/android/uitest/robot/detail/section/MessageBodySection.kt +++ b/app/src/uiTest/kotlin/ch/protonmail/android/uitest/robot/detail/section/MessageBodySection.kt @@ -97,8 +97,6 @@ internal class MessageBodySection : ComposeSectionRobot() { } fun hasRemoteImageLoaded(expected: Boolean) { - composeTestRule.waitForIdle() - val jsSnippet = """ |var image = document.querySelector('img'); |var isLoaded = image.complete && image.naturalWidth != 0 && image.naturalHeight != 0; @@ -108,6 +106,19 @@ internal class MessageBodySection : ComposeSectionRobot() { runAndMatchJsCodeOutput(jsSnippet, expected) } + fun hasHtmlContentSanitised() { + val jsSnippet = """ + |var onLoad = document.querySelector('body').onload; + |var form = document.querySelector('form'); + |var relLinks = document.querySelector('link'); + |var iframe = document.querySelector('iframe'); + |var ping = document.querySelector('a').ping; + |return onLoad == null && form == null && relLinks == null && iframe == null && ping == ""; + """.trimMargin() + + runAndMatchJsCodeOutput(jsSnippet, true) + } + private fun runAndMatchJsCodeOutput(script: String, expected: Boolean) { webView.check( webMatches( diff --git a/scripts/uitests/AssetsFile.lock b/scripts/uitests/AssetsFile.lock index f1481a98ba..ca31de6637 100644 --- a/scripts/uitests/AssetsFile.lock +++ b/scripts/uitests/AssetsFile.lock @@ -1 +1 @@ -NETWORK_ASSETS_TARGET_REF=342e486a95b4128ea2c481d8522de77509b7dbc9 +NETWORK_ASSETS_TARGET_REF=25c54c4ebd7d01b426006f2bc53fba56659c7d75