diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 0a2434d93d..4424b32de5 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 0a2434d93d67bec1804e3e827b3795a62f961d90 +Subproject commit 4424b32de5a80305cd3ca6eca219b09a16e940c5 diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index 52c92e43e6..ed7b6b5621 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -9,6 +9,7 @@ import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechApiV2 import com.tangem.datasource.api.tangemTech.TangemTechServiceApi +import com.tangem.datasource.utils.RequestHeader import com.tangem.datasource.utils.RequestHeader.* import com.tangem.datasource.utils.addHeaders import com.tangem.datasource.utils.addLoggers @@ -118,11 +119,12 @@ class NetworkModule { appVersionProvider: AppVersionProvider, ): TangemTechServiceApi { return provideTangemTechApiInternal( - moshi, - context, - appVersionProvider, - PROD_V1_TANGEM_TECH_BASE_URL, + moshi = moshi, + context = context, + appVersionProvider = appVersionProvider, + baseUrl = PROD_V1_TANGEM_TECH_BASE_URL, timeoutSeconds = TANGEM_TECH_SERVICE_TIMEOUT_SECONDS, + requestHeaders = listOf(AppVersionPlatformHeaders(appVersionProvider)), ) } @@ -132,6 +134,7 @@ class NetworkModule { appVersionProvider: AppVersionProvider, baseUrl: String, timeoutSeconds: Long? = null, + requestHeaders: List = listOf(CacheControlHeader, AppVersionPlatformHeaders(appVersionProvider)), ): T { val client = OkHttpClient.Builder() .let { builder -> @@ -142,8 +145,7 @@ class NetworkModule { } } .addHeaders( - CacheControlHeader, - AppVersionPlatformHeaders(appVersionProvider), + *requestHeaders.toTypedArray(), // TODO("refactor header init") get auth data after biometric auth to avoid race condition // AuthenticationHeader(authProvider), ) diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index b98c624a8a..bf362e041c 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -13,7 +13,7 @@ }, { "name": "LOCAL_USER_LOGS_ENABLED", - "version": "5.11.0" + "version": "5.13.0" }, { "name": "GENERATE_XPUB_ENABLED", @@ -29,7 +29,7 @@ }, { "name": "CARDANO_TOKENS_SUPPORT_ENABLED", - "version": "5.11.0" + "version": "5.12.0" }, { "name": "STAKING_ENABLED", diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt index 310ec39f9a..b56ad1dd18 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoader.kt @@ -1,10 +1,12 @@ package com.tangem.blockchainsdk.loader +import androidx.core.util.PatternsCompat import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchainsdk.BlockchainProvidersResponse import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.tangemTech.TangemTechServiceApi import com.tangem.datasource.asset.loader.AssetLoader +import com.tangem.datasource.config.models.ProviderModel import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.runCatching import timber.log.Timber @@ -68,7 +70,15 @@ internal class BlockchainProvidersResponseLoader @Inject constructor( * * local + remote // { a = 1, e = 4, f = 5, b = 2, c = 3 } */ - val result = local + remote.filterValues { it.isNotEmpty() } + val remoteWithoutInvalidProviders = remote + .mapValues { + it.value + .filterUnsupportedProviders() + .filterInvalidProviders() + } + .filterValues { it.isNotEmpty() } + + val result = local + remoteWithoutInvalidProviders if (result != remote) { val missingBlockchains = result.keys - remote.keys @@ -80,6 +90,23 @@ internal class BlockchainProvidersResponseLoader @Inject constructor( return result } + private fun List.filterUnsupportedProviders() = filter { it !is ProviderModel.UnsupportedType } + + private fun List.filterInvalidProviders() = mapNotNull { provider -> + if (provider is ProviderModel.Public) { + if (isValidUrl(provider.url)) provider else null + } else { + provider + } + } + + private fun isValidUrl(url: String): Boolean { + val forbiddenScheme = forbiddenSchemes.firstOrNull { url.startsWith(prefix = it) } + val inputUrl = if (forbiddenScheme != null) url.substringAfter(forbiddenScheme) else url + + return PatternsCompat.WEB_URL.matcher(inputUrl).matches() + } + private fun recordException(missingBlockchains: Set) { val exception = IllegalStateException( "Remote config does not contain required blockchains or providers information: " + @@ -93,5 +120,7 @@ internal class BlockchainProvidersResponseLoader @Inject constructor( private companion object { const val PROVIDER_TYPES_FILE_NAME = "tangem-app-config/providers_order" + + val forbiddenSchemes = listOf("wss://") } } \ No newline at end of file diff --git a/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt b/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt index 8824f5ec24..ed55c6cfed 100644 --- a/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt +++ b/libs/blockchain-sdk/src/test/java/com/tangem/blockchainsdk/loader/BlockchainProvidersResponseLoaderTest.kt @@ -218,6 +218,67 @@ internal class BlockchainProvidersResponseLoaderTest { Truth.assertThat(actual).isEqualTo(remoteProviders) } + @Test + fun test_load_if_remote_config_contains_unsupported_types() = runTest { + val ethProvider = "ethereum" to listOf(ProviderModel.UnsupportedType, ProviderModel.Private(name = "nownodes")) + val remoteProvidersWithEth = remoteProviders + ethProvider + + everyGettingLocalConfig(json = localProvidersJson) returns localProviders + everyGettingRemoteConfig() returns remoteProvidersWithEth + everyCrashlyticsRecording() just Runs + + val actual = loader.load() + + coVerifyOrder { + assetReader.read(LOCAL_CONFIG_FILE_NAME) + moshi.adapter() + jsonAdapter.fromJson(localProvidersJson) + authProvider.getCardPublicKey() + authProvider.getCardId() + tangemTechServiceApi.getBlockchainProviders( + cardPublicKey = DEFAULT_CARD_PUBLIC_KEY, + cardId = DEFAULT_CARD_ID, + ) + firebaseCrashlytics.recordException(any()) + } + + val expected = remoteProviders + ("ethereum" to listOf(ProviderModel.Private(name = "nownodes"))) + + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun test_load_if_remote_config_contains_invalid_public_providers() = runTest { + val localEthProvider = "ethereum" to listOf(ProviderModel.Private(name = "nownodes")) + val localProvidersWithEth = localProviders + localEthProvider + + val remoteEthProvider = "ethereum" to listOf(ProviderModel.UnsupportedType, ProviderModel.Public("adbw2138")) + val remoteProvidersWithEth = remoteProviders + remoteEthProvider + + everyGettingLocalConfig(json = localProvidersJson) returns localProvidersWithEth + everyGettingRemoteConfig() returns remoteProvidersWithEth + everyCrashlyticsRecording() just Runs + + val actual = loader.load() + + coVerifyOrder { + assetReader.read(LOCAL_CONFIG_FILE_NAME) + moshi.adapter() + jsonAdapter.fromJson(localProvidersJson) + authProvider.getCardPublicKey() + authProvider.getCardId() + tangemTechServiceApi.getBlockchainProviders( + cardPublicKey = DEFAULT_CARD_PUBLIC_KEY, + cardId = DEFAULT_CARD_ID, + ) + firebaseCrashlytics.recordException(any()) + } + + val expected = remoteProviders + localEthProvider + + Truth.assertThat(actual).isEqualTo(expected) + } + private fun everyGettingLocalConfig( json: String, ): MockKStubScope {