Updated on 2026-08-14
This commit is contained in:
parent
a6bf9d2e2f
commit
e181c5a1a7
3 changed files with 44 additions and 4 deletions
|
|
@ -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(
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,14 +11,21 @@ import java.net.URI
|
|||
*/
|
||||
object ExternalUrlValidator {
|
||||
|
||||
private val trustedHost: List<String> = listOf("tangem.com")
|
||||
private val trustedHosts: Set<String> = 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,32 @@ class ExternalUrlValidatorTest(private val model: Model) {
|
|||
@JvmStatic
|
||||
@Parameterized.Parameters
|
||||
fun data(): Collection<Model> = 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),
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue