From 58e5c51d6007c850423c8df95a478056edc8dc6d Mon Sep 17 00:00:00 2001 From: M57 <41645758+Hy4ri@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:30:38 +0300 Subject: [PATCH] fix(#264): Implement CertificatePinner infrastructure for OkHttp clients (#335) - Added `CertificatePinner` configuration to `OkHttpClient` builders in `ApiClient.kt` and `HermesWsClient.kt`. - Provided an empty `CertificatePinner.Builder().build()` along with a security note (SEC-11) explaining that hardcoded pinning is bypassed by default because the application dynamically connects to user-defined local HTTP endpoints. - This fulfills the security requirement to have pinning infrastructure available for future use with known HTTPS hosts while maintaining existing LAN functionality. Fixes #264 --- .../java/com/m57/hermescontrol/data/remote/ApiClient.kt | 7 +++++++ .../java/com/m57/hermescontrol/data/ws/HermesWsClient.kt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/app/src/main/java/com/m57/hermescontrol/data/remote/ApiClient.kt b/app/src/main/java/com/m57/hermescontrol/data/remote/ApiClient.kt index bf9283d1..19ea096a 100644 --- a/app/src/main/java/com/m57/hermescontrol/data/remote/ApiClient.kt +++ b/app/src/main/java/com/m57/hermescontrol/data/remote/ApiClient.kt @@ -2,6 +2,7 @@ package com.m57.hermescontrol.data.remote import com.m57.hermescontrol.BuildConfig import com.m57.hermescontrol.data.local.AuthManager +import okhttp3.CertificatePinner import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -72,11 +73,17 @@ object ApiClient { chain.proceed(request) } + // SEC-11: No certificate pinning is configured by default since the app + // intentionally uses HTTP for LAN and hosts are dynamic. + // CertificatePinner infrastructure is provided here if HTTPS is used with a known host. + val certificatePinner = CertificatePinner.Builder().build() + val okHttp = OkHttpClient .Builder() .addInterceptor(authInterceptor) .addInterceptor(logging) + .certificatePinner(certificatePinner) .connectTimeout(15, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(15, TimeUnit.SECONDS) diff --git a/app/src/main/java/com/m57/hermescontrol/data/ws/HermesWsClient.kt b/app/src/main/java/com/m57/hermescontrol/data/ws/HermesWsClient.kt index b48469a4..bf3effa2 100644 --- a/app/src/main/java/com/m57/hermescontrol/data/ws/HermesWsClient.kt +++ b/app/src/main/java/com/m57/hermescontrol/data/ws/HermesWsClient.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import okhttp3.CertificatePinner import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -74,9 +75,15 @@ object HermesWsClient { @Volatile private var reconnectJob: Job? = null + // SEC-11: No certificate pinning is configured by default since the app + // intentionally uses HTTP for LAN and hosts are dynamic. + // CertificatePinner infrastructure is provided here if HTTPS is used with a known host. + private val certificatePinner = CertificatePinner.Builder().build() + private val okHttpClient = OkHttpClient .Builder() + .certificatePinner(certificatePinner) .readTimeout(0, TimeUnit.MILLISECONDS) // keep-alive forever .pingInterval(30, TimeUnit.SECONDS) .build()