From fd1bc63f8869574a00bc1029ce08ea577e6beb7a Mon Sep 17 00:00:00 2001 From: Kate Manning <20972610+laghee@users.noreply.github.com> Date: Sun, 9 Feb 2025 14:49:12 -0800 Subject: [PATCH] Eliminate preview for tabs showing malicious site warning and add error page pixels (#5602) Task/Issue URL: https://app.asana.com/0/1201870266890790/1209289574297311/f ### Description Don't show preview of blocked page in tab switcher when malicious site warning has been triggered ### Steps to test this PR _Block tab previews for warning page_ - [ ] Open new tab with https://privacy-test-pages.site/security/badware/malware.html - [ ] When warning shows, tap to show all tabs - [ ] Check that there is no bitmap preview for that tab --- .../app/browser/BrowserTabFragment.kt | 10 +++++++++- .../app/browser/BrowserTabViewModel.kt | 17 +++++++++++++++-- .../app/browser/WebViewClientListener.kt | 2 +- .../app/browser/WebViewRequestInterceptor.kt | 8 ++++---- .../global/api/PixelParamRemovalInterceptor.kt | 2 ++ .../com/duckduckgo/app/pixels/AppPixelName.kt | 2 ++ 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index e3cf59048253..ebac07a54bd7 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -874,7 +874,9 @@ class BrowserTabFragment : object : DefaultLifecycleObserver { override fun onStop(owner: LifecycleOwner) { if (isVisible) { - updateOrDeleteWebViewPreview() + if (viewModel.browserViewState.value?.maliciousSiteDetected != true) { + updateOrDeleteWebViewPreview() + } } } }, @@ -1400,6 +1402,10 @@ class BrowserTabFragment : maliciousWarningView.bind(feed) { action -> viewModel.onMaliciousSiteUserAction(action, url, feed, isActiveCustomTab()) } + viewModel.deleteTabPreview(tabId) + lifecycleScope.launch(dispatchers.main()) { + viewModel.updateTabTitle(tabId, newTitle = SITE_SECURITY_WARNING) + } maliciousWarningView.show() binding.focusDummy.requestFocus() } @@ -3654,6 +3660,8 @@ class BrowserTabFragment : private const val AUTOCOMPLETE_PADDING_DP = 6 + private const val SITE_SECURITY_WARNING = "Warning: Security Risk" + fun newInstance( tabId: String, query: String? = null, diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 88c557c0b0bd..3cbaee86e372 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -1882,6 +1882,8 @@ class BrowserTabViewModel @Inject constructor( ) { when (action) { LeaveSite -> { + val params = mapOf(CATEGORY_KEY to feed.name) + pixel.fire(AppPixelName.MALICIOUS_SITE_PROTECTION_VISIT_SITE, params) if (activeCustomTab) { command.postValue(CloseCustomTab) } else { @@ -3197,13 +3199,14 @@ class BrowserTabViewModel @Inject constructor( command.postValue(WebViewError(errorType, url)) } - override fun onReceivedMaliciousSiteWarning(url: Uri, feed: Feed, exempted: Boolean) { - // TODO (cbarreiro): Fire pixel + override fun onReceivedMaliciousSiteWarning(url: Uri, feed: Feed, exempted: Boolean, clientSideHit: Boolean) { site?.maliciousSiteStatus = when (feed) { MALWARE -> MaliciousSiteStatus.MALWARE PHISHING -> MaliciousSiteStatus.PHISHING } if (!exempted) { + val params = mapOf(CATEGORY_KEY to feed.name, CLIENT_SIDE_HIT_KEY to clientSideHit.toString()) + pixel.fire(AppPixelName.MALICIOUS_SITE_PROTECTION_ERROR_SHOWN, params) loadingViewState.postValue( currentLoadingViewState().copy(isLoading = false, progress = 100, url = url.toString()), ) @@ -3218,6 +3221,13 @@ class BrowserTabViewModel @Inject constructor( } } + suspend fun updateTabTitle(tabId: String, newTitle: String) { + getSite()?.let { site -> + site.title = newTitle + tabRepository.update(tabId, site) + } + } + override fun recordErrorCode( error: String, url: String, @@ -3822,6 +3832,9 @@ class BrowserTabViewModel @Inject constructor( private const val HTTP_STATUS_CODE_CLIENT_ERROR_PREFIX = 4 // 4xx, client error status code prefix private const val HTTP_STATUS_CODE_SERVER_ERROR_PREFIX = 5 // 5xx, server error status code prefix + private const val CATEGORY_KEY = "category" + private const val CLIENT_SIDE_HIT_KEY = "clientSideHit" + // https://www.iso.org/iso-3166-country-codes.html private val PRINT_LETTER_FORMAT_COUNTRIES_ISO3166_2 = setOf( Locale.US.country, diff --git a/app/src/main/java/com/duckduckgo/app/browser/WebViewClientListener.kt b/app/src/main/java/com/duckduckgo/app/browser/WebViewClientListener.kt index 3f16933bdc57..050f8ea83919 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/WebViewClientListener.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/WebViewClientListener.kt @@ -96,7 +96,7 @@ interface WebViewClientListener { fun linkOpenedInNewTab(): Boolean fun isActiveTab(): Boolean fun onReceivedError(errorType: WebViewErrorResponse, url: String) - fun onReceivedMaliciousSiteWarning(url: Uri, feed: Feed, exempted: Boolean) + fun onReceivedMaliciousSiteWarning(url: Uri, feed: Feed, exempted: Boolean, clientSideHit: Boolean) fun recordErrorCode(error: String, url: String) fun recordHttpErrorCode(statusCode: Int, url: String) diff --git a/app/src/main/java/com/duckduckgo/app/browser/WebViewRequestInterceptor.kt b/app/src/main/java/com/duckduckgo/app/browser/WebViewRequestInterceptor.kt index d33c8c40e2b7..58094edab3c1 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/WebViewRequestInterceptor.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/WebViewRequestInterceptor.kt @@ -205,7 +205,7 @@ class WebViewRequestInterceptor( when (result) { WaitForConfirmation, Safe -> return false is MaliciousSite -> { - handleSiteBlocked(webViewClientListener, url, result.feed, result.exempted) + handleSiteBlocked(webViewClientListener, url, result.feed, result.exempted, clientSideHit = true) return !result.exempted } } @@ -220,12 +220,12 @@ class WebViewRequestInterceptor( /* * If the site is exempted, we'll never get here, as we won't call isMalicious */ - handleSiteBlocked(webViewClientListener, url, isMalicious.feed, false) + handleSiteBlocked(webViewClientListener, url, isMalicious.feed, exempted = false, clientSideHit = false) } } - private fun handleSiteBlocked(webViewClientListener: WebViewClientListener?, url: Uri?, feed: Feed, exempted: Boolean) { - url?.let { webViewClientListener?.onReceivedMaliciousSiteWarning(it, feed, exempted) } + private fun handleSiteBlocked(webViewClientListener: WebViewClientListener?, url: Uri?, feed: Feed, exempted: Boolean, clientSideHit: Boolean) { + url?.let { webViewClientListener?.onReceivedMaliciousSiteWarning(it, feed, exempted, clientSideHit) } } override fun addExemptedMaliciousSite(url: Uri, feed: Feed) { diff --git a/app/src/main/java/com/duckduckgo/app/global/api/PixelParamRemovalInterceptor.kt b/app/src/main/java/com/duckduckgo/app/global/api/PixelParamRemovalInterceptor.kt index d2c748a31be0..761108a2fbfe 100644 --- a/app/src/main/java/com/duckduckgo/app/global/api/PixelParamRemovalInterceptor.kt +++ b/app/src/main/java/com/duckduckgo/app/global/api/PixelParamRemovalInterceptor.kt @@ -95,6 +95,8 @@ object PixelInterceptorPixelsRequiringDataCleaning : PixelParamRemovalPlugin { SITE_NOT_WORKING_WEBSITE_BROKEN.pixelName to PixelParameter.removeAtb(), AppPixelName.APP_VERSION_AT_SEARCH_TIME.pixelName to PixelParameter.removeAll(), AppPixelName.MALICIOUS_SITE_PROTECTION_SETTING_TOGGLED.pixelName to PixelParameter.removeAtb(), + AppPixelName.MALICIOUS_SITE_PROTECTION_VISIT_SITE.pixelName to PixelParameter.removeAtb(), + AppPixelName.MALICIOUS_SITE_PROTECTION_ERROR_SHOWN.pixelName to PixelParameter.removeAtb(), AppPixelName.SET_AS_DEFAULT_PROMPT_IMPRESSION.pixelName to PixelParameter.removeAll(), AppPixelName.SET_AS_DEFAULT_PROMPT_CLICK.pixelName to PixelParameter.removeAll(), AppPixelName.SET_AS_DEFAULT_PROMPT_DISMISSED.pixelName to PixelParameter.removeAll(), diff --git a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt index 0b5349bd275c..ff0d908c5c85 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt @@ -364,6 +364,8 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { DUCK_PLAYER_SETTING_ALWAYS_DUCK_PLAYER("duckplayer_setting_always_duck-player"), MALICIOUS_SITE_PROTECTION_SETTING_TOGGLED("m_malicious-site-protection_feature-toggled"), + MALICIOUS_SITE_PROTECTION_VISIT_SITE("m_malicious-site-protection_visit-site"), + MALICIOUS_SITE_PROTECTION_ERROR_SHOWN("m_malicious-site-protection_error-page-shown"), ADD_BOOKMARK_CONFIRM_EDITED("m_add_bookmark_confirm_edit"),