Skip to content

Commit

Permalink
Merge branch '5.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Oct 24, 2022
2 parents 17aacf1 + 8d66153 commit db8d646
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 10 deletions.
4 changes: 3 additions & 1 deletion release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,14 @@ Project: woodstox
#78: Shade MSV dependency
- Add `module-info.class` for Java 9+ module system

5.3.1 (not yet released)
5.4.0 (not yet released)

#104: `NullPointerException` in `DTDValidator.validateElementEnd()`
for element undefined in DTD
(reported by ChrisTrenkamp@github)
#105: W3CSchemaFactory constructor incorrectly references relaxng
#160: Add limit and configuration setting for maximum nesting for DTD subsets
(contributed by @pjfanning)

5.3.0 (15-Jul-2019)

Expand Down
27 changes: 26 additions & 1 deletion src/main/java/com/ctc/wstx/api/ReaderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public final class ReaderConfig
public final static int DEFAULT_MAX_ENTITY_DEPTH = 500;
public final static int DEFAULT_MAX_ENTITY_COUNT = 100 * 1000;

// @since 5.4/6.4
public final static int DEFAULT_MAX_DTD_DEPTH = 500;

/*
///////////////////////////////////////////////////////////////////////
// Constants for reader properties:
Expand Down Expand Up @@ -133,7 +136,9 @@ public final class ReaderConfig
final static int PROP_MAX_TEXT_LENGTH = 66;
final static int PROP_MAX_ENTITY_COUNT = 67;
final static int PROP_MAX_ENTITY_DEPTH = 68;


final static int PROP_MAX_DTD_DEPTH = 69;

/*
////////////////////////////////////////////////
// Limits for numeric properties
Expand Down Expand Up @@ -341,6 +346,10 @@ public final class ReaderConfig
PROP_MAX_ENTITY_DEPTH);
sProperties.put(WstxInputProperties.P_MAX_ENTITY_COUNT,
PROP_MAX_ENTITY_COUNT);
// since 5.4/6.4
sProperties.put(WstxInputProperties.P_MAX_DTD_DEPTH,
PROP_MAX_DTD_DEPTH);

sProperties.put(WstxInputProperties.P_MAX_CHARACTERS, PROP_MAX_CHARACTERS);
sProperties.put(WstxInputProperties.P_CUSTOM_INTERNAL_ENTITIES,
Integer.valueOf(PROP_CUSTOM_INTERNAL_ENTITIES));
Expand Down Expand Up @@ -400,6 +409,9 @@ public final class ReaderConfig

protected int mMaxEntityDepth = DEFAULT_MAX_ENTITY_DEPTH;
protected long mMaxEntityCount = DEFAULT_MAX_ENTITY_COUNT;

// since 5.4/6.4
protected int mMaxDtdDepth = DEFAULT_MAX_DTD_DEPTH;

/**
* Base URL to use as the resolution context for relative entity
Expand Down Expand Up @@ -506,6 +518,7 @@ private ReaderConfig(ReaderConfig base,
mMaxTextLength = base.mMaxTextLength;
mMaxEntityDepth = base.mMaxEntityDepth;
mMaxEntityCount = base.mMaxEntityCount;
mMaxDtdDepth = base.mMaxDtdDepth;
}

/* Ok, let's then see if we can find a buffer recycler. Since they
Expand Down Expand Up @@ -569,6 +582,7 @@ public ReaderConfig createNonShared(SymbolTable sym)
rc.mMaxElementDepth = mMaxElementDepth;
rc.mMaxEntityDepth = mMaxEntityDepth;
rc.mMaxEntityCount = mMaxEntityCount;
rc.mMaxDtdDepth = mMaxDtdDepth;
if (mSpecialProperties != null) {
int len = mSpecialProperties.length;
Object[] specProps = new Object[len];
Expand Down Expand Up @@ -735,6 +749,8 @@ public boolean willAllowXml11EscapedCharsInXml10() {
public int getMaxEntityDepth() { return mMaxEntityDepth; }
public long getMaxEntityCount() { return mMaxEntityCount; }

public int getMaxDtdDepth() { return mMaxDtdDepth; }

public long getMaxCharacters() { return mMaxCharacters; }
public long getMaxTextLength() { return mMaxTextLength; }

Expand Down Expand Up @@ -1000,6 +1016,10 @@ public void setMaxEntityDepth(int value) {
public void setMaxEntityCount(long value) {
mMaxEntityCount = value;
}
// @since 5.4/6.4
public void setMaxDtdDepth(int value) {
mMaxDtdDepth = value;
}

public void setCustomInternalEntities(Map<String,?> m)
{
Expand Down Expand Up @@ -1498,6 +1518,8 @@ public Object getProperty(int id)
return getMaxEntityDepth();
case PROP_MAX_ENTITY_COUNT:
return getMaxEntityCount();
case PROP_MAX_DTD_DEPTH:
return getMaxDtdDepth();

case PROP_MIN_TEXT_SEGMENT:
return getShortestReportedTextSegment();
Expand Down Expand Up @@ -1686,6 +1708,9 @@ public boolean setProperty(String propName, int id, Object value)
case PROP_MAX_ENTITY_COUNT:
setMaxEntityCount(ArgUtil.convertToLong(propName, value, 1));
break;
case PROP_MAX_DTD_DEPTH:
setMaxDtdDepth(ArgUtil.convertToInt(propName, value, 1));
break;

case PROP_MIN_TEXT_SEGMENT:
setShortestReportedTextSegment(ArgUtil.convertToInt(propName, value, 1));
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/ctc/wstx/api/WstxInputProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ public final class WstxInputProperties

// // // Constraints on sizes of text segments parsed:


/**
* Property to specify shortest non-complete text segment (part of
* CDATA section or text content) that parser is allowed to return,
Expand Down Expand Up @@ -252,6 +251,15 @@ public final class WstxInputProperties
*/
public final static String P_MAX_ENTITY_DEPTH = "com.ctc.wstx.maxEntityDepth";

// and yet more size constraints (4.3+)

/**
* Maximum level of nesting of XML elements, starting with root element.
*
* @since 5.4 / 6.4
*/
public final static String P_MAX_DTD_DEPTH = "com.ctc.wstx.maxDtdDepth";

// // // Entity handling

/**
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/com/ctc/wstx/dtd/FullDTDReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,7 @@ private void handleElementDecl()
vldContent = XMLValidator.CONTENT_ALLOW_ANY_TEXT; // checked against DTD
} else {
--mInputPtr; // let's push it back...
ContentSpec spec = readContentSpec(elemName, true, mCfgFullyValidating);
ContentSpec spec = readContentSpec(elemName, mCfgFullyValidating, 0);
val = spec.getSimpleValidator();
if (val == null) {
val = new DFAValidator(DFAState.constructDFA(spec));
Expand Down Expand Up @@ -3044,13 +3044,14 @@ private StructValidator readMixedSpec(PrefixedName elemName, boolean construct)
return val;
}

/**
* @param mainLevel Whether this is the main-level content specification or nested
*/
private ContentSpec readContentSpec(PrefixedName elemName, boolean mainLevel,
boolean construct)
private ContentSpec readContentSpec(final PrefixedName elemName, final boolean construct,
final int recursionDepth)
throws XMLStreamException
{
verifyLimit("Maximum DTD nesting depth (WstxInputProperties.P_MAX_DTD_DEPTH)",
mConfig.getMaxDtdDepth(),
recursionDepth);

ArrayList<ContentSpec> subSpecs = new ArrayList<ContentSpec>();
boolean isChoice = false; // default to sequence
boolean choiceSet = false;
Expand Down Expand Up @@ -3082,7 +3083,7 @@ private ContentSpec readContentSpec(PrefixedName elemName, boolean mainLevel,
}
}
if (c == '(') {
ContentSpec cs = readContentSpec(elemName, false, construct);
ContentSpec cs = readContentSpec(elemName, construct, recursionDepth + 1);
subSpecs.add(cs);
continue;
}
Expand Down
23 changes: 23 additions & 0 deletions src/test/java/wstxtest/BaseWstxTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,29 @@ protected static String getAndVerifyText(XMLStreamReader sr)
return text;
}

protected byte[] readResource(String ref)
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
final byte[] buf = new byte[4000];

InputStream in = getClass().getResourceAsStream(ref);
if (in != null) {
try {
int len;
while ((len = in.read(buf)) > 0) {
bytes.write(buf, 0, len);
}
in.close();
} catch (IOException e) {
throw new RuntimeException("Failed to read resource '"+ref+"': "+e);
}
}
if (bytes.size() == 0) {
throw new IllegalArgumentException("Failed to read resource '"+ref+"': empty resource?");
}
return bytes.toByteArray();
}

/*
//////////////////////////////////////////////////
// Misc other helpers
Expand Down
80 changes: 80 additions & 0 deletions src/test/java/wstxtest/fuzz/Fuzz_DTDReadTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package wstxtest.fuzz;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import javax.xml.stream.XMLStreamReader;

import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.exc.WstxLazyException;
import com.ctc.wstx.stax.WstxInputFactory;
import org.codehaus.stax2.io.Stax2ByteArraySource;
import wstxtest.stream.BaseStreamTest;

public class Fuzz_DTDReadTest extends BaseStreamTest
{
private final byte[] DOC = readResource("/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt");

private final WstxInputFactory STAX_F = getWstxInputFactory();

public void testIssueInputStream() throws Exception
{
XMLStreamReader sr = STAX_F.createXMLStreamReader(new ByteArrayInputStream(DOC));
try {
streamThrough(sr);
fail("Should not pass");
} catch (WstxLazyException e) {
verifyException(e, "Maximum DTD nesting depth");
verifyException(e, "500");
}
sr.close();
}

public void testIssueInputStreamHigherRecursionLimit() throws Exception
{
final WstxInputFactory staxF = getWstxInputFactory();
staxF.setProperty(WstxInputProperties.P_MAX_DTD_DEPTH, 1100);

XMLStreamReader sr = staxF.createXMLStreamReader(new ByteArrayInputStream(DOC));
try {
streamThrough(sr);
fail("Should not pass");
} catch (WstxLazyException e) {
verifyException(e, "Maximum DTD nesting depth");
verifyException(e, "1100");
}
sr.close();
}

public void testIssueReader() throws Exception
{
Reader r = new InputStreamReader(new ByteArrayInputStream(DOC),
"UTF-8");
XMLStreamReader sr = STAX_F.createXMLStreamReader(r);
try {
streamThrough(sr);
fail("Should not pass");
} catch (WstxLazyException e) {
verifyException(e, "Maximum DTD nesting depth");
verifyException(e, "500");
}
sr.close();
}

public void testIssueStax2ByteArray() throws Exception
{
// Then "native" Byte array
Stax2ByteArraySource src = new Stax2ByteArraySource(DOC, 0, DOC.length);
XMLStreamReader sr = STAX_F.createXMLStreamReader(src);
try {
streamThrough(sr);
fail("Should not pass");
} catch (WstxLazyException e) {
verifyException(e, "Maximum DTD nesting depth");
verifyException(e, "500");
}
sr.close();
}
}

Large diffs are not rendered by default.

0 comments on commit db8d646

Please sign in to comment.