BoundsChecksForInputTest.java

package com.fasterxml.jackson.databind;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.fail;

import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.newJsonMapper;
import static com.fasterxml.jackson.databind.testutil.DatabindTestUtil.verifyException;

// Tests for verifying [databind#3572]
public class BoundsChecksForInputTest
{
    interface ByteBackedCreation {
        void call(byte[] data, int offset, int len) throws Exception;
    }

    interface CharBackedCreation {
        void call(char[] data, int offset, int len) throws Exception;
    }

    private final ObjectMapper MAPPER = newJsonMapper();
    private final ObjectReader OBJ_READER = MAPPER.reader();

    /*
    /**********************************************************************
    /* Test methods, byte[] backed
    /**********************************************************************
     */

    @Test
    public void testBoundsWithByteArrayInput() throws Exception {
        _testBoundsWithByteArrayInput(
                (data,offset,len)->MAPPER.createParser(data, offset, len));
        _testBoundsWithByteArrayInput(
                (data,offset,len)->OBJ_READER.createParser(data, offset, len));

        _testBoundsWithByteArrayInput(
                (data,offset,len)->MAPPER.readTree(data, offset, len));

        _testBoundsWithByteArrayInput(
                (data,offset,len)->MAPPER.readValue(data, offset, len, Object.class));
        _testBoundsWithByteArrayInput(
                (data,offset,len)->OBJ_READER.readValue(data, offset, len));

        final JavaType TYPE = MAPPER.constructType(String.class);
        _testBoundsWithByteArrayInput(
                (data,offset,len)->MAPPER.readValue(data, offset, len, TYPE));
    }

    private void _testBoundsWithByteArrayInput(ByteBackedCreation creator) throws Exception
    {
        final byte[] DATA = new byte[10];
        _testBoundsWithByteArrayInput(creator, DATA, -1, 1);
        _testBoundsWithByteArrayInput(creator, DATA, 4, -1);
        _testBoundsWithByteArrayInput(creator, DATA, 4, -6);
        _testBoundsWithByteArrayInput(creator, DATA, 9, 5);
        // and the integer overflow, too
        _testBoundsWithByteArrayInput(creator, DATA, Integer.MAX_VALUE, 4);
        _testBoundsWithByteArrayInput(creator, DATA, Integer.MAX_VALUE, Integer.MAX_VALUE);
        // and null checks too
        _testBoundsWithByteArrayInput(creator, null, 0, 3);
    }

    private void _testBoundsWithByteArrayInput(ByteBackedCreation creator,
            byte[] data, int offset, int len) throws Exception
    {
        try {
            creator.call(data, offset, len);
            fail("Should not pass");
        } catch (IllegalArgumentException e) {
            if (data == null) {
                // If it gets to TokenStreamFactory we'll have:
                // verifyException(e, "Invalid `byte[]` argument: `null`");
                // But ObjectMapper/ObjectReader use different exception
                verifyException(e, "argument \"");
                verifyException(e, "is null");
            } else {
                verifyException(e, "Invalid 'offset'");
                verifyException(e, "'len'");
                verifyException(e, "arguments for `byte[]` of length "+data.length);
            }
        }
    }

    /*
    /**********************************************************************
    /* Test methods, char[] backed
    /**********************************************************************
     */

    @Test
    public void testBoundsWithCharArrayInput() throws Exception {
        testBoundsWithCharArrayInput(
                (data,offset,len)->MAPPER.createParser(data, offset, len));
        testBoundsWithCharArrayInput(
                (data,offset,len)->OBJ_READER.createParser(data, offset, len));
    }

    private void testBoundsWithCharArrayInput(CharBackedCreation creator) throws Exception
    {
        final char[] DATA = new char[10];
        testBoundsWithCharArrayInput(creator, DATA, -1, 1);
        testBoundsWithCharArrayInput(creator, DATA, 4, -1);
        testBoundsWithCharArrayInput(creator, DATA, 4, -6);
        testBoundsWithCharArrayInput(creator, DATA, 9, 5);
        // and the integer overflow, too
        testBoundsWithCharArrayInput(creator, DATA, Integer.MAX_VALUE, 4);
        testBoundsWithCharArrayInput(creator, DATA, Integer.MAX_VALUE, Integer.MAX_VALUE);
        // and null checks too
        testBoundsWithCharArrayInput(creator, null, 0, 3);
    }

    private void testBoundsWithCharArrayInput(CharBackedCreation creator,
            char[] data, int offset, int len) throws Exception
    {
        try {
            creator.call(data, offset, len);
            fail("Should not pass");
        } catch (IllegalArgumentException e) {
            if (data == null) {
                // If it gets to TokenStreamFactory we'll have:
                // verifyException(e, "Invalid `char[]` argument: `null`");
                // But ObjectMapper/ObjectReader use different exception
                verifyException(e, "argument \"");
                verifyException(e, "is null");
            } else {
                verifyException(e, "Invalid 'offset'");
                verifyException(e, "'len'");
                verifyException(e, "arguments for `char[]` of length "+data.length);
            }
        }
    }
}