package com.peachpasswords.shared import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import java.util.Locale /** * eTLD+1 ("bank.co.uk", not "co.uk"), the key everything from autofill matching to * vault health compares on. * * [okhttp3.HttpUrl.topPrivateDomain] is OkHttp's *public* API over the same public * suffix list its internal `PublicSuffixDatabase` exposes, so this keeps real PSL * accuracy without depending on an `okhttp3.internal` class. Accuracy is not * optional here: a "last two labels" heuristic would collapse `evil.co.uk` and * `bank.co.uk` to the same key and hand credentials to the wrong site. * Returns null for IP literals and bare public suffixes, as before. */ fun extractDomain(url: String): String? { return try { val candidate = url.trim() if (candidate.isEmpty()) return null val normalized = if ( candidate.startsWith("http://", ignoreCase = true) || candidate.startsWith("https://", ignoreCase = true) ) { candidate } else { "https://$candidate" } val parsed = normalized.toHttpUrlOrNull() ?: return null val host = parsed.host.lowercase(Locale.ROOT) // Autofill must never treat local names or IP literals as registrable // origins. topPrivateDomain() rejects most of these already, but the // explicit guards keep the fail-closed contract stable across OkHttp PSL // updates and make the policy obvious to callers. if ( host.isEmpty() || !host.contains('.') || host == "localhost" || host.endsWith(".localhost") || host == "local" || host.endsWith(".local") || host.contains(':') || host.matches(Regex("\\d+(?:\\.\\d+){3}")) ) return null parsed.topPrivateDomain()?.lowercase(Locale.ROOT) } catch (_: Exception) { null } } fun getDisplayDomain(url: String): String { return try { val normalized = if (url.startsWith("http")) url else "https://$url" java.net.URL(normalized).host } catch (_: Exception) { url } } /** * Browser parity with `shared/domain.ts::detectHomograph`: true when the host * contains non-ASCII characters or a punycode `xn--` label. This is warning-only; * matching/keying remains handled by [extractDomain]. */ fun detectHomograph(url: String): Boolean { return try { val normalized = if (url.contains("://")) url else "https://$url" val host = java.net.URL(normalized).host host.any { it.code > 0x7F } || host.split('.').any { it.startsWith("xn--", ignoreCase = true) } } catch (_: Exception) { false } }