From acf10cf4a6f68ef66219f2e3050852c44b83e654 Mon Sep 17 00:00:00 2001 From: Aleksei Tirman Date: Tue, 24 Dec 2024 13:31:26 +0200 Subject: [PATCH] Redact sensitive headers --- .../io/ktor/client/plugins/logging/Logging.kt | 14 +++++-- .../client/plugins/logging/NewFormatTest.kt | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt b/ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt index 10e5511504..c1d7b56991 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt +++ b/ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt @@ -171,7 +171,11 @@ public val Logging: ClientPlugin = createClientPlugin("Logging", } for ((name, values) in headers.entries()) { - logger.log("$name: ${values.joinToString(separator = ", ")}") + if (sanitizedHeaders.find { sh -> sh.predicate(name) } == null) { + logger.log("$name: ${values.joinToString(separator = ", ")}") + } else { + logger.log("$name: ██") + } } if (!isBody() || request.method == HttpMethod.Get) { @@ -203,7 +207,7 @@ public val Logging: ClientPlugin = createClientPlugin("Logging", suspend fun logResponseStdFormat(response: HttpResponse): HttpResponse { if (isNone()) return response - var contentLength = response.headers[HttpHeaders.ContentLength]?.toLongOrNull() + val contentLength = response.headers[HttpHeaders.ContentLength]?.toLongOrNull() val request = response.request val duration = response.responseTime.timestamp - response.requestTime.timestamp @@ -226,7 +230,11 @@ public val Logging: ClientPlugin = createClientPlugin("Logging", } for ((name, values) in response.headers.entries()) { - logger.log("$name: ${values.joinToString(separator = ", ")}") + if (sanitizedHeaders.find { sh -> sh.predicate(name) } == null) { + logger.log("$name: ${values.joinToString(separator = ", ")}") + } else { + logger.log("$name: ██") + } } if (!isBody()) { diff --git a/ktor-client/ktor-client-plugins/ktor-client-logging/jvm/test/io/ktor/client/plugins/logging/NewFormatTest.kt b/ktor-client/ktor-client-plugins/ktor-client-logging/jvm/test/io/ktor/client/plugins/logging/NewFormatTest.kt index f551162c17..a569917af0 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-logging/jvm/test/io/ktor/client/plugins/logging/NewFormatTest.kt +++ b/ktor-client/ktor-client-plugins/ktor-client-logging/jvm/test/io/ktor/client/plugins/logging/NewFormatTest.kt @@ -757,6 +757,46 @@ class NewFormatTest { .assertNoMoreLogs() } + @Test + fun headersAreRedacted() = runTest { + HttpClient(MockEngine) { + install(Logging) { + level = LogLevel.HEADERS + logger = log + standardFormat = true + sanitizeHeader { it == "SeNsItIvE" } + } + + engine { + addHandler { + respondWithLength("", headers = Headers.build { + append("SeNsItIvE", "value") + append("Not-Sensitive", "value") + }) + } + } + }.use { client -> + client.get("/") { + header("SeNsItIvE", "value") + header("Not-Sensitive", "value") + } + log.assertLogEqual("--> GET /") + .assertLogEqual("SeNsItIvE: ██") + .assertLogEqual("Not-Sensitive: value") + .assertLogEqual("Accept-Charset: UTF-8") + .assertLogEqual("Accept: */*") + .assertLogEqual("--> END GET") + .assertLogMatch(Regex("""<-- 200 OK / \(\d+ms\)""")) + .assertLogEqual("SeNsItIvE: ██") + .assertLogEqual("Not-Sensitive: value") + .assertLogEqual("Content-Length: 0") + .assertLogEqual("Content-Type: text/plain") + .assertLogEqual("<-- END HTTP") + .assertNoMoreLogs() + + } + } + private fun MockRequestHandleScope.respondWithLength(): HttpResponseData { return respond("", headers = Headers.build { append("Content-Length", "0")