From 89ec8dc13b03af9f7b74a82d187b9bee3c444925 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 3 Nov 2026 19:13:36 +0100 Subject: fix(app): validate the proxy fully and keep its password out of properties - A host with an empty port was saved, but ProxySelector treats port -1 as no proxy, so git silently connected directly. The port is now required while a host is set. - Patterns.WEB_URL accepted "http://proxy.example.org:3128/" as a host, which then broke every HTTPS operation. The host must be a numeric address or a plain domain name, without scheme, port or path. - setDefaultProxy copied the proxy password into the process-wide http.proxyPassword system property. The Authenticator already supplies the credentials, so the properties are only cleared now. Co-Authored-By: Claude Opus 5.5 --- .../main/java/app/passwordstore/ui/settings/ProxyViewModel.kt | 10 ++++++---- app/src/main/java/app/passwordstore/util/proxy/ProxyUtils.kt | 11 ++++------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'app/src/main/java') diff --git a/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt b/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt index ccd275a6..fb2bfe93 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/ProxyViewModel.kt @@ -17,12 +17,13 @@ import app.passwordstore.util.settings.GitSettings import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject -/** A numeric address or a web host name, as the old proxy screen accepted. */ +/** A numeric address or a plain host name: no scheme, port or path, which the port field holds. */ fun isProxyHost(text: String): Boolean { val numeric = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) InetAddresses.isNumericAddress(text) else @Suppress("DEPRECATION") Patterns.IP_ADDRESS.matcher(text).matches() - return numeric || Patterns.WEB_URL.matcher(text).matches() + return numeric || + (!text.contains(':') && !text.contains('/') && Patterns.DOMAIN_NAME.matcher(text).matches()) } /** @@ -43,12 +44,13 @@ constructor(private val gitSettings: GitSettings, private val proxyUtils: ProxyU val hostError: Boolean get() = host.isNotBlank() && !isProxyHost(host.trim()) + /** Invalid, or empty while a host is set: a proxy without a port is not used at all. */ val portError: Boolean - get() = proxyPort(port) == null + get() = proxyPort(port).let { it == null || (it == -1 && host.isNotBlank()) } /** Stores the proxy and applies it at once; false, storing nothing, when a field is invalid. */ fun save(): Boolean { - if (hostError) return false + if (hostError || portError) return false val portValue = proxyPort(port) ?: return false gitSettings.proxyHost = host.trim().ifEmpty { null } gitSettings.proxyPort = portValue diff --git a/app/src/main/java/app/passwordstore/util/proxy/ProxyUtils.kt b/app/src/main/java/app/passwordstore/util/proxy/ProxyUtils.kt index 1b521141..6382e1ed 100644 --- a/app/src/main/java/app/passwordstore/util/proxy/ProxyUtils.kt +++ b/app/src/main/java/app/passwordstore/util/proxy/ProxyUtils.kt @@ -42,13 +42,10 @@ class ProxyUtils @Inject constructor(private val gitSettings: GitSettings) { ) val user = gitSettings.proxyUsername ?: "" val password = gitSettings.proxyPassword ?: "" - if (user.isEmpty() || password.isEmpty()) { - System.clearProperty(HTTP_PROXY_USER_PROPERTY) - System.clearProperty(HTTP_PROXY_PASSWORD_PROPERTY) - } else { - System.setProperty(HTTP_PROXY_USER_PROPERTY, user) - System.setProperty(HTTP_PROXY_PASSWORD_PROPERTY, password) - } + // The Authenticator below supplies the credentials; never copy the password into a + // process-wide system property. + System.clearProperty(HTTP_PROXY_USER_PROPERTY) + System.clearProperty(HTTP_PROXY_PASSWORD_PROPERTY) Authenticator.setDefault( object : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication? { -- cgit v1.2.3