231 lines
9.6 KiB
Diff
231 lines
9.6 KiB
Diff
From: Markus Koschany <apo@debian.org>
|
|
Date: Mon, 14 Nov 2022 22:40:03 +0100
|
|
Subject: CVE-2022-42003
|
|
|
|
Origin: https://github.com/FasterXML/jackson-databind/commit/d78d00ee7b5245b93103fef3187f70543d67ca33
|
|
|
|
---
|
|
.../databind/deser/std/StdDeserializer.java | 48 ++++++++---
|
|
.../dos/DeepArrayWrappingForDeser3590Test.java | 93 ++++++++++++++++++++++
|
|
2 files changed, 129 insertions(+), 12 deletions(-)
|
|
create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepArrayWrappingForDeser3590Test.java
|
|
|
|
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
|
|
index 9a6f482..cf46afc 100644
|
|
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
|
|
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
|
|
@@ -178,7 +178,9 @@ public abstract class StdDeserializer<T>
|
|
}
|
|
// [databind#381]
|
|
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- p.nextToken();
|
|
+ if (p.nextToken() == JsonToken.START_ARRAY) {
|
|
+ return (boolean) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final boolean parsed = _parseBooleanPrimitive(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -250,7 +252,9 @@ public abstract class StdDeserializer<T>
|
|
return 0;
|
|
case JsonTokenId.ID_START_ARRAY:
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- p.nextToken();
|
|
+ if (p.nextToken() == JsonToken.START_ARRAY) {
|
|
+ return (int) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final int parsed = _parseIntPrimitive(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -310,7 +314,9 @@ public abstract class StdDeserializer<T>
|
|
return 0L;
|
|
case JsonTokenId.ID_START_ARRAY:
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- p.nextToken();
|
|
+ if (p.nextToken() == JsonToken.START_ARRAY) {
|
|
+ return (long) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final long parsed = _parseLongPrimitive(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -356,7 +362,9 @@ public abstract class StdDeserializer<T>
|
|
return 0.0f;
|
|
case JsonTokenId.ID_START_ARRAY:
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- p.nextToken();
|
|
+ if (p.nextToken() == JsonToken.START_ARRAY) {
|
|
+ return (float) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final float parsed = _parseFloatPrimitive(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -417,7 +425,9 @@ public abstract class StdDeserializer<T>
|
|
return 0.0;
|
|
case JsonTokenId.ID_START_ARRAY:
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- p.nextToken();
|
|
+ if (p.nextToken() == JsonToken.START_ARRAY) {
|
|
+ return (double) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final double parsed = _parseDoublePrimitive(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -498,6 +508,9 @@ public abstract class StdDeserializer<T>
|
|
}
|
|
}
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
+ if (t == JsonToken.START_ARRAY) {
|
|
+ return (java.util.Date) handleNestedArrayForSingle(p, ctxt);
|
|
+ }
|
|
final Date parsed = _parseDate(p, ctxt);
|
|
_verifyEndArrayForSingle(p, ctxt);
|
|
return parsed;
|
|
@@ -662,11 +675,11 @@ public abstract class StdDeserializer<T>
|
|
}
|
|
}
|
|
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
|
|
- final T parsed = deserialize(p, ctxt);
|
|
+ final T parsed = _deserializeWrappedValue(p, ctxt);
|
|
if (p.nextToken() != JsonToken.END_ARRAY) {
|
|
handleMissingEndArrayForSingle(p, ctxt);
|
|
}
|
|
- return parsed;
|
|
+ return parsed;
|
|
}
|
|
} else {
|
|
t = p.getCurrentToken();
|
|
@@ -689,12 +702,8 @@ public abstract class StdDeserializer<T>
|
|
// 23-Mar-2017, tatu: Let's specifically block recursive resolution to avoid
|
|
// either supporting nested arrays, or to cause infinite looping.
|
|
if (p.hasToken(JsonToken.START_ARRAY)) {
|
|
- String msg = String.format(
|
|
-"Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s",
|
|
- ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY,
|
|
- "DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS");
|
|
@SuppressWarnings("unchecked")
|
|
- T result = (T) ctxt.handleUnexpectedToken(_valueClass, p.getCurrentToken(), p, msg);
|
|
+ T result = (T) handleNestedArrayForSingle(p, ctxt);
|
|
return result;
|
|
}
|
|
return (T) deserialize(p, ctxt);
|
|
@@ -1169,6 +1178,21 @@ handledType().getName());
|
|
// but for now just fall through
|
|
}
|
|
|
|
+ /**
|
|
+ * Helper method called when detecting a deep(er) nesting of Arrays when trying
|
|
+ * to unwrap value for {@code DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS}.
|
|
+ *
|
|
+ * @since 2.14
|
|
+ */
|
|
+ protected Object handleNestedArrayForSingle(JsonParser p, DeserializationContext ctxt) throws IOException
|
|
+ {
|
|
+ String msg = String.format(
|
|
+"Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s",
|
|
+ ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY,
|
|
+ "DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS");
|
|
+ return ctxt.handleUnexpectedToken(_valueClass, p.currentToken(), p, msg);
|
|
+ }
|
|
+
|
|
protected void _verifyEndArrayForSingle(JsonParser p, DeserializationContext ctxt) throws IOException
|
|
{
|
|
JsonToken t = p.nextToken();
|
|
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepArrayWrappingForDeser3590Test.java b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepArrayWrappingForDeser3590Test.java
|
|
new file mode 100644
|
|
index 0000000..8dfddcc
|
|
--- /dev/null
|
|
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepArrayWrappingForDeser3590Test.java
|
|
@@ -0,0 +1,93 @@
|
|
+package com.fasterxml.jackson.databind.deser.dos;
|
|
+
|
|
+import java.util.Date;
|
|
+
|
|
+import com.fasterxml.jackson.databind.*;
|
|
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
|
|
+
|
|
+public class DeepArrayWrappingForDeser3590Test extends BaseMapTest
|
|
+{
|
|
+ // 05-Sep-2022, tatu: Before fix, failed with 5000
|
|
+ private final static int TOO_DEEP_NESTING = 9999;
|
|
+
|
|
+ private final static String TOO_DEEP_DOC = _nestedDoc(TOO_DEEP_NESTING, "[ ", "] ", "123");
|
|
+
|
|
+ public void testArrayWrappingForBoolean() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Boolean.class);
|
|
+ _testArrayWrappingFor(Boolean.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForByte() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Byte.class);
|
|
+ _testArrayWrappingFor(Byte.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForShort() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Short.class);
|
|
+ _testArrayWrappingFor(Short.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForInt() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Integer.class);
|
|
+ _testArrayWrappingFor(Integer.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForLong() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Long.class);
|
|
+ _testArrayWrappingFor(Long.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForFloat() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Float.class);
|
|
+ _testArrayWrappingFor(Float.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForDouble() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Double.class);
|
|
+ _testArrayWrappingFor(Double.TYPE);
|
|
+ }
|
|
+
|
|
+ public void testArrayWrappingForDate() throws Exception
|
|
+ {
|
|
+ _testArrayWrappingFor(Date.class);
|
|
+ }
|
|
+
|
|
+ private void _testArrayWrappingFor(Class<?> cls) throws Exception
|
|
+ {
|
|
+ final ObjectMapper MAPPER = new ObjectMapper();
|
|
+ MAPPER.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
|
|
+ try {
|
|
+ MAPPER.readValue(TOO_DEEP_DOC, cls);
|
|
+ fail("Should not pass");
|
|
+ } catch (MismatchedInputException e) {
|
|
+ verifyException(e, "Cannot deserialize");
|
|
+ verifyException(e, "nested Arrays not allowed");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static String _nestedDoc(int nesting, String open, String close, String content) {
|
|
+ StringBuilder sb = new StringBuilder(nesting * (open.length() + close.length()));
|
|
+ for (int i = 0; i < nesting; ++i) {
|
|
+ sb.append(open);
|
|
+ if ((i & 31) == 0) {
|
|
+ sb.append("\n");
|
|
+ }
|
|
+ }
|
|
+ sb.append("\n").append(content).append("\n");
|
|
+ for (int i = 0; i < nesting; ++i) {
|
|
+ sb.append(close);
|
|
+ if ((i & 31) == 0) {
|
|
+ sb.append("\n");
|
|
+ }
|
|
+ }
|
|
+ return sb.toString();
|
|
+ }
|
|
+
|
|
+}
|