Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,31 @@ class ApiUrlDiscoveryTest {
)
}

@Test
fun testAllowedHostnamesDoesNotBreakValidSites() = runTest {
val httpClient = WpHttpClient.DefaultHttpClient(emptyList())
val executor = WpRequestExecutor(httpClient)
val loginClient = WpLoginClient(requestExecutor = executor)

// First, configure an allowed hostname override for a specific cert/hostname pair
httpClient.addAllowedAlternativeNamesForHostname(
"vanilla.wpmt.co",
listOf("wordpress-1315525-4803651.cloudwaysapps.com")
)

// The override should work
assertEquals(
"https://vanilla.wpmt.co/wp-admin/authorize-application.php",
loginClient.apiDiscovery("https://wordpress-1315525-4803651.cloudwaysapps.com")
.assertSuccess().applicationPasswordsAuthenticationUrl.url()
)

// Other valid SSL sites should still work via fallback to default hostname verification.
// google.com uses wildcard/SAN certificates which require proper OkHttp verification.
val reason = loginClient.apiDiscovery("https://google.com").assertFailureFindApiRoot()
assertInstanceOf(FindApiRootFailure.ProbablyNotAWordPressSite::class.java, reason)
}

@Test
fun testCustomOkHttpClient() = runTest {
val executor =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package rs.wordpress.api.kotlin

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSession

Expand All @@ -25,9 +26,7 @@ sealed class WpHttpClient {
private fun buildClient(): OkHttpClient {
return OkHttpClient.Builder().apply {
this@DefaultHttpClient.interceptors.forEach { addInterceptor(it) }
if (allowedHostnames.isNotEmpty()) {
hostnameVerifier(WpRequestExecutorHostnameVerifier(allowedHostnames))
}
hostnameVerifier(WpRequestExecutorHostnameVerifier(allowedHostnames))
}.build()
}

Expand All @@ -41,9 +40,12 @@ sealed class WpHttpClient {

private class WpRequestExecutorHostnameVerifier(private val allowedHostnames: Map<String, List<String>>) :
HostnameVerifier {
override fun verify(hostname: String?, session: SSLSession?): Boolean =
session?.let {
val peerPrincipalName = it.peerPrincipal.name.replace("CN=", "")
peerPrincipalName == hostname || allowedHostnames[peerPrincipalName]?.contains(hostname) ?: false
} ?: false
override fun verify(hostname: String?, session: SSLSession?): Boolean {
if (hostname == null || session == null) return false

// Check our custom allowlist first, then fall back to default OkHttp verification
val peerPrincipalName = session.peerPrincipal.name.replace("CN=", "")
val customMatch = allowedHostnames[peerPrincipalName]?.contains(hostname) ?: false
return customMatch || OkHostnameVerifier.verify(hostname, session)
}
}