From b7b0b20cbb2ca6ca8734fb0449370a6ec88999f9 Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Tue, 3 May 2022 13:54:18 +0200 Subject: [PATCH] Limit the size of toString() to a configurable limit (#272) --- .../co/elastic/clients/json/JsonpUtils.java | 60 +++++++++++++++++-- .../elasticsearch/json/JsonpUtilsTest.java | 23 +++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java index 9a0bb50bc..646cb6679 100644 --- a/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java +++ b/java-client/src/main/java/co/elastic/clients/json/JsonpUtils.java @@ -32,7 +32,6 @@ import jakarta.json.stream.JsonParsingException; import javax.annotation.Nullable; -import java.io.IOException; import java.io.StringReader; import java.io.Writer; import java.util.AbstractMap; @@ -242,15 +241,51 @@ public static void serializeIntOrNull(JsonGenerator generator, int value, int de } } + /** + * Renders a JsonpSerializable as a string by serializing it to JSON. Any object of an application-specific + * class in the object graph is rendered using that object's toString() representation as a JSON string value. + *

+ * The size of the string is limited to {@link #MAX_TO_STRING_LENGTH}. + * + * @see #MAX_TO_STRING_LENGTH + */ public static String toString(JsonpSerializable value) { return toString(value, ToStringMapper.INSTANCE, new StringBuilder()).toString(); } + /** + * Maximum length of the toString representation of a JsonpSerializable. + *

+ * The default is 10k characters, and can be changed globally by changing the value of this field. + */ + public static int MAX_TO_STRING_LENGTH = 10000; + + private static class ToStringTooLongException extends RuntimeException { + } + + /** + * Renders a JsonpSerializable as a string in a destination StringBuilderby serializing it to JSON. + *

+ * The size of the string is limited to {@link #MAX_TO_STRING_LENGTH}. + * + * @return the dest parameter, for chaining. + * @see #toString(JsonpSerializable) + * @see #MAX_TO_STRING_LENGTH + */ public static StringBuilder toString(JsonpSerializable value, JsonpMapper mapper, StringBuilder dest) { Writer writer = new Writer() { + int length = 0; @Override - public void write(char[] cbuf, int off, int len) throws IOException { - dest.append(cbuf, off, len); + public void write(char[] cbuf, int off, int len) { + int max = MAX_TO_STRING_LENGTH; + length += len; + if (length > max) { + dest.append(cbuf, off, len - (length - max)); + dest.append("..."); + throw new ToStringTooLongException(); + } else { + dest.append(cbuf, off, len); + } } @Override @@ -263,11 +298,26 @@ public void close() { }; JsonGenerator generator = mapper.jsonProvider().createGenerator(writer); - value.serialize(generator, mapper); - generator.close(); + try { + value.serialize(generator, mapper); + generator.close(); + } catch (ToStringTooLongException e) { + // Ignore + } return dest; } + /** + * Renders a JsonpSerializable as a string in a destination StringBuilderby serializing it to JSON. + * Any object of an application-specific class in the object graph is rendered using that object's toString() + * representation as a JSON string value. + *

+ * The size of the string is limited to {@link #MAX_TO_STRING_LENGTH}. + * + * @return the dest parameter, for chaining. + * @see #toString(JsonpSerializable) + * @see #MAX_TO_STRING_LENGTH + */ public static StringBuilder toString(JsonpSerializable value, StringBuilder dest) { return toString(value, ToStringMapper.INSTANCE, dest); } diff --git a/java-client/src/test/java/co/elastic/clients/elasticsearch/json/JsonpUtilsTest.java b/java-client/src/test/java/co/elastic/clients/elasticsearch/json/JsonpUtilsTest.java index 7edc1bdfe..851dcc4fc 100644 --- a/java-client/src/test/java/co/elastic/clients/elasticsearch/json/JsonpUtilsTest.java +++ b/java-client/src/test/java/co/elastic/clients/elasticsearch/json/JsonpUtilsTest.java @@ -83,6 +83,29 @@ public String toString() { } } + @Test + public void testLargeObjectToString() { + // Build a large string + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 1001; i++) { + sb.append("0123456789"); + } + + String text = sb.toString(); + assertEquals(10010, text.length()); + + Hit hit = Hit.of(h -> h + .source(text) + .index("idx") + .id("id1") + ); + + String toString = hit.toString(); + + assertEquals(10003, toString.length()); + assertTrue(toString.endsWith("...")); + } + @Test public void testSerializeDoubleOrNull() { // ---- Double values