Skip to content

Commit

Permalink
Update release notes wrt #1047, minor tweaking
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Aug 29, 2023
1 parent bc8433b commit e7ad6bb
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 125 deletions.
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ a pure JSON library.
#1041: Start using AssertJ in unit tests
#1042: Allow configuring spaces before and/or after the colon in `DefaultPrettyPrinter`
(contributed by @digulla)
#1047: Add configurable limit for the maximum length of Object property names
to parse before failing
(contributed by @pjfanning)
#1048: Add configurable processing limits for JSON generator (`StreamWriteConstraints`)
(contributed by @pjfanning)
#1050: Compare `_snapshotInfo` in `Version`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ public void validateNameLength(int length) throws StreamConstraintsException
{
if (length > _maxNameLen) {
throw _constructException(
"Name value length (%d) exceeds the maximum allowed (%d, from %s)",
"Name length (%d) exceeds the maximum allowed (%d, from %s)",
length, _maxNameLen,
_constrainRef("getMaxNameLength"));
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Arrays;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.ContentReference;
import com.fasterxml.jackson.core.io.NumberInput;
Expand Down Expand Up @@ -1569,6 +1570,16 @@ protected static int[] growArrayBy(int[] arr, int more) throws IllegalArgumentEx
return Arrays.copyOf(arr, len);
}

/* Helper method to call to expand "quad" buffer for name decoding
*
* @since 2.16
*/
protected int[] _growNameDecodeBuffer(int[] arr, int more) throws StreamConstraintsException {
// the following check will fail if the array is already bigger than is allowed for names
_streamReadConstraints.validateNameLength(arr.length << 2);
return growArrayBy(arr, more);
}

/*
/**********************************************************
/* Stuff that was abstract and required before 2.8, but that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1980,7 +1980,7 @@ protected final String parseLongName(int q, final int q2, int q3) throws IOExcep

// Nope, no end in sight. Need to grow quad array etc
if (qlen >= _quadBuffer.length) {
_quadBuffer = growArrayWithNameLenCheck(_quadBuffer, qlen);
_quadBuffer = _growNameDecodeBuffer(_quadBuffer, qlen);
}
_quadBuffer[qlen++] = q;
q = i;
Expand Down Expand Up @@ -2014,12 +2014,6 @@ private final String parseName(int q1, int ch, int lastQuadBytes) throws IOExcep
return parseEscapedName(_quadBuffer, 0, q1, ch, lastQuadBytes);
}

private int[] growArrayWithNameLenCheck(int[] arr, int more) throws StreamConstraintsException {
// the following check will fail if the array is already bigger than is allowed for names
_streamReadConstraints.validateNameLength(arr.length << 2);
return growArrayBy(_quadBuffer, more);
}

private final String parseName(int q1, int q2, int ch, int lastQuadBytes) throws IOException {
_quadBuffer[0] = q1;
return parseEscapedName(_quadBuffer, 1, q2, ch, lastQuadBytes);
Expand Down Expand Up @@ -2063,7 +2057,7 @@ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2079,7 +2073,7 @@ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2098,7 +2092,7 @@ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand All @@ -2114,7 +2108,7 @@ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
}
Expand Down Expand Up @@ -2174,7 +2168,7 @@ protected String _handleOddName(int ch) throws IOException
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand All @@ -2194,7 +2188,7 @@ protected String _handleOddName(int ch) throws IOException

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
}
Expand Down Expand Up @@ -2248,7 +2242,7 @@ protected String _parseAposName() throws IOException
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2264,7 +2258,7 @@ protected String _parseAposName() throws IOException
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2283,7 +2277,7 @@ protected String _parseAposName() throws IOException
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand All @@ -2299,7 +2293,7 @@ protected String _parseAposName() throws IOException

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
}
Expand Down Expand Up @@ -2364,7 +2358,7 @@ private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadB
throws JsonParseException, StreamConstraintsException
{
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = _padLastQuad(lastQuad, lastQuadBytes);
String name = _symbols.findName(quads, qlen);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.JsonReadFeature;
Expand Down Expand Up @@ -2097,7 +2096,7 @@ private final JsonToken _parseEscapedName(int qlen, int currQuad, int currQuadBy
continue;
}
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand Down Expand Up @@ -2130,7 +2129,7 @@ private final JsonToken _parseEscapedName(int qlen, int currQuad, int currQuadBy
// 7-bit ASCII. Gets pretty messy. If this happens often, may
// want to use different name canonicalization to avoid these hits.
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
if (ch > 127) {
// Ok, we'll need room for first byte right away
Expand Down Expand Up @@ -2170,7 +2169,7 @@ private final JsonToken _parseEscapedName(int qlen, int currQuad, int currQuadBy

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
} else if (qlen == 0) { // rare, but may happen
Expand Down Expand Up @@ -2260,7 +2259,7 @@ private JsonToken _finishUnquotedName(int qlen, int currQuad, int currQuadBytes)
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand All @@ -2270,7 +2269,7 @@ private JsonToken _finishUnquotedName(int qlen, int currQuad, int currQuadBytes)

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
}
Expand Down Expand Up @@ -2320,7 +2319,7 @@ private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes)
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2336,7 +2335,7 @@ private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes)
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
Expand All @@ -2355,7 +2354,7 @@ private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes)
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
Expand All @@ -2365,7 +2364,7 @@ private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes)

if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayWithNameLenCheck(quads, quads.length);
_quadBuffer = quads = _growNameDecodeBuffer(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
} else if (qlen == 0) { // rare case but possible
Expand All @@ -2387,7 +2386,7 @@ protected final JsonToken _finishFieldWithEscape() throws IOException
return JsonToken.NOT_AVAILABLE;
}
if (_quadLength >= _quadBuffer.length) {
_quadBuffer = growArrayWithNameLenCheck(_quadBuffer, 32);
_quadBuffer = _growNameDecodeBuffer(_quadBuffer, 32);
}
int currQuad = _pending32;
int currQuadBytes = _pendingBytes;
Expand Down Expand Up @@ -2430,12 +2429,6 @@ protected final JsonToken _finishFieldWithEscape() throws IOException
return _parseEscapedName(_quadLength, currQuad, currQuadBytes);
}

private int[] growArrayWithNameLenCheck(int[] arr, int more) throws StreamConstraintsException {
// the following check will fail if the array is already bigger than is allowed for names
_streamReadConstraints.validateNameLength(arr.length << 2);
return growArrayBy(_quadBuffer, more);
}

private int _decodeSplitEscaped(int value, int bytesRead) throws IOException
{
if (_inputPtr >= _inputEnd) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,8 @@ public String findSymbol(char[] buffer, int start, int len, int h) throws IOExce
if (len < 1) { // empty Strings are simplest to handle up front
return "";
}
_streamReadConstraints.validateNameLength(len);
if (!_canonicalize) { // [JACKSON-259]
_streamReadConstraints.validateNameLength(len);
return new String(buffer, start, len);
}

Expand Down Expand Up @@ -536,6 +536,7 @@ public String findSymbol(char[] buffer, int start, int len, int h) throws IOExce
}
}
}
_streamReadConstraints.validateNameLength(len);
return _addSymbol(buffer, start, len, h, index);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.fasterxml.jackson.core.constraints;

import java.io.IOException;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.StreamReadConstraints;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;

// [core#1047]: Add max-name-length constraints
public class LargeNameReadTest extends BaseTest
{
private final JsonFactory JSON_F_DEFAULT = newStreamFactory();

private final JsonFactory JSON_F_NAME_100 = JsonFactory.builder()
.streamReadConstraints(StreamReadConstraints.builder().maxNameLength(100).build())
.build();

// Test name that is below default max name
public void testLargeNameBytes() throws Exception {
final String doc = generateJSON(StreamReadConstraints.defaults().getMaxNameLength() - 100);
try (JsonParser p = createParserUsingStream(JSON_F_DEFAULT, doc, "UTF-8")) {
consumeTokens(p);
}
}

public void testLargeNameChars() throws Exception {
final String doc = generateJSON(StreamReadConstraints.defaults().getMaxNameLength() - 100);
try (JsonParser p = createParserUsingReader(JSON_F_DEFAULT, doc)) {
consumeTokens(p);
}
}

public void testLargeNameWithSmallLimitBytes() throws Exception
{
final String doc = generateJSON(1000);
try (JsonParser p = createParserUsingStream(JSON_F_NAME_100, doc, "UTF-8")) {
consumeTokens(p);
fail("expected StreamConstraintsException");
} catch (StreamConstraintsException e) {
verifyException(e, "Name length");
}
}

public void testLargeNameWithSmallLimitCharss() throws Exception
{
final String doc = generateJSON(1000);
try (JsonParser p = createParserUsingReader(JSON_F_NAME_100, doc)) {
consumeTokens(p);
fail("expected StreamConstraintsException");
} catch (StreamConstraintsException e) {
verifyException(e, "Name length");
}
}

public void testLargeNameWithSmallLimitAsync() throws Exception
{
final byte[] doc = utf8Bytes(generateJSON(1000));

try (NonBlockingJsonParser p = (NonBlockingJsonParser) JSON_F_NAME_100.createNonBlockingByteArrayParser()) {
p.feedInput(doc, 0, doc.length);
consumeTokens(p);
fail("expected StreamConstraintsException");
} catch (StreamConstraintsException e) {
verifyException(e, "Name length");
}
}

private void consumeTokens(JsonParser p) throws IOException {
while (p.nextToken() != null) {
;
}
}

private String generateJSON(final int nameLen) {
final StringBuilder sb = new StringBuilder();
sb.append("{\"");
for (int i = 0; i < nameLen; i++) {
sb.append("a");
}
sb.append("\":\"value\"}");
return sb.toString();
}
}

0 comments on commit e7ad6bb

Please sign in to comment.