PM-13848 Handle URIs with ports and host matching (#4203)

This commit is contained in:
Dave Severns 2024-11-05 10:29:05 -05:00 committed by GitHub
parent 8f9585e4bc
commit 202b4de5ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 138 additions and 3 deletions

View file

@ -56,6 +56,7 @@ fun String.getWebHostFromAndroidUriOrNull(): String? =
fun String.getDomainOrNull(resourceCacheManager: ResourceCacheManager): String? =
this
.toUriOrNull()
?.addSchemeToUriIfNecessary()
?.parseDomainOrNull(resourceCacheManager = resourceCacheManager)
/**
@ -63,7 +64,10 @@ fun String.getDomainOrNull(resourceCacheManager: ResourceCacheManager): String?
*/
@OmitFromCoverage
fun String.hasPort(): Boolean {
val uri = this.toUriOrNull() ?: return false
val uri = this
.toUriOrNull()
?.addSchemeToUriIfNecessary()
?: return false
return uri.port != -1
}
@ -71,14 +75,19 @@ fun String.hasPort(): Boolean {
* Extract the host from this [String] if possible, otherwise return null.
*/
@OmitFromCoverage
fun String.getHostOrNull(): String? = this.toUriOrNull()?.host
fun String.getHostOrNull(): String? = this.toUriOrNull()
?.addSchemeToUriIfNecessary()
?.host
/**
* Extract the host with optional port from this [String] if possible, otherwise return null.
*/
@OmitFromCoverage
fun String.getHostWithPortOrNull(): String? {
val uri = this.toUriOrNull() ?: return null
val uri = this
.toUriOrNull()
?.addSchemeToUriIfNecessary()
?: return null
return uri.host?.let { host ->
val port = uri.port
if (port != -1) {

View file

@ -45,6 +45,27 @@ fun URI.parseDomainNameOrNull(resourceCacheManager: ResourceCacheManager): Domai
)
}
/**
* Adds and HTTPS scheme to a valid URI if that URI has a valid host in the raw string but
* the standard parsing of `URI("foo.com")` is not able to determine a valid host.
* (i.e. val uri = URI("foo.bar:1090") -> uri.host == null)
*/
fun URI.addSchemeToUriIfNecessary(): URI {
val uriString = this.toString()
return if (
// see if the string contains a host pattern
uriString.contains(".") &&
// if it does but the URI's host is null, add an https scheme
this.host == null &&
// provided that scheme does not exist already.
!uriString.hasHttpProtocol()
) {
URI("https://$uriString")
} else {
this
}
}
/**
* The internal implementation of [parseDomainNameOrNull]. This doesn't extend URI and has a
* non-null [host] parameter. Technically, URI.host could be null and we want to avoid issues with

View file

@ -3,8 +3,11 @@ package com.x8bit.bitwarden.data.util
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.util.findLastSubstringIndicesOrNull
import com.x8bit.bitwarden.data.platform.util.getDomainOrNull
import com.x8bit.bitwarden.data.platform.util.getHostOrNull
import com.x8bit.bitwarden.data.platform.util.getHostWithPortOrNull
import com.x8bit.bitwarden.data.platform.util.getWebHostFromAndroidUriOrNull
import com.x8bit.bitwarden.data.platform.util.hasHttpProtocol
import com.x8bit.bitwarden.data.platform.util.hasPort
import com.x8bit.bitwarden.data.platform.util.isAndroidApp
import com.x8bit.bitwarden.data.platform.util.parseDomainOrNull
import com.x8bit.bitwarden.data.platform.util.toUriOrNull
@ -154,4 +157,71 @@ class StringExtensionsTest {
// Verify
assertEquals(expected, actual)
}
@Test
fun `getHostOrNull should return host when one is present`() {
val expectedHost = "www.google.com"
assertEquals(expectedHost, expectedHost.getHostOrNull())
}
@Test
fun `getHostOrNull should return null when no host is present`() {
assertNull("boo".getHostOrNull())
}
@Test
fun `getHostOrNull should return host from URI string when present and custom URI scheme`() {
val expectedHost = "www.google.com"
val hostWithScheme = "androidapp://$expectedHost"
assertEquals(expectedHost, hostWithScheme.getHostOrNull())
}
@Suppress("MaxLineLength")
@Test
fun `getHostOrNull should return host from URI string when present and has port but no scheme`() {
val expectedHost = "www.google.com"
val hostWithPort = "$expectedHost:8080"
assertEquals(expectedHost, hostWithPort.getHostOrNull())
}
@Test
fun `hasPort returns true when port is present`() {
val uriString = "www.google.com:8080"
assertTrue("www.google.com:8080".hasPort())
}
@Test
fun `hasPort returns false when port is not present`() {
assertFalse("www.google.com".hasPort())
}
@Test
fun `hasPort return true when port is present and custom scheme is present`() {
val uriString = "androidapp://www.google.com:8080"
assertTrue(uriString.hasPort())
}
@Test
fun `getHostWithPortOrNull should return host with port when present`() {
val uriString = "www.google.com:8080"
assertEquals("www.google.com:8080", uriString.getHostWithPortOrNull())
}
@Test
fun `getHostWithPortOrNull should return host when no port is present`() {
val uriString = "www.google.com"
assertEquals("www.google.com", uriString.getHostWithPortOrNull())
}
@Test
fun `getHostWithPortOrNull should return null when no host is present`() {
assertNull("boo".getHostWithPortOrNull())
}
@Suppress("MaxLineLength")
@Test
fun `getHostWithPortOrNull should return host with port when present and custom scheme is present`() {
val uriString = "androidapp://www.google.com:8080"
assertEquals("www.google.com:8080", uriString.getHostWithPortOrNull())
}
}

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.util
import com.x8bit.bitwarden.data.platform.manager.ResourceCacheManager
import com.x8bit.bitwarden.data.platform.manager.model.DomainName
import com.x8bit.bitwarden.data.platform.util.addSchemeToUriIfNecessary
import com.x8bit.bitwarden.data.platform.util.parseDomainNameOrNull
import com.x8bit.bitwarden.data.platform.util.parseDomainOrNull
import io.mockk.every
@ -321,4 +322,38 @@ class UriExtensionsTest {
assertEquals(expected[index], actual)
}
}
@Test
fun `addSchemeToUriIfNecessary should add https when missing`() {
val uriWithNoScheme = URI("example.com")
assertEquals(URI("https://example.com"), uriWithNoScheme.addSchemeToUriIfNecessary())
}
@Suppress("MaxLineLength")
@Test
fun `addSchemeToUriIfNecessary should add https when https scheme is missing and a port is present`() {
val uriWithPort = URI("example.com:8080")
assertEquals(URI("https://example.com:8080"), uriWithPort.addSchemeToUriIfNecessary())
}
@Test
fun `addSchemeToUriIfNecessary should not add https when http scheme is present`() {
val uriWithHttpScheme = URI("http://example.com")
assertEquals(URI("http://example.com"), uriWithHttpScheme.addSchemeToUriIfNecessary())
}
@Test
fun `addSchemeToUriIfNecessary should not add https when https scheme is present`() {
val uriWithHttpsScheme = URI("https://example.com")
assertEquals(URI("https://example.com"), uriWithHttpsScheme.addSchemeToUriIfNecessary())
}
@Test
fun `addSchemeToUriIfNecessary should not add https when custom scheme is already present`() {
val uriWithCustomScheme = URI("bitwarden://example.com")
assertEquals(
URI("bitwarden://example.com"),
uriWithCustomScheme.addSchemeToUriIfNecessary(),
)
}
}