Skip to content

Commit 0a59138

Browse files
committed
KTOR-9423 CannotTransformContentToTypeException leaks internal class names in response body
1 parent 12de7fb commit 0a59138

3 files changed

Lines changed: 31 additions & 19 deletions

File tree

ktor-server/ktor-server-core/common/src/io/ktor/server/engine/BaseApplicationEngine.kt

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2014-2026 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package io.ktor.server.engine
@@ -9,7 +9,6 @@ import io.ktor.http.*
99
import io.ktor.http.content.*
1010
import io.ktor.server.application.*
1111
import io.ktor.server.http.content.*
12-
import io.ktor.server.plugins.*
1312
import io.ktor.server.response.*
1413
import io.ktor.server.routing.*
1514
import io.ktor.util.date.*
@@ -113,18 +112,6 @@ private fun Application.installDefaultInterceptors() {
113112
}
114113

115114
private fun Application.installDefaultTransformationChecker() {
116-
// Respond with "415 Unsupported Media Type" if content cannot be transformed on receive
117-
intercept(ApplicationCallPipeline.Plugins) {
118-
try {
119-
proceed()
120-
} catch (e: CannotTransformContentToTypeException) {
121-
when (val message = e.message) {
122-
null -> call.respond(HttpStatusCode.UnsupportedMediaType)
123-
else -> call.respond(HttpStatusCode.UnsupportedMediaType, message)
124-
}
125-
}
126-
}
127-
128115
val checkBodyPhase = PipelinePhase("BodyTransformationCheckPostRender")
129116
sendPipeline.insertPhaseAfter(ApplicationSendPipeline.Render, checkBodyPhase)
130117
sendPipeline.intercept(checkBodyPhase) { subject ->

ktor-server/ktor-server-core/common/src/io/ktor/server/engine/DefaultEnginePipeline.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2014-2026 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package io.ktor.server.engine
@@ -91,7 +91,8 @@ public suspend fun logError(call: ApplicationCall, error: Throwable) {
9191
public fun defaultExceptionStatusCode(cause: Throwable): HttpStatusCode? = when (cause) {
9292
is BadRequestException -> HttpStatusCode.BadRequest
9393
is NotFoundException -> HttpStatusCode.NotFound
94-
is UnsupportedMediaTypeException -> HttpStatusCode.UnsupportedMediaType
94+
is UnsupportedMediaTypeException,
95+
is CannotTransformContentToTypeException -> HttpStatusCode.UnsupportedMediaType
9596
is PayloadTooLargeException -> HttpStatusCode.PayloadTooLarge
9697
is TimeoutException, is TimeoutCancellationException -> HttpStatusCode.GatewayTimeout
9798
else -> null
@@ -100,8 +101,8 @@ public fun defaultExceptionStatusCode(cause: Throwable): HttpStatusCode? = when
100101
private suspend fun tryRespondError(call: ApplicationCall, statusCode: HttpStatusCode, message: String?) {
101102
if (call.response.isCommitted || call.response.isSent) return
102103
try {
103-
when (message) {
104-
null -> call.respond(statusCode)
104+
when {
105+
message == null || !call.application.developmentMode -> call.respond(statusCode)
105106
else -> call.respond(statusCode, message)
106107
}
107108
} catch (_: BaseApplicationResponse.ResponseAlreadySentException) {
@@ -126,7 +127,8 @@ private fun ApplicationEnvironment.logFailure(call: ApplicationCall, cause: Thro
126127
is BadRequestException,
127128
is NotFoundException,
128129
is PayloadTooLargeException,
129-
is UnsupportedMediaTypeException -> log.debug(infoString, cause)
130+
is UnsupportedMediaTypeException,
131+
is CannotTransformContentToTypeException -> log.debug(infoString, cause)
130132

131133
else -> log.error("$status: $logString", cause)
132134
}

ktor-server/ktor-server-plugins/ktor-server-status-pages/common/test/io/ktor/server/plugins/statuspages/StatusPagesTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import kotlinx.coroutines.CancellationException
2222
import kotlinx.coroutines.async
2323
import kotlinx.coroutines.delay
2424
import kotlinx.coroutines.launch
25+
import kotlin.reflect.typeOf
2526
import kotlin.test.*
2627

2728
class StatusPagesTest {
@@ -624,4 +625,26 @@ class StatusPagesTest {
624625
assertEquals("Custom-Value-Response", headers["Custom-Header-Response"])
625626
}
626627
}
628+
629+
@Test
630+
fun testCannotTransformContentToTypeException() = testApplication {
631+
application {
632+
install(StatusPages) {
633+
exception<CannotTransformContentToTypeException> { call, _ ->
634+
call.respondText("Custom Unsupported Media Type", status = HttpStatusCode.UnsupportedMediaType)
635+
}
636+
}
637+
638+
routing {
639+
post("/") {
640+
throw CannotTransformContentToTypeException(typeOf<String>())
641+
}
642+
}
643+
}
644+
645+
client.post("/").let { response ->
646+
assertEquals(HttpStatusCode.UnsupportedMediaType, response.status)
647+
assertEquals("Custom Unsupported Media Type", response.bodyAsText())
648+
}
649+
}
627650
}

0 commit comments

Comments
 (0)