From fec80104f19883e6b9e37f2de55c15076da1b603 Mon Sep 17 00:00:00 2001 From: Simon Mavi Stewart Date: Thu, 2 Jul 2026 11:38:02 +0100 Subject: [PATCH] [java] Reject unescaped control characters in JSON strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 8259 §7 requires characters U+0000..U+001F to be escaped inside JSON strings. readString() previously appended them silently, so inputs containing a literal newline / tab / etc. inside quotes were accepted. --- java/src/org/openqa/selenium/json/JsonInput.java | 6 ++++++ .../org/openqa/selenium/json/JsonInputTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/java/src/org/openqa/selenium/json/JsonInput.java b/java/src/org/openqa/selenium/json/JsonInput.java index 4f4220f78fe0c..d40fb51f2b7b2 100644 --- a/java/src/org/openqa/selenium/json/JsonInput.java +++ b/java/src/org/openqa/selenium/json/JsonInput.java @@ -585,6 +585,12 @@ private String readString() { readEscape(builder); break; default: + // RFC 8259 §7: characters U+0000..U+001F MUST be escaped. + if (c < 0x20) { + throw new JsonException( + String.format( + "Illegal unescaped control character U+%04X in string. %s", c, input)); + } builder.append((char) c); } } diff --git a/java/test/org/openqa/selenium/json/JsonInputTest.java b/java/test/org/openqa/selenium/json/JsonInputTest.java index 73245c604e28a..99737c49b8618 100644 --- a/java/test/org/openqa/selenium/json/JsonInputTest.java +++ b/java/test/org/openqa/selenium/json/JsonInputTest.java @@ -309,6 +309,22 @@ void shouldReadU_FFFF_AsALiteralCharacterAndNotEndOfInput() { } } + @Test + void shouldRejectUnescapedControlCharactersInStrings() { + // RFC 8259 §7: characters U+0000..U+001F MUST be escaped in JSON strings. + // A literal newline / tab / etc. inside quotes is not valid JSON. + try (JsonInput input = newInput("\"a\nb\"")) { + assertThatExceptionOfType(JsonException.class) + .isThrownBy(input::nextString) + .withMessageStartingWith("Illegal unescaped control character"); + } + + // Escaped equivalents are still fine. + try (JsonInput input = newInput("\"a\\nb\"")) { + assertThat(input.nextString()).isEqualTo("a\nb"); + } + } + @Test void nullInputsShouldCoerceAsNullValues() throws IOException { try (InputStream is = new ByteArrayInputStream(new byte[0]);