From 1692b700a80b5cd8f5f43696545fa5335fab01b9 Mon Sep 17 00:00:00 2001 From: gregoryn22 Date: Wed, 3 Dec 2025 15:28:13 -0500 Subject: [PATCH 1/3] fix: handle invalid totalBookCount values in KomgaMediaServerClientAdapter --- .../komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt index 7af0b62c..f0d4b9c1 100644 --- a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt +++ b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt @@ -398,7 +398,8 @@ class KomgaMediaServerClientAdapter( language = patchIfNotNull(language), genres = patchIfNotNull(genres), tags = patchIfNotNull(tags), - totalBookCount = patchIfNotNull(totalBookCount), + // Komga rejects totalBookCount <= 0, so omit invalid values + totalBookCount = patchIfNotNull(totalBookCount?.takeIf { it > 0 }), links = patchIfNotNull(links?.map { KomgaWebLink(it.label, it.url) }), statusLock = patchIfNotNull(statusLock), From e0690e115bf831839928e8b55f02cf1460f023fe Mon Sep 17 00:00:00 2001 From: gregoryn22 Date: Thu, 4 Dec 2025 11:52:54 -0500 Subject: [PATCH 2/3] fix: improve URL validation and safe link handling in KomgaMediaServerClientAdapter - Add safe URL handling for links to prevent unsafe URLs - Enhance URL validation to ensure only valid http/https schemes and non-empty hosts are accepted --- .../komga/KomgaMediaServerClientAdapter.kt | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt index f0d4b9c1..7c1ed0f9 100644 --- a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt +++ b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt @@ -1,6 +1,7 @@ package snd.komf.mediaserver.komga import io.github.oshai.kotlinlogging.KotlinLogging +import java.net.URI import snd.komf.mediaserver.MediaServerClient import snd.komf.mediaserver.model.MediaServerAlternativeTitle import snd.komf.mediaserver.model.MediaServerAuthor @@ -51,6 +52,34 @@ import snd.komga.client.series.KomgaSeriesThumbnail private val logger = KotlinLogging.logger {} +private fun safeUrlOrNull(raw: String?): String? { + val trimmed = raw?.trim() + if (trimmed.isNullOrEmpty()) return null + + return try { + val uri = URI(trimmed) + + val scheme = uri.scheme?.lowercase() + val host = uri.host + + // Require http/https + a host, otherwise Komga will likely reject it + if (scheme != "http" && scheme != "https") { + logger.warn { "Dropping URL without http/https scheme from metadata: $trimmed" } + return null + } + + if (host.isNullOrBlank()) { + logger.warn { "Dropping URL without host from metadata: $trimmed" } + return null + } + + trimmed + } catch (e: Exception) { + logger.warn(e) { "Dropping invalid URL from metadata: $trimmed" } + null + } +} + class KomgaMediaServerClientAdapter( private val komgaBookClient: KomgaBookClient, private val komgaSeriesClient: KomgaSeriesClient, @@ -400,7 +429,9 @@ class KomgaMediaServerClientAdapter( tags = patchIfNotNull(tags), // Komga rejects totalBookCount <= 0, so omit invalid values totalBookCount = patchIfNotNull(totalBookCount?.takeIf { it > 0 }), - links = patchIfNotNull(links?.map { KomgaWebLink(it.label, it.url) }), + links = patchIfNotNull(links?.mapNotNull { link -> + safeUrlOrNull(link.url)?.let { KomgaWebLink(link.label, it) } + }), statusLock = patchIfNotNull(statusLock), titleLock = patchIfNotNull(titleLock), @@ -448,7 +479,9 @@ class KomgaMediaServerClientAdapter( authors = patchIfNotNull(authors?.map { KomgaAuthor(it.name, it.role) }), tags = patchIfNotNull(tags), isbn = patchIfNotNull(isbn), - links = patchIfNotNull(links?.map { KomgaWebLink(it.label, it.url) }), + links = patchIfNotNull(links?.mapNotNull { link -> + safeUrlOrNull(link.url)?.let { KomgaWebLink(link.label, it) } + }), titleLock = patchIfNotNull(titleLock), summaryLock = patchIfNotNull(summaryLock), From 6e87454e76db0ebdbba4c2118378131d33569ded Mon Sep 17 00:00:00 2001 From: gregoryn22 Date: Mon, 8 Dec 2025 08:25:51 -0500 Subject: [PATCH 3/3] Use ktor parseUrl and Url.toStingEncoded for Komga web link handling --- .../komga/KomgaMediaServerClientAdapter.kt | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt index 7c1ed0f9..1f0221f6 100644 --- a/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt +++ b/komf-mediaserver/src/commonMain/kotlin/snd/komf/mediaserver/komga/KomgaMediaServerClientAdapter.kt @@ -1,8 +1,9 @@ package snd.komf.mediaserver.komga import io.github.oshai.kotlinlogging.KotlinLogging -import java.net.URI +import io.ktor.http.parseUrl import snd.komf.mediaserver.MediaServerClient +import snd.komf.util.toStingEncoded import snd.komf.mediaserver.model.MediaServerAlternativeTitle import snd.komf.mediaserver.model.MediaServerAuthor import snd.komf.mediaserver.model.MediaServerBook @@ -57,25 +58,10 @@ private fun safeUrlOrNull(raw: String?): String? { if (trimmed.isNullOrEmpty()) return null return try { - val uri = URI(trimmed) - - val scheme = uri.scheme?.lowercase() - val host = uri.host - - // Require http/https + a host, otherwise Komga will likely reject it - if (scheme != "http" && scheme != "https") { - logger.warn { "Dropping URL without http/https scheme from metadata: $trimmed" } - return null - } - - if (host.isNullOrBlank()) { - logger.warn { "Dropping URL without host from metadata: $trimmed" } - return null - } - - trimmed - } catch (e: Exception) { - logger.warn(e) { "Dropping invalid URL from metadata: $trimmed" } + val url = parseUrl(trimmed) + url.toStingEncoded() + } catch (e: Throwable) { + logger.warn("Dropping invalid URL from metadata: {}", trimmed, e) null } }