@@ -2,6 +2,9 @@ package dev.hotwire.navigation.navigator
22
33import android.os.Bundle
44import android.view.View
5+ import androidx.annotation.VisibleForTesting
6+ import androidx.annotation.VisibleForTesting.Companion.PROTECTED
7+ import androidx.core.net.toUri
58import androidx.fragment.app.Fragment
69import androidx.fragment.app.FragmentManager
710import androidx.fragment.app.FragmentOnAttachListener
@@ -11,6 +14,9 @@ import dev.hotwire.core.config.Hotwire
1114import dev.hotwire.navigation.activities.HotwireActivity
1215import dev.hotwire.navigation.config.HotwireNavigation
1316
17+ internal const val DEEPLINK_EXTRAS_KEY = " android-support-nav:controller:deepLinkExtras"
18+ internal const val LOCATION_KEY = " location"
19+
1420open class NavigatorHost : NavHostFragment (), FragmentOnAttachListener {
1521 internal lateinit var activity: HotwireActivity
1622 lateinit var navigator: Navigator
@@ -51,6 +57,8 @@ open class NavigatorHost : NavHostFragment(), FragmentOnAttachListener {
5157 }
5258
5359 internal fun initControllerGraph () {
60+ ensureDeeplinkStartLocationValid()
61+
5462 navController.apply {
5563 graph = NavigatorGraphBuilder (
5664 navigatorName = configuration.name,
@@ -63,6 +71,26 @@ open class NavigatorHost : NavHostFragment(), FragmentOnAttachListener {
6371 }
6472 }
6573
74+ /* *
75+ * Google's Navigation library automatically navigates to deep links provided in the
76+ * Activity's Intent. This exposes a vulnerability for malicious Intents to open an arbitrary
77+ * webpage outside of the app's domain, allowing javascript injection on the page. Ensure
78+ * that deep link intents always match the app's domain.
79+ */
80+ @VisibleForTesting(otherwise = PROTECTED )
81+ fun ensureDeeplinkStartLocationValid () {
82+ val extrasBundle = activity.intent.extras?.getBundle(DEEPLINK_EXTRAS_KEY ) ? : return
83+ val startLocation = extrasBundle.getString(LOCATION_KEY ) ? : return
84+
85+ val deepLinkStartUri = startLocation.toUri()
86+ val configStartUri = configuration.startLocation.toUri()
87+
88+ if (deepLinkStartUri.host != configStartUri.host) {
89+ extrasBundle.putString(LOCATION_KEY , configuration.startLocation)
90+ activity.intent.putExtra(DEEPLINK_EXTRAS_KEY , extrasBundle)
91+ }
92+ }
93+
6694 private val configuration get() = activity.navigatorConfigurations().firstOrNull {
6795 id == it.navigatorHostId
6896 } ? : throw IllegalStateException (" No configuration found for NavigatorHost" )
0 commit comments