Skip to content

Commit

Permalink
Display warning page for malicious sites (#5416)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/72649045549333/1208836754662807/f

### Description

### Steps to test this PR

_Pre-requisites_
- [ ] Enable maliciousSiteProtection and enableMaliciousSiteProtection
RC flags

_Feature 1_
- [ ] Open https://privacy-test-pages.site/security/badware/, check
snackbar isn't shown
- [ ] Check a Snackbar informing of a malicious site is shown for the
following tests
- [ ] [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [ ] [Standard Malware
Test](https://privacy-test-pages.site/security/badware/malware.html)
- [ ] [Phishing iFrame
Loader](https://privacy-test-pages.site/security/badware/phishing-iframe-loader.html)
- [ ] [Phishing JS Redirector
(Direct)](https://privacy-test-pages.site/security/badware/phishing-js-redirector-helper.html)
- [ ] [Phishing JS Redirector
(Indirect)](https://privacy-test-pages.site/security/badware/phishing-js-redirector.html)
- [ ] [Phishing Legit iFrame
Loader](https://privacy-test-pages.site/security/badware/phishing-legit-iframe-loader.html)
- [ ] [Phishing Redirect via Meta Refresh (Not Flagged in
Dataset)](https://privacy-test-pages.site/security/badware/phishing-meta-redirect-clean.html)
- [ ] [Phishing Redirect via Meta Refresh (Flagged in
Dataset)](https://privacy-test-pages.site/security/badware/phishing-meta-redirect.html)
- [ ] [Phishing Open via
Popups](https://privacy-test-pages.site/security/badware/phishing-popups.html)
- [ ] [Phishing Opening with URL
Tampering](https://privacy-test-pages.site/security/badware/phishing-url-tampering.html)
- [ ] [Phishing Form
Submission](https://privacy-test-pages.site/security/badware/phishing-form-submission.html)
-> Submit form
- [ ] [Phishing Service
Worker](https://privacy-test-pages.site/security/badware/phishing-service-worker.html)
- Only navigate, not fetch
- [ ] [HTTP 301 Redirect to Main Phishing Test
Page](https://privacy-test-pages.site/security/badware/phishing-redirect/)
- [ ] [HTTP 302 Redirect to Main Phishing Test
Page](https://privacy-test-pages.site/security/badware/phishing-redirect/302)
- [ ] [HTTP Redirect to Phishing JS Redirector
(Indirect)](https://privacy-test-pages.site/security/badware/phishing-redirect/js)
- [ ] [HTTP Redirect to Phishing JS Redirector
(Direct)](https://privacy-test-pages.site/security/badware/phishing-redirect/js2)
- [ ] [HTTP Redirect to Phishing iFrame
Loader](https://privacy-test-pages.site/security/badware/phishing-redirect/iframe)
- [ ] [HTTP Redirect to Clean Meta Refresh
Redirector](https://privacy-test-pages.site/security/badware/phishing-redirect/meta)
- [ ] [HTTP Redirect to Flagged Meta Refresh
Redirector](https://privacy-test-pages.site/security/badware/phishing-redirect/meta2)

_Feature 2_

- [x] Open [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [x] Wait for the error page to show
- [ ] Check the globe icon is shown instead of privacy shield. Known
issue, will be addressed as a follow-up. See
https://app.asana.com/0/0/1209270355529416/f
- [x] Tap on the omnibar and load the same page again
- [x] Check the error is shown again

_Feature 3_
- [x] Open wikipedia
- [x] On the same tab, navigate to [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [x] Wait for the error page to show
- [x] Tap on the omnibar and load the same page again
- [x] Check the error is shown again

_Feature 4_
- [x] Open [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [x] Wait for the error page to show
- [x] Tap on the omnibar and navigate to a different site
- [x] Check navigation is performed normally

_Feature 5_
- [x] Open [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [x] Wait for the error page to show
- [x] Tap on "Leave This Site"
- [x] Check current tab is closed and a new one is opened

_Feature 6_
- [x] Open [Standard Phishing
Test](https://privacy-test-pages.site/security/badware/phishing.html)
- [x] Wait for the error page to show
- [x] Tap on "Advanced"
- [x] Tap on "Accept Risk and Visit Site"
- [x] Check the site is loaded 
- [x] Reload the site
- [x] Check it's loaded normally, no error page shown
- [x] Kill the app
- [x] Open it again and load the same site again
- [x] Check error page is now shown




### UI changes
| Before  | After |
| ------ | ----- |
!(Upload before screenshot)|(Upload after screenshot)|

---------

Co-authored-by: laghee <[email protected]>
  • Loading branch information
CrisBarreiro and laghee authored Jan 31, 2025
1 parent 5a3aa6e commit 0c0da0e
Show file tree
Hide file tree
Showing 21 changed files with 642 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ class BrowserTabViewModelTest {
toggleReports = mockToggleReports,
brokenSitePrompt = mockBrokenSitePrompt,
tabStatsBucketing = mockTabStatsBucketing,
maliciousSiteBlockerWebViewIntegration = mock(),
)

testee.loadData("abc", null, false, false)
Expand Down Expand Up @@ -5088,16 +5089,16 @@ class BrowserTabViewModelTest {
}

@Test
fun whenRecoveringFromSSLWarningPageAndBrowserShouldShowThenViewStatesUpdated() {
testee.recoverFromSSLWarningPage(true)
fun whenRecoveringFromWarningPageAndBrowserShouldShowThenViewStatesUpdated() {
testee.recoverFromWarningPage(true)

assertEquals(NONE, browserViewState().sslError)
assertEquals(true, browserViewState().browserShowing)
}

@Test
fun whenRecoveringFromSSLWarningPageAndBrowserShouldNotShowThenViewStatesUpdated() = runTest {
testee.recoverFromSSLWarningPage(false)
fun whenRecoveringFromWarningPageAndBrowserShouldNotShowThenViewStatesUpdated() = runTest {
testee.recoverFromWarningPage(false)

assertEquals(NONE, browserViewState().sslError)
assertEquals(false, browserViewState().browserShowing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import com.duckduckgo.app.browser.databinding.IncludeOmnibarToolbarMockupBinding
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.BOTTOM
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.TOP
import com.duckduckgo.app.browser.shortcut.ShortcutBuilder
import com.duckduckgo.app.browser.webview.RealMaliciousSiteBlockerWebViewIntegration
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.downloads.DownloadsScreens.DownloadsScreenNoParams
import com.duckduckgo.app.feedback.ui.common.FeedbackActivity
Expand Down Expand Up @@ -128,6 +129,9 @@ open class BrowserActivity : DuckDuckGoActivity() {
@Inject
lateinit var appBuildConfig: AppBuildConfig

@Inject
lateinit var maliciousSiteBlockerWebViewIntegration: RealMaliciousSiteBlockerWebViewIntegration

private val lastActiveTabs = TabList()

private var currentTab: BrowserTabFragment? = null
Expand Down
87 changes: 84 additions & 3 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ import com.duckduckgo.app.browser.applinks.AppLinksSnackBarConfigurator
import com.duckduckgo.app.browser.autocomplete.BrowserAutoCompleteSuggestionsAdapter
import com.duckduckgo.app.browser.autocomplete.SuggestionItemDecoration
import com.duckduckgo.app.browser.commands.Command
import com.duckduckgo.app.browser.commands.Command.OpenBrokenSiteLearnMore
import com.duckduckgo.app.browser.commands.Command.ReportBrokenSiteError
import com.duckduckgo.app.browser.commands.Command.ShowBackNavigationHistory
import com.duckduckgo.app.browser.commands.NavigationCommand
import com.duckduckgo.app.browser.cookies.ThirdPartyCookieManager
Expand Down Expand Up @@ -223,6 +225,7 @@ import com.duckduckgo.autofill.api.emailprotection.EmailInjector
import com.duckduckgo.browser.api.WebViewVersionProvider
import com.duckduckgo.browser.api.brokensite.BrokenSiteData
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.RELOAD_THREE_TIMES_WITHIN_20_SECONDS
import com.duckduckgo.browser.api.ui.BrowserScreens.WebViewActivityWithParams
import com.duckduckgo.common.ui.DuckDuckGoFragment
import com.duckduckgo.common.ui.store.BrowserAppTheme
import com.duckduckgo.common.ui.view.DaxDialog
Expand Down Expand Up @@ -579,6 +582,9 @@ class BrowserTabFragment :
private val errorView
get() = binding.includeErrorView

private val maliciousWarningView
get() = binding.maliciousSiteWarningLayout

private val sslErrorView
get() = binding.sslErrorWarningLayout

Expand Down Expand Up @@ -1301,6 +1307,7 @@ class BrowserTabFragment :
webView?.hide()
errorView.errorLayout.gone()
sslErrorView.gone()
maliciousWarningView.gone()
}

private fun showBrowser() {
Expand All @@ -1312,6 +1319,7 @@ class BrowserTabFragment :
webView?.onResume()
errorView.errorLayout.gone()
sslErrorView.gone()
maliciousWarningView.gone()
omnibar.setViewMode(Omnibar.ViewMode.Browser(viewModel.url))
}

Expand All @@ -1323,6 +1331,7 @@ class BrowserTabFragment :
newBrowserTab.newTabLayout.gone()
newBrowserTab.newTabContainerLayout.gone()
sslErrorView.gone()
maliciousWarningView.gone()
omnibar.setViewMode(Omnibar.ViewMode.Error)
webView?.onPause()
webView?.hide()
Expand All @@ -1335,6 +1344,71 @@ class BrowserTabFragment :
errorView.errorLayout.show()
}

private fun showMaliciousWarning(url: Uri) {
webViewContainer.gone()
newBrowserTab.newTabLayout.gone()
newBrowserTab.newTabContainerLayout.gone()
sslErrorView.gone()
errorView.errorLayout.gone()
binding.browserLayout.gone()
omnibar.setViewMode(ViewMode.MaliciousSiteWarning)
webView?.onPause()
webView?.hide()
webView?.stopLoading()
maliciousWarningView.bind { action ->
viewModel.onMaliciousSiteUserAction(action, url)
}
maliciousWarningView.show()
binding.focusDummy.requestFocus()
}

private fun hideMaliciousWarning() {
val navList = webView?.safeCopyBackForwardList()
val currentIndex = navList?.currentIndex ?: 0

if (currentIndex >= 0) {
Timber.d("MaliciousSite: hiding warning page and triggering a reload of the previous")
viewModel.recoverFromWarningPage(true)
refresh()
} else {
Timber.d("MaliciousSite: no previous page to load, showing home")
viewModel.recoverFromWarningPage(false)
renderer.showNewTab()
maliciousWarningView.gone()
}
}

private fun onEscapeMaliciousSite() {
maliciousWarningView.gone()
viewModel.openNewTab()
viewModel.closeCurrentTab()
}

private fun onBypassMaliciousWarning(url: Uri) {
showBrowser()
webView?.loadUrl(url.toString())
}

private fun openBrokenSiteLearnMore(url: String) {
globalActivityStarter.start(
this.requireContext(),
WebViewActivityWithParams(
url = url,
getString(R.string.maliciousSiteLearnMoreTitle),
),
)
}

private fun openBrokenSiteReportError(url: String) {
globalActivityStarter.start(
this.requireContext(),
WebViewActivityWithParams(
url = url,
getString(R.string.maliciousSiteReportErrorTitle),
),
)
}

private fun showSSLWarning(
handler: SslErrorHandler,
errorResponse: SslErrorResponse,
Expand All @@ -1347,6 +1421,7 @@ class BrowserTabFragment :
omnibar.setViewMode(Omnibar.ViewMode.SSLWarning)
errorView.errorLayout.gone()
binding.browserLayout.gone()
maliciousWarningView.gone()
sslErrorView.bind(handler, errorResponse) { action ->
viewModel.onSSLCertificateWarningAction(action, errorResponse.url)
}
Expand All @@ -1359,11 +1434,11 @@ class BrowserTabFragment :

if (currentIndex >= 0) {
Timber.d("SSLError: hiding warning page and triggering a reload of the previous")
viewModel.recoverFromSSLWarningPage(true)
viewModel.recoverFromWarningPage(true)
refresh()
} else {
Timber.d("SSLError: no previous page to load, showing home")
viewModel.recoverFromSSLWarningPage(false)
viewModel.recoverFromWarningPage(false)
}
}

Expand Down Expand Up @@ -1677,6 +1752,12 @@ class BrowserTabFragment :
)

is Command.WebViewError -> showError(it.errorType, it.url)
is Command.ShowWarningMaliciousSite -> showMaliciousWarning(it.url)
is Command.HideWarningMaliciousSite -> hideMaliciousWarning()
is Command.EscapeMaliciousSite -> onEscapeMaliciousSite()
is Command.BypassMaliciousSiteWarning -> onBypassMaliciousWarning(it.url)
is OpenBrokenSiteLearnMore -> openBrokenSiteLearnMore(it.url)
is ReportBrokenSiteError -> openBrokenSiteReportError(it.url)
is Command.SendResponseToJs -> contentScopeScripts.onResponse(it.data)
is Command.SendResponseToDuckPlayer -> duckPlayerScripts.onResponse(it.data)
is Command.WebShareRequest -> webShareRequest.launch(it.data)
Expand Down Expand Up @@ -3940,7 +4021,7 @@ class BrowserTabFragment :
viewModel.onCtaShown()
}

private fun showNewTab() {
fun showNewTab() {
newTabPageProvider.provideNewTabPageVersion().onEach { newTabPage ->
if (newBrowserTab.newTabContainerLayout.childCount == 0) {
newBrowserTab.newTabContainerLayout.addView(
Expand Down
Loading

0 comments on commit 0c0da0e

Please sign in to comment.