From f3e5e87c3d44e771489b7eab46d874723f19f7d3 Mon Sep 17 00:00:00 2001 From: Mama1emon Date: Mon, 23 Dec 2024 14:19:34 +0400 Subject: [PATCH] AND-9449 [Security] Add validation of external urls for WebView Signed-off-by: Mama1emon --- common/build.gradle.kts | 24 ++++++++++++- .../tangem/common/module/ModuleErrorCode.kt | 0 .../com/tangem/common/module/ModuleMessage.kt | 0 .../tangem/common/uri/ExternalUrlValidator.kt | 31 ++++++++++++++++ .../common/uri/ExternalUrlValidatorTest.kt | 36 +++++++++++++++++++ 5 files changed, 90 insertions(+), 1 deletion(-) rename common/src/main/{java => kotlin}/com/tangem/common/module/ModuleErrorCode.kt (100%) rename common/src/main/{java => kotlin}/com/tangem/common/module/ModuleMessage.kt (100%) create mode 100644 common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt create mode 100644 common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 2a800ccc03..f9dd95ce21 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,4 +1,26 @@ plugins { - alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) id("configuration") } + +android { + namespace = "com.tangem.common" +} + +dependencies { + + // region Firebase libraries + implementation(platform(deps.firebase.bom)) + implementation(deps.firebase.analytics) + implementation(deps.firebase.crashlytics) + implementation(deps.firebase.messaging) + // end + + implementation(deps.timber) + + implementation(deps.arrow.core) + + implementation(deps.test.junit) + implementation(deps.test.truth) +} diff --git a/common/src/main/java/com/tangem/common/module/ModuleErrorCode.kt b/common/src/main/kotlin/com/tangem/common/module/ModuleErrorCode.kt similarity index 100% rename from common/src/main/java/com/tangem/common/module/ModuleErrorCode.kt rename to common/src/main/kotlin/com/tangem/common/module/ModuleErrorCode.kt diff --git a/common/src/main/java/com/tangem/common/module/ModuleMessage.kt b/common/src/main/kotlin/com/tangem/common/module/ModuleMessage.kt similarity index 100% rename from common/src/main/java/com/tangem/common/module/ModuleMessage.kt rename to common/src/main/kotlin/com/tangem/common/module/ModuleMessage.kt diff --git a/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt b/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt new file mode 100644 index 0000000000..1ddea7ccfd --- /dev/null +++ b/common/src/main/kotlin/com/tangem/common/uri/ExternalUrlValidator.kt @@ -0,0 +1,31 @@ +package com.tangem.common.uri + +import com.google.firebase.crashlytics.FirebaseCrashlytics +import timber.log.Timber +import java.net.URI + +/** + * External url validator + * + * @author Andrew Khokhlov on 23/12/2024 + */ +object ExternalUrlValidator { + + private val trustedHost: List = listOf("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 + } catch (e: Exception) { + val exception = IllegalStateException("Failed to validate URI: $externalUri", e) + + Timber.e(exception) + FirebaseCrashlytics.getInstance().recordException(exception) + + false + } + } +} diff --git a/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt b/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt new file mode 100644 index 0000000000..2d59b74b83 --- /dev/null +++ b/common/src/testDebug/kotlin/com/tangem/common/uri/ExternalUrlValidatorTest.kt @@ -0,0 +1,36 @@ +package com.tangem.common.uri + +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * @author Andrew Khokhlov on 23/12/2024 + */ +@RunWith(Parameterized::class) +class ExternalUrlValidatorTest(private val model: Model) { + + @Test + fun test() { + val actual = ExternalUrlValidator.isUriTrusted(externalUri = model.url) + + Truth.assertThat(actual).isEqualTo(model.expected) + } + + companion object { + + @JvmStatic + @Parameterized.Parameters + fun data(): Collection = listOf( + Model(url = "https://tangem.com", expected = true), + Model(url = "https://tange.com", expected = false), + Model(url = "https://fake.tangem.com", expected = false), + Model(url = "http://tangem.com", expected = false), + Model(url = "http://tandem.com", expected = false), + Model(url = "adawdawdassdw", expected = false), + ) + + data class Model(val url: String, val expected: Boolean) + } +} \ No newline at end of file