diff --git a/app/src/main/java/com/tangem/tap/common/deeplink/DefaultDeeplinkLauncher.kt b/app/src/main/java/com/tangem/tap/common/deeplink/DefaultDeeplinkLauncher.kt index ce74ae1064..0b6f9c503e 100644 --- a/app/src/main/java/com/tangem/tap/common/deeplink/DefaultDeeplinkLauncher.kt +++ b/app/src/main/java/com/tangem/tap/common/deeplink/DefaultDeeplinkLauncher.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.net.Uri import androidx.core.net.toUri import com.tangem.common.routing.DeepLinkScheme +import com.tangem.common.uri.ExternalUrlValidator import com.tangem.core.navigation.deeplink.DeeplinkLauncher import com.tangem.core.navigation.url.UrlOpener import com.tangem.utils.logging.TangemLogger @@ -24,7 +25,18 @@ internal class DefaultDeeplinkLauncher( DeepLinkScheme.Tangem.scheme, DeepLinkScheme.WalletConnect.scheme, -> launchDeepLink(deeplinkUri) - DeepLinkScheme.Https.scheme -> launchDeeplinkOrOpenBrowser(deeplinkUri, link) + DeepLinkScheme.Https.scheme -> { + if (ExternalUrlValidator.isUriTrusted(link)) { + launchDeeplinkOrOpenBrowser(deeplinkUri, link) + } else { + TangemLogger.i( + """ + Untrusted HTTPS link dropped + |- Received URI: $deeplinkUri + """.trimIndent(), + ) + } + } else -> { TangemLogger.i( """ diff --git a/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt b/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt index 49785fb80e..467a7493f0 100644 --- a/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt +++ b/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt @@ -11,14 +11,21 @@ import java.net.URI */ object ExternalUrlValidator { - private val trustedHost: List = listOf("tangem.com") + private val trustedHosts: Set = setOf( + "tangem.com", + "www.tangem.com", + "buy.tangem.com", + "app.tangem.com", + "tangem.surveysparrow.com", + "feedback.tangem.com", + ) /** Check if [externalUri] is trusted */ fun isUriTrusted(externalUri: String): Boolean { return try { val uri = URI.create(externalUri) - uri.scheme == "https" && uri.host in trustedHost + uri.scheme == "https" && uri.host in trustedHosts } catch (e: Exception) { val exception = IllegalStateException("Failed to validate URI: $externalUri", e) diff --git a/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt b/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt index f03d64dde6..ab2e743adf 100644 --- a/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt +++ b/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt @@ -23,11 +23,32 @@ class ExternalUrlValidatorTest(private val model: Model) { @JvmStatic @Parameterized.Parameters fun data(): Collection = listOf( + // Trusted hosts — exact match Model(url = "https://tangem.com", expected = true), - Model(url = "https://tange.com", expected = false), + Model(url = "https://tangem.com/pricing/?promocode=tgapp20ups", expected = true), + Model(url = "https://www.tangem.com", expected = true), + Model(url = "https://app.tangem.com", expected = true), + Model(url = "https://buy.tangem.com/?promocode=NEWINAPP", expected = true), + Model(url = "https://feedback.tangem.com", expected = true), + Model(url = "https://tangem.surveysparrow.com/s/tangem-pay/tt-F8XXH", expected = true), + // Subdomains not on the list + Model(url = "https://express.tangem.com/v1/", expected = false), Model(url = "https://fake.tangem.com", expected = false), + Model(url = "https://join.tangem.com", expected = false), + // Sibling hosts on the same registrable parent + Model(url = "https://surveysparrow.com", expected = false), + Model(url = "https://fake.surveysparrow.com", expected = false), + // Suffix-injection attempts + Model(url = "https://tangem.com.attacker.com", expected = false), + Model(url = "https://faketangem.com", expected = false), + Model(url = "https://buy.tangem.com.attacker.com", expected = false), + // Wrong scheme Model(url = "http://tangem.com", expected = false), + Model(url = "http://buy.tangem.com", expected = false), + // Typos + Model(url = "https://tange.com", expected = false), Model(url = "http://tandem.com", expected = false), + // Garbage Model(url = "adawdawdassdw", expected = false), )