Skip to content

Commit 105d22f

Browse files
longvantruongpandeymanggmattinanntgithub-advanced-security[bot]
authored
Merge main into Mobile sdk custom (#28)
* chore: custom sdk tcb * chore: custom sdk tcb * chore: custom sdk tcb * chore: custom sdk tcb * chore: custom sdk tcb * fix: blackduck issues (#22) * fixes blackduck issues * fix: dokka version * fix: Workflow does not contain permissions (#20) Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * chore: handle back press survey * chore: handle back press survey * fix: fixes the pixel fold issue (#24) * fixes the pixel fold issue * adds errors * fixes log * add hidden field * handle back press survey * fixes responses stuck error (#25) * feat(api): add cancel support for ongoing retrofit calls * fix: blocks http requests (#27) * fix: blocks http requests * fix: fixes network sec config * fix: retrofit * feat(network): enforce TLS 1.2 1.3 support odder Android version --------- Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Co-authored-by: Matti Nannt <mail@matti.sh> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent f67bc53 commit 105d22f

6 files changed

Lines changed: 72 additions & 9 deletions

File tree

android/src/androidTest/java/com/formbricks/android/FormbricksInstrumentedTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit
2929
class FormbricksInstrumentedTest {
3030

3131
private val environmentId = "environmentId"
32-
private val appUrl = "http://appUrl"
32+
private val appUrl = "https://example.com"
3333
private val userId = "6CCCE716-6783-4D0F-8344-9C7DFA43D8F7"
3434
private val surveyID = "cm6ovw6j7000gsf0kduf4oo4i"
3535

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
44
<uses-permission android:name="android.permission.INTERNET" />
55
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="35" />
6+
<application android:networkSecurityConfig="@xml/network_security_config" />
67
</manifest>

android/src/main/java/com/formbricks/android/Formbricks.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ object Formbricks {
6969
return
7070
}
7171

72+
// Validate HTTPS URL
73+
if (!config.appUrl.startsWith("https://", ignoreCase = true)) {
74+
val error = RuntimeException("Only HTTPS URLs are allowed for security reasons. HTTP URLs are not permitted. Provided URL: ${config.appUrl}")
75+
Logger.e(error)
76+
return
77+
}
78+
7279
applicationContext = context
7380

7481
appUrl = config.appUrl

android/src/main/java/com/formbricks/android/network/FormbricksApiService.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.formbricks.android.network
22

33
import com.formbricks.android.api.error.FormbricksAPIError
44
import com.formbricks.android.helper.mapToJsonElement
5+
import com.formbricks.android.logger.Logger
56
import com.formbricks.android.model.environment.EnvironmentDataHolder
67
import com.formbricks.android.model.environment.EnvironmentResponse
78
import com.formbricks.android.model.user.PostUserBody
@@ -15,19 +16,25 @@ import retrofit2.Retrofit
1516

1617
open class FormbricksApiService {
1718

18-
private lateinit var retrofit: Retrofit
19+
private var retrofit: Retrofit? = null
1920
private val callProvider = mutableListOf<Call<*>>()
20-
2121
fun initialize(appUrl: String, isLoggingEnabled: Boolean) {
22-
retrofit = FormbricksRetrofitBuilder(appUrl, isLoggingEnabled)
23-
.getBuilder()
24-
.build()
22+
val builder = FormbricksRetrofitBuilder(appUrl, isLoggingEnabled).getBuilder()
23+
if (builder != null) {
24+
retrofit = builder.build()
25+
} else {
26+
// Builder returned null due to HTTP URL - log error and skip initialization
27+
val error = RuntimeException("Failed to initialize API service due to invalid URL configuration. Only HTTPS URLs are allowed.")
28+
Logger.e(error)
29+
retrofit = null
30+
}
2531
}
2632

2733
open fun getEnvironmentStateObject(environmentId: String): Result<EnvironmentDataHolder> {
2834
return try {
35+
val retrofitInstance = retrofit ?: return Result.failure(RuntimeException("API service not initialized due to invalid URL"))
2936
val result = execute {
30-
retrofit.create(FormbricksService::class.java)
37+
retrofitInstance.create(FormbricksService::class.java)
3138
.getEnvironmentState(environmentId)
3239
}
3340
val json = Json { ignoreUnknownKeys = true }
@@ -42,8 +49,9 @@ open class FormbricksApiService {
4249
}
4350

4451
open fun postUser(environmentId: String, body: PostUserBody): Result<UserResponse> {
52+
val retrofitInstance = retrofit ?: return Result.failure(RuntimeException("API service not initialized due to invalid URL"))
4553
return execute {
46-
retrofit.create(FormbricksService::class.java)
54+
retrofitInstance.create(FormbricksService::class.java)
4755
.postUser(environmentId, body)
4856
}
4957
}

android/src/main/java/com/formbricks/android/network/FormbricksRetrofitBuilder.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
package com.formbricks.android.network
22

3+
import com.formbricks.android.logger.Logger
4+
import okhttp3.ConnectionSpec
5+
import okhttp3.Interceptor
36
import okhttp3.OkHttpClient
7+
import okhttp3.Response
8+
import okhttp3.TlsVersion
49
import okhttp3.logging.HttpLoggingInterceptor
510
import retrofit2.Retrofit
611
import retrofit2.converter.gson.GsonConverterFactory
12+
import java.io.IOException
713
import java.util.concurrent.TimeUnit
814

915
class FormbricksRetrofitBuilder(private val baseUrl: String, private val loggingEnabled: Boolean) {
16+
fun getBuilder(): Retrofit.Builder? {
17+
// Validate base URL is HTTPS
18+
if (!baseUrl.startsWith("https://", ignoreCase = true)) {
19+
val error = RuntimeException("Only HTTPS URLs are allowed. HTTP URLs are not permitted for security reasons. Provided URL: $baseUrl")
20+
Logger.e(error)
21+
return null
22+
}
23+
24+
val tlsSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
25+
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3)
26+
.allEnabledCipherSuites()
27+
.build()
1028

11-
fun getBuilder(): Retrofit.Builder {
1229
val clientBuilder = OkHttpClient.Builder()
1330
.connectTimeout(CONNECT_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
1431
.readTimeout(READ_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
1532
.followSslRedirects(true)
33+
.connectionSpecs(listOf(tlsSpec))
34+
.addInterceptor(HttpsOnlyInterceptor())
35+
1636
if (loggingEnabled) {
1737
val logging = HttpLoggingInterceptor()
1838
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
@@ -25,6 +45,25 @@ class FormbricksRetrofitBuilder(private val baseUrl: String, private val logging
2545
.client(clientBuilder.build())
2646
}
2747

48+
/**
49+
* Interceptor that ensures all requests use HTTPS protocol
50+
*/
51+
private class HttpsOnlyInterceptor : Interceptor {
52+
@Throws(IOException::class)
53+
override fun intercept(chain: Interceptor.Chain): Response {
54+
val request = chain.request()
55+
val url = request.url
56+
57+
if (!url.isHttps) {
58+
val error = RuntimeException("HTTP request blocked. Only HTTPS requests are allowed. Attempted URL: $url")
59+
Logger.e(error)
60+
throw IOException("HTTP request blocked. Only HTTPS requests are allowed. Attempted URL: $url")
61+
}
62+
63+
return chain.proceed(request)
64+
}
65+
}
66+
2867
companion object {
2968
private const val CONNECT_TIMEOUT_MS = 30 * 1000 // 30 seconds
3069
private const val READ_TIMEOUT_MS = 30 * 1000 // 30 seconds
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<network-security-config>
3+
<base-config cleartextTrafficPermitted="false">
4+
<trust-anchors>
5+
<certificates src="system"/>
6+
</trust-anchors>
7+
</base-config>
8+
</network-security-config>

0 commit comments

Comments
 (0)