Repository: tipsy/javalin
Pull request #971 (2020-05-19 09:25:20)

TestExceptionMapper: Do proper JSON escaping on title
The jsonEscape function in TestExceptionMapper didn't
really do anything other than replace `"` with `\"`, which
doesn't handle things like newlines, backslashes or
any other characters that need to be escaped in JSON.

This patch uses the Jackson library function
JsonStringEncoder.quoteAsString to do the escaping.

tipsy: Thanks @krig. The mapper is supposed to work without any JSON dependency, which is why it has a lot of ugly (and apparently incorrect) code. Unfortunately I can't merge this as it introduces a hard dependency on Jackson. You're welcome to join the hackathon and submit a "manual" fix though :)
krig: Ah, OK. I'll see if I can come up with an improved escaper at least :)
tipsy: That would be great, thank you!
krig: @tipsy I've replaced the dependency with a minimal utility function now :)
krig: Hm, just noticed I'm double-appending, will fix that quickly..
tipsy: Very nice, thanks!

Patch:
diff --git a/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt b/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
index 1bf5747f..56346922 100644
--- a/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
+++ b/javalin/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
@@ -7,6 +7,7 @@
 package io.javalin.http
 
 import io.javalin.core.util.Header
+import io.javalin.http.util.JsonEscapeUtil
 import java.util.concurrent.CompletionException
 
 object HttpResponseExceptionMapper {
@@ -58,6 +59,6 @@ object HttpResponseExceptionMapper {
         else -> docsUrl + "error-responses"
     }
 
-    private fun String.jsonEscape() = this.replace("\"", "\\\"")
+    private fun String.jsonEscape() = JsonEscapeUtil.escape(this)
 
 }
diff --git a/javalin/src/main/java/io/javalin/http/util/JsonEscapeUtil.kt b/javalin/src/main/java/io/javalin/http/util/JsonEscapeUtil.kt
new file mode 100644
index 00000000..ca95b885
--- /dev/null
+++ b/javalin/src/main/java/io/javalin/http/util/JsonEscapeUtil.kt
@@ -0,0 +1,25 @@
+/*
+ * Javalin - https://javalin.io
+ * Copyright 2017 David Åse
+ * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
+ */
+
+package io.javalin.http.util
+
+object JsonEscapeUtil {
+    fun escape(str: String): String {
+        val builder = StringBuilder(str.length)
+        for (ch in str) {
+            builder.append(when (ch) {
+                '\"' -> "\\\""
+                '\n' -> "\\n"
+                '\r' -> "\\r"
+                '\\' -> "\\\\"
+                '\t' -> "\\t"
+                '\b' -> "\\b"
+                else -> ch
+            })
+        }
+        return builder.toString()
+    }
+}


Test patch:
diff --git a/javalin/src/test/java/io/javalin/TestExceptionMapper.kt b/javalin/src/test/java/io/javalin/TestExceptionMapper.kt
index 5addacda..4f904bb9 100644
--- a/javalin/src/test/java/io/javalin/TestExceptionMapper.kt
+++ b/javalin/src/test/java/io/javalin/TestExceptionMapper.kt
@@ -92,4 +92,10 @@ class TestExceptionMapper {
         assertThat(http.jsonGet("/").body).contains("""MY MESSAGE WITH \"QUOTES\"""")
     }
 
+    @Test
+    fun `exception title can contain newlines`() = TestUtil.test { app, http ->
+        app.get("/") { throw BadRequestResponse("MY MESSAGE WITH \nNEWLINES\n") }
+        assertThat(http.jsonGet("/").body).contains("""MY MESSAGE WITH \nNEWLINES\n""")
+    }
+
 }