CVE-2022-42003 (Java, published 2022-10-02T05:15Z)
Description: value: In FasterXML jackson-databind before versions 2.13.4.1 and 2.12.17.1, resource exhaustion can occur because of a lack of a check in primitive value deserializers to avoid deep wrapper array nesting, when the UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-502 \u2014 Deserialization of Untrusted Data
The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
Repository: https://github.com/FasterXML/jackson-databind
Files changed:
release-notes/VERSION-2.x
src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
src/test/java/com/fasterxml/jackson/databind/deser/dos/DeepArrayWrappingForDeser3590Test.java
Commit message: Fix #3590


Vulnerable code:
            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");
            T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, msg);
                p.nextToken();
                p.nextToken();
                p.nextToken();
                p.nextToken();
                p.nextToken();
                p.nextToken();
                p.nextToken();

Fixed code:
#3590: Add check in primitive value deserializers to avoid deep wrapper array
  nesting wrt `UNWRAP_SINGLE_VALUE_ARRAYS`
            T result = (T) handleNestedArrayForSingle(p, ctxt);
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (boolean) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (byte) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (short) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (int) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (long) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (float) handleNestedArrayForSingle(p, ctxt);
                }
                if (p.nextToken() == JsonToken.START_ARRAY) {
                    return (double) handleNestedArrayForSingle(p, ctxt);
                }
                if (t == JsonToken.START_ARRAY) {
                    return (java.util.Date) handleNestedArrayForSingle(p, ctxt);
                }
    /**
     * 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(getValueType(ctxt), p.currentToken(), p, msg);
    }

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 ObjectMapper MAPPER = jsonMapperBuilder()
            .enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)
            .build();

    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
    {
        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();
    }

}