diff --git a/app/src/main/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSource.kt b/app/src/main/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSource.kt index 3da24ae..d7d1f54 100644 --- a/app/src/main/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSource.kt +++ b/app/src/main/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSource.kt @@ -60,8 +60,6 @@ abstract class BaseJsoupSource( val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustAllCerts, java.security.SecureRandom()) connection.sslSocketFactory(sslContext.socketFactory) - // Note: setDefaultHostnameVerifier is global and might affect other parts of the app - javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true } } return connection.get() @@ -86,7 +84,6 @@ abstract class BaseJsoupSource( val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustAllCerts, java.security.SecureRandom()) connection.sslSocketFactory(sslContext.socketFactory) - javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true } } return connection diff --git a/app/src/main/java/io/aatricks/novelscraper/data/repository/source/NovelFireSource.kt b/app/src/main/java/io/aatricks/novelscraper/data/repository/source/NovelFireSource.kt index 441d1a0..a38b89f 100644 --- a/app/src/main/java/io/aatricks/novelscraper/data/repository/source/NovelFireSource.kt +++ b/app/src/main/java/io/aatricks/novelscraper/data/repository/source/NovelFireSource.kt @@ -76,10 +76,13 @@ class NovelFireSource @Inject constructor( val url = "$baseUrl/ajax/searchLive?inputContent=$encodedQuery" runCatching { - val response = connect(url) - .ignoreContentType(true) - .execute() - .body() + val request = okhttp3.Request.Builder() + .url(url) + .header("User-Agent", userAgent) + .header("Referer", baseUrl) + .build() + + val response = okHttpClient.newCall(request).execute().use { it.body?.string() ?: "" } val json = JSONObject(response) val data = json.getJSONArray("data") diff --git a/app/src/test/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSourceSecurityTest.kt b/app/src/test/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSourceSecurityTest.kt new file mode 100644 index 0000000..b8aa023 --- /dev/null +++ b/app/src/test/java/io/aatricks/novelscraper/data/repository/source/BaseJsoupSourceSecurityTest.kt @@ -0,0 +1,55 @@ +package io.aatricks.novelscraper.data.repository.source + +import io.aatricks.novelscraper.data.local.PreferencesManager +import io.aatricks.novelscraper.data.model.ExploreItem +import okhttp3.OkHttpClient +import org.junit.Assert.assertFalse +import org.junit.Assert.fail +import org.junit.Test +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.SSLSession + +class BaseJsoupSourceSecurityTest { + + class TestSource( + preferencesManager: PreferencesManager, + okHttpClient: OkHttpClient? + ) : BaseJsoupSource(preferencesManager, okHttpClient) { + override val name = "Test" + override val baseUrl = "https://example.com" + override suspend fun getPopularNovels(page: Int, tags: List) = emptyList() + override suspend fun searchNovels(query: String, page: Int) = emptyList() + override suspend fun getNovelDetails(url: String) = ExploreItem(title = "", url = "", source = "Test") + + fun testConnect(url: String) = connect(url) + // Helper to access protected getDocument + fun testGetDocument(url: String) = getDocument(url) + } + + @Test + fun `connect does NOT set global hostname verifier when ignoreSslErrors is true`() { + val originalVerifier = HttpsURLConnection.getDefaultHostnameVerifier() + try { + val prefs = mock(PreferencesManager::class.java) + `when`(prefs.ignoreSslErrors).thenReturn(true) + + val source = TestSource(prefs, null) + + // Trigger the code path + source.testConnect("https://example.com") + + val currentVerifier = HttpsURLConnection.getDefaultHostnameVerifier() + val mockSession = mock(SSLSession::class.java) + + // A secure verifier should reject this + val accepted = currentVerifier.verify("evil.com", mockSession) + + assertFalse("Global HostnameVerifier was modified to accept invalid hostnames!", accepted) + + } finally { + HttpsURLConnection.setDefaultHostnameVerifier(originalVerifier) + } + } +}