Repository: netty/netty
Issue: JsonObjectDecoder does not handle escaped backslash
Labels: defect

I've noticed the JsonObjectDecoder checks end of value string by looking at the previous char and if its the escape char \ then it's not the end of string if the current char is double quote. This is not correct.

Let's send the json object with key "key" and value "something\" , it is serialized as this:

{"key":"something\\"}

The decoder will not decode this since the last double quote is  after a backslash.
However this backslash is escaped but this is not handled in the code:
https://github.com/netty/netty/blob/83c349ffa94d3992c4ee511d3625afc0c97c12bb/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java#L194

This is with 5.0.0 Alpha2
Cheers


Issue comments:
@Zpetkov the 5.x branch has been deprecated and is no longer supported. However, the issue also exists in 4.1. I ll take care of this.

Fixed by https://github.com/netty/netty/pull/5203


Pull request review comments:

Patch that resolved the issue:
diff --git a/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java b/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java
index b791e410370..a34334b6c0e 100644
--- a/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java
@@ -190,9 +190,22 @@ private void decodeByte(byte c, ByteBuf in, int idx) {
             // also contain braces/brackets and that could lead to incorrect results.
             if (!insideString) {
                 insideString = true;
-            // If the double quote wasn't escaped then this is the end of a string.
-            } else if (in.getByte(idx - 1) != '\\') {
-                insideString = false;
+            } else {
+                int backslashCount = 0;
+                idx--;
+                while (idx >= 0) {
+                    if (in.getByte(idx) == '\\') {
+                        backslashCount++;
+                        idx--;
+                    } else {
+                        break;
+                    }
+                }
+                // The double quote isn't escaped only if there are even "\"s.
+                if (backslashCount % 2 == 0) {
+                    // Since the double quote isn't escaped then this is the end of a string.
+                    insideString = false;
+                }
             }
         }
     }


Test patch:
diff --git a/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java
index 08ece60917b..083b4eb1d43 100644
--- a/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java
+++ b/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java
@@ -87,6 +87,51 @@ public void testSingleByteStream() {
         assertFalse(ch.finish());
     }
 
+    @Test
+    public void testBackslashInString1() {
+        EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder());
+        // {"foo" : "bar\""}
+        String json = "{\"foo\" : \"bar\\\"\"}";
+        System.out.println(json);
+        ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8));
+
+        ByteBuf res = ch.readInbound();
+        assertEquals(json, res.toString(CharsetUtil.UTF_8));
+        res.release();
+
+        assertFalse(ch.finish());
+    }
+
+    @Test
+    public void testBackslashInString2() {
+        EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder());
+        // {"foo" : "bar\\"}
+        String json = "{\"foo\" : \"bar\\\\\"}";
+        System.out.println(json);
+        ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8));
+
+        ByteBuf res = ch.readInbound();
+        assertEquals(json, res.toString(CharsetUtil.UTF_8));
+        res.release();
+
+        assertFalse(ch.finish());
+    }
+
+    @Test
+    public void testBackslashInString3() {
+        EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder());
+        // {"foo" : "bar\\\""}
+        String json = "{\"foo\" : \"bar\\\\\\\"\"}";
+        System.out.println(json);
+        ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8));
+
+        ByteBuf res = ch.readInbound();
+        assertEquals(json, res.toString(CharsetUtil.UTF_8));
+        res.release();
+
+        assertFalse(ch.finish());
+    }
+
     @Test
     public void testMultipleJsonObjectsInOneWrite() {
         EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder());