Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 63 additions & 46 deletions xstream/src/java/com/thoughtworks/xstream/XStream.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 20. February 2006 by Mauro Talevi
*/
package com.thoughtworks.xstream.converters;

import com.thoughtworks.xstream.core.SecurityUtils;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

Expand Down Expand Up @@ -46,6 +47,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC
}

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
SecurityUtils.checkFieldValueLimit(context, reader.getValue());
return fromString(reader.getValue());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.Caching;
import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
import com.thoughtworks.xstream.core.SecurityUtils;
import com.thoughtworks.xstream.core.util.ArrayIterator;
import com.thoughtworks.xstream.core.util.Fields;
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
Expand Down Expand Up @@ -62,7 +63,7 @@ public AbstractReflectionConverter(Mapper mapper, ReflectionProvider reflectionP
serializationMethodInvoker = new SerializationMethodInvoker();
serializationMembers = serializationMethodInvoker.serializationMembers;
}

protected boolean canAccess(Class type) {
try {
reflectionProvider.getFieldOrNull(type, "%");
Expand Down Expand Up @@ -283,6 +284,9 @@ public Object doUnmarshal(final Object result, final HierarchicalStreamReader re
final Class resultType = result.getClass();
final MemberDictionary seenFields = new MemberDictionary();

SecurityUtils.checkDepthLimit(context, reader);
int currentFieldCount = 0;

// process attributes before recursing into child elements.
Iterator it = reader.getAttributeNames();
while (it.hasNext()) {
Expand Down Expand Up @@ -417,6 +421,8 @@ public Object doUnmarshal(final Object result, final HierarchicalStreamReader re
type = mapper.defaultImplementationOf(field.getType());
}
// TODO the reflection provider should already return the proper field
currentFieldCount += 1;
SecurityUtils.checkFieldLimit(context, currentFieldCount);
value = unmarshallField(context, result, type, field);
Class definedType = field.getType();
if (!definedType.isPrimitive()) {
Expand Down
29 changes: 26 additions & 3 deletions xstream/src/java/com/thoughtworks/xstream/core/SecurityUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
package com.thoughtworks.xstream.core;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.security.InputManipulationException;


Expand All @@ -26,11 +28,11 @@ public class SecurityUtils {

/**
* Check the consumed time adding elements to collections or maps.
*
*
* Every custom converter should call this method after an unmarshalled element has been added to a collection or
* map. In case of an attack the operation will take too long, because the calculation of the hash code or the
* comparison of the elements in the collection operate on recursive structures.
*
* comparison of the elements in the collection operate on recursive structures.
*
* @param context the unmarshalling context
* @param start the timestamp just before the element was added to the collection or map
* @since 1.4.19
Expand All @@ -53,4 +55,25 @@ public static void checkForCollectionDoSAttack(final UnmarshallingContext contex
}
}
}

public static void checkDepthLimit(final UnmarshallingContext context, HierarchicalStreamReader reader) {
Integer maxAllowedDepth = (Integer)context.get(XStream.MAX_ALLOWED_DEPTH);
if(maxAllowedDepth != null && reader.getLevel() > maxAllowedDepth) {
throw new XStreamException("XML depth exceeds maximum allowed depth of " + maxAllowedDepth);
}
}

public static void checkFieldLimit(final UnmarshallingContext context, int fieldsLength) {
Integer maxAllowedFields = (Integer)context.get(XStream.MAX_ALLOWED_FIELDS);
if(maxAllowedFields != null && fieldsLength > maxAllowedFields) {
throw new XStreamException("Encountered more fields than the maximum allowed size of " + maxAllowedFields);
}
}

public static void checkFieldValueLimit(final UnmarshallingContext context, String value) {
Integer maxAllowedValue = (Integer)context.get(XStream.MAX_ALLOWED_VALUE);
if(maxAllowedValue != null && value.length() > maxAllowedValue) {
throw new XStreamException("Size of value longer than the maximum allowed size of " + maxAllowedValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 07. March 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.io;
Expand Down Expand Up @@ -47,6 +47,15 @@ public interface HierarchicalStreamReader extends ErrorReporter {
*/
String getValue();

/**
* Retrieve the current nesting level. The method counts the number of unbalanced calls to {@link #moveDown()} and
* {@link #moveUp()}.
*
* @return the current nesting level
* @since upcoming
*/
int getLevel();

/**
* Get the value of an attribute of the current node.
* <p>
Expand All @@ -63,7 +72,7 @@ public interface HierarchicalStreamReader extends ErrorReporter {
* </p>
*/
String getAttribute(int index);

/**
* Number of attributes in current node.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 10. April 2005 by Joe Walnes
*/
package com.thoughtworks.xstream.io;
Expand Down Expand Up @@ -44,6 +44,11 @@ public String getNodeName() {
return wrapped.getNodeName();
}

@Override
public int getLevel() {
return wrapped.getLevel();
}

public String getValue() {
return wrapped.getValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 04. June 2006 by Joe Walnes
*/
package com.thoughtworks.xstream.io.binary;
Expand Down Expand Up @@ -148,6 +148,11 @@ public void moveUp() {
pushBack(nextToken);
}

@Override
public int getLevel() {
return depthState.getLevel();
}

private Token readToken() {
if (pushback == null) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 04. June 2006 by Joe Walnes
*/
package com.thoughtworks.xstream.io.binary;
Expand Down Expand Up @@ -34,6 +34,7 @@ private static class State {
List attributes;
boolean hasMoreChildren;
State parent;
int level;
}

private static class Attribute {
Expand All @@ -46,13 +47,18 @@ private static class Attribute {
public void push() {
State newState = new State();
newState.parent = current;
newState.level = getLevel() + 1;
current = newState;
}

public void pop() {
current = current.parent;
}

public int getLevel() {
return current != null ? current.level : 0;
}

public String getName() {
return current.name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 24. April 2005 by Joe Walnes
*/
package com.thoughtworks.xstream.io.xml;
Expand All @@ -29,7 +29,7 @@ protected AbstractDocumentReader(Object rootElement) {

/**
* @since 1.4
*/
*/
protected AbstractDocumentReader(Object rootElement, NameCoder nameCoder) {
super(nameCoder);
this.current = rootElement;
Expand All @@ -40,11 +40,11 @@ protected AbstractDocumentReader(Object rootElement, NameCoder nameCoder) {
/**
* @since 1.2
* @deprecated As of 1.4, use {@link AbstractDocumentReader#AbstractDocumentReader(Object, NameCoder)} instead.
*/
*/
protected AbstractDocumentReader(Object rootElement, XmlFriendlyReplacer replacer) {
this(rootElement, (NameCoder)replacer);
}

protected abstract void reassignCurrentElement(Object current);
protected abstract Object getParent();
protected abstract Object getChild(int index);
Expand Down Expand Up @@ -84,9 +84,14 @@ public Iterator getAttributeNames() {
return new AttributeNameIterator(this);
}

@Override
public int getLevel() {
return pointers.size();
}

public void appendErrors(ErrorWriter errorWriter) {
}

public Object getCurrent() {
return this.current;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 24. April 2005 by Joe Walnes
*/
package com.thoughtworks.xstream.io.xml;
Expand Down Expand Up @@ -58,7 +58,7 @@ protected AbstractPullReader(NameCoder nameCoder) {
protected AbstractPullReader(XmlFriendlyReplacer replacer) {
this((NameCoder)replacer);
}


/**
* Pull the next event from the stream.
Expand Down Expand Up @@ -114,6 +114,11 @@ public void moveUp() {
}
}

@Override
public int getLevel() {
return elementStack.size();
}

private void move() {
final Event event = readEvent();
pool.push(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 09. December 2005 by Joe Walnes
*/
package com.thoughtworks.acceptance;
Expand All @@ -24,6 +24,7 @@
import com.thoughtworks.xstream.io.ReaderWrapper;
import com.thoughtworks.xstream.io.xml.MXParserDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.Xpp3Driver;
import com.thoughtworks.xstream.io.xml.XppReader;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.testutil.CallLog;
Expand Down Expand Up @@ -233,6 +234,41 @@ public void testFailSafeDeserialization() throws IOException, ClassNotFoundExcep
ois.close();
}

public void testFailSafeDeserializationWithHierarchicalStreamReader() throws IOException, ClassNotFoundException {
final String xml = ""
+ "<object-stream>\n"
+ " <string>top</string>\n"
+ " <list>\n"
+ " <string>first</string>\n"
+ " <int-array>\n"
+ " <int>1</int>\n"
+ " <int>invalid</int>\n" // deserialization will fail here
+ " <int>3</int>\n"
+ " </int-array>\n"
+ " <string>last</string>\n"
+ " </list>\n"
+ " <string>bottom</string>\n"
+ "</object-stream>";

@SuppressWarnings("resource")
final HierarchicalStreamReader reader = new Xpp3Driver().createReader(new StringReader(xml));
final ObjectInputStream ois = xstream.createObjectInputStream(reader);
final int level = reader.getLevel();
assertEquals(1, level);
assertEquals("top", ois.readObject());
try {
ois.readObject();
fail("Thrown " + ConversionException.class.getName() + " expected");
} catch (final ConversionException e) {
assertEquals(4, reader.getLevel());
do {
reader.moveUp();
} while (level != reader.getLevel());
}
assertEquals("bottom", ois.readObject());
ois.close();
}

public void testObjectOutputStreamPropagatesCloseAndFlushEvents() throws IOException {
// setup
final CallLog log = new CallLog();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.thoughtworks.acceptance.someobjects;


public class Employee extends TestPerson {
private String employeeId;
private String department;

public Employee(String firstname, String lastname, PhoneNumber phone, String employeeId, String department) {
super(firstname, lastname, phone);
this.employeeId = employeeId;
this.department = department;
}

@Override
public String toString() {
return "Employee{" +
"employeeId='" + employeeId + '\'' +
", department='" + department + '\'' +
"} " + super.toString();
}
}
Loading