diff --git a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/OkHostnameVerifier.kt b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/OkHostnameVerifier.kt index 0431b28e3ba4..1a5669d11f74 100644 --- a/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/OkHostnameVerifier.kt +++ b/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/tls/OkHostnameVerifier.kt @@ -24,7 +24,6 @@ import javax.net.ssl.SSLException import javax.net.ssl.SSLSession import okhttp3.internal.canParseAsIpAddress import okhttp3.internal.toCanonicalHost -import okio.utf8Size /** * A HostnameVerifier consistent with [RFC 2818][rfc_2818]. @@ -96,7 +95,7 @@ object OkHostnameVerifier : HostnameVerifier { } /** Returns true if the [String] is ASCII encoded (0-127). */ - private fun String.isAscii() = length == utf8Size().toInt() + private fun String.isAscii() = all { it.code <= 127 } /** * Returns true if [hostname] matches the domain name [pattern]. diff --git a/okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/HostnameVerifierTest.kt b/okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/HostnameVerifierTest.kt index 070fc13ff57a..64b1f38ac403 100644 --- a/okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/HostnameVerifierTest.kt +++ b/okhttp/src/jvmTest/kotlin/okhttp3/internal/tls/HostnameVerifierTest.kt @@ -803,6 +803,33 @@ class HostnameVerifierTest { assertThat(localVerifier.verify("\uD83D\uDCA9.com", session)).isFalse() } + /** + * Test that malformed surrogates (unpaired high/low surrogates) are correctly + * identified as non-ASCII and rejected. + * + * https://github.com/square/okhttp/issues/6357 + */ + @Test fun malformedSurrogatesAreNotAscii() { + val heldCertificate = + HeldCertificate + .Builder() + .commonName("Foo Corp") + .addSubjectAlternativeName("foo.com") + .build() + val session = session(heldCertificate.certificatePem()) + + // Unpaired high surrogate - should not match any hostname + assertThat(verifier.verify("\uD800.com", session)).isFalse() + assertThat(verifier.verify("foo\uD800.com", session)).isFalse() + + // Unpaired low surrogate - should not match any hostname + assertThat(verifier.verify("\uDC00.com", session)).isFalse() + assertThat(verifier.verify("foo\uDC00.com", session)).isFalse() + + // Valid hostname should still work + assertThat(verifier.verify("foo.com", session)).isTrue() + } + @Test fun verifyAsIpAddress() { // IPv4 assertThat("127.0.0.1".canParseAsIpAddress()).isTrue()