From 688d1f236598a1ec3b548e324265c589a9557b87 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Tue, 30 Jul 2019 13:45:07 +0200 Subject: [PATCH] Closes #3944: AppLinksFeature: Do not open third-party apps for URLs with javascript scheme. --- .../feature/app/links/AppLinksFeature.kt | 17 +++++--- .../feature/app/links/AppLinksFeatureTest.kt | 39 +++++++++++++++++-- docs/changelog.md | 3 ++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt b/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt index eff359ceade..51eb60a21c0 100644 --- a/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt +++ b/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt @@ -38,13 +38,19 @@ import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes * A [Boolean] flag is provided at construction to allow the feature and use cases to be landed without * adjoining UI. The UI will be activated in https://github.com/mozilla-mobile/android-components/issues/2974 * and https://github.com/mozilla-mobile/android-components/issues/2975. + * + * @param alwaysAllowedSchemes List of schemes that will always be allowed to be opened in a third-party + * app even if [interceptLinkClicks] is `false`. + * @param alwaysDeniedSchemes List of schemes that will never be opened in a third-party app even if + * [interceptLinkClicks] is `true`. */ class AppLinksFeature( private val context: Context, private val sessionManager: SessionManager, private val sessionId: String? = null, private val interceptLinkClicks: Boolean = false, - private val whitelistedSchemes: Set = setOf("mailto", "market", "sms", "tel"), + private val alwaysAllowedSchemes: Set = setOf("mailto", "market", "sms", "tel"), + private val alwaysDeniedSchemes: Set = setOf("javascript"), private val fragmentManager: FragmentManager? = null, private var dialog: RedirectDialogFragment = SimpleRedirectDialogFragment.newInstance(), private val useCases: AppLinksUseCases = AppLinksUseCases(context) @@ -66,7 +72,7 @@ class AppLinksFeature( * Starts observing app links on the selected session. */ override fun start() { - if (interceptLinkClicks || whitelistedSchemes.isNotEmpty()) { + if (interceptLinkClicks || alwaysAllowedSchemes.isNotEmpty()) { observer.observeIdOrSelected(sessionId) } findPreviousDialogFragment()?.let { @@ -75,7 +81,7 @@ class AppLinksFeature( } override fun stop() { - if (interceptLinkClicks || whitelistedSchemes.isNotEmpty()) { + if (interceptLinkClicks || alwaysAllowedSchemes.isNotEmpty()) { observer.stop() } } @@ -107,8 +113,9 @@ class AppLinksFeature( return } - redirect.appIntent?.data?.scheme?.let { - if (!interceptLinkClicks && !whitelistedSchemes.contains(it)) { + redirect.appIntent?.data?.scheme?.let { scheme -> + if ((!interceptLinkClicks && !alwaysAllowedSchemes.contains(scheme)) || + alwaysDeniedSchemes.contains(scheme)) { return } } diff --git a/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt b/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt index 25ce7b61a97..b677822d5c3 100644 --- a/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt +++ b/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt @@ -8,6 +8,7 @@ package mozilla.components.feature.app.links import android.content.Context import android.content.Intent +import android.net.Uri import androidx.fragment.app.FragmentManager import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.browser.session.Session @@ -15,11 +16,14 @@ import mozilla.components.browser.session.SessionManager import mozilla.components.concept.engine.Engine import mozilla.components.support.test.any import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.whenever import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mockito.`when` +import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions @@ -95,7 +99,7 @@ class AppLinksFeatureTest { mockSessionManager, useCases = mockUseCases, interceptLinkClicks = false, - whitelistedSchemes = setOf() + alwaysAllowedSchemes = setOf() ) subject.start() @@ -128,7 +132,7 @@ class AppLinksFeatureTest { mockContext, mockSessionManager, interceptLinkClicks = false, - whitelistedSchemes = setOf(whitelistedScheme), + alwaysAllowedSchemes = setOf(whitelistedScheme), useCases = mockUseCases ) @@ -178,7 +182,7 @@ class AppLinksFeatureTest { sessionManager = mockSessionManager, useCases = mockUseCases, interceptLinkClicks = false, - whitelistedSchemes = setOf("whitelisted") + alwaysAllowedSchemes = setOf("whitelisted") ) feature.start() @@ -298,4 +302,33 @@ class AppLinksFeatureTest { verifyNoMoreInteractions(mockDialog) verify(mockOpenRedirect).invoke(any()) } + + @Test + fun `an external app is not opened for URLs with javascript scheme`() { + val javascriptUri = "javascript:;" + + val openAppUseCase: AppLinksUseCases.OpenAppLinkRedirect = mock() + val useCases: AppLinksUseCases = mock() + whenever(useCases.openAppLink).thenReturn(openAppUseCase) + + val feature = AppLinksFeature( + testContext, + sessionManager = mock(), + interceptLinkClicks = true, + useCases = useCases + ) + + val intent = Intent().apply { + data = Uri.parse(javascriptUri) + } + + val redirect = AppLinkRedirect( + intent, + javascriptUri, + false) + + feature.handleRedirect(redirect, Session("https://www.amazon.ca")) + + verify(openAppUseCase, never()).invoke(redirect) + } } diff --git a/docs/changelog.md b/docs/changelog.md index 14146f71e49..502aacc059b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,9 @@ permalink: /changelog/ * [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt) * [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt) +* **feature-app-links** + * Fixed [#3944](https://github.com/mozilla-mobile/android-components/issues/3944) causing third-party apps being opened when links with a `javascript` scheme are clicked. + # 6.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v5.0.0...v6.0.0)