diff --git a/xstream-CVE-2013-7285.patch b/xstream-CVE-2013-7285.patch new file mode 100644 index 0000000..cc56623 --- /dev/null +++ b/xstream-CVE-2013-7285.patch @@ -0,0 +1,1136 @@ +From b235f65141d07e5dc44b5965657bbe1e539cc966 Mon Sep 17 00:00:00 2001 +From: Mikolaj Izdebski +Date: Tue, 11 Feb 2014 10:44:19 +0100 +Subject: [PATCH] Fix for CVE-2013-7285 + +Backported from upstream revision 2210 +--- + xstream/build.xml | 2 +- + .../src/java/com/thoughtworks/xstream/XStream.java | 148 ++++++++++++++- + .../thoughtworks/xstream/core/util/Primitives.java | 4 + + .../thoughtworks/xstream/mapper/CachingMapper.java | 28 ++- + .../xstream/mapper/SecurityMapper.java | 75 ++++++++ + .../xstream/security/AnyTypePermission.java | 35 ++++ + .../xstream/security/ArrayTypePermission.java | 35 ++++ + .../xstream/security/ExplicitTypePermission.java | 38 ++++ + .../xstream/security/ForbiddenClassException.java | 27 +++ + .../xstream/security/NoPermission.java | 40 ++++ + .../xstream/security/NoTypePermission.java | 39 ++++ + .../xstream/security/NullPermission.java | 27 +++ + .../xstream/security/PrimitiveTypePermission.java | 37 ++++ + .../xstream/security/RegExpTypePermission.java | 49 +++++ + .../xstream/security/TypePermission.java | 25 +++ + .../xstream/security/WildcardTypePermission.java | 84 +++++++++ + .../xstream/mapper/SecurityMapperTest.java | 207 +++++++++++++++++++++ + 17 files changed, 890 insertions(+), 10 deletions(-) + create mode 100644 xstream/src/java/com/thoughtworks/xstream/mapper/SecurityMapper.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/AnyTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/ArrayTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/ExplicitTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/NoPermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/NoTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/NullPermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/PrimitiveTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/RegExpTypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/TypePermission.java + create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/WildcardTypePermission.java + create mode 100644 xstream/src/test/com/thoughtworks/xstream/mapper/SecurityMapperTest.java + +diff --git a/xstream/build.xml b/xstream/build.xml +index 18ef35a..eaa187e 100644 +--- a/xstream/build.xml ++++ b/xstream/build.xml +@@ -62,7 +62,7 @@ + + + +- ++ + + + +diff --git a/xstream/src/java/com/thoughtworks/xstream/XStream.java b/xstream/src/java/com/thoughtworks/xstream/XStream.java +index 506e612..8af0f6a 100644 +--- a/xstream/src/java/com/thoughtworks/xstream/XStream.java ++++ b/xstream/src/java/com/thoughtworks/xstream/XStream.java +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes. +- * Copyright (C) 2006, 2007, 2008 XStream Committers. ++ * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD +@@ -92,8 +92,16 @@ import com.thoughtworks.xstream.mapper.Mapper; + import com.thoughtworks.xstream.mapper.MapperWrapper; + import com.thoughtworks.xstream.mapper.OuterClassMapper; + import com.thoughtworks.xstream.mapper.PackageAliasingMapper; ++import com.thoughtworks.xstream.mapper.SecurityMapper; + import com.thoughtworks.xstream.mapper.SystemAttributeAliasingMapper; + import com.thoughtworks.xstream.mapper.XStream11XmlFriendlyMapper; ++import com.thoughtworks.xstream.security.AnyTypePermission; ++import com.thoughtworks.xstream.security.ExplicitTypePermission; ++import com.thoughtworks.xstream.security.NoPermission; ++import com.thoughtworks.xstream.security.NoTypePermission; ++import com.thoughtworks.xstream.security.RegExpTypePermission; ++import com.thoughtworks.xstream.security.TypePermission; ++import com.thoughtworks.xstream.security.WildcardTypePermission; + + import java.io.EOFException; + import java.io.File; +@@ -132,6 +140,7 @@ import java.util.Set; + import java.util.TreeMap; + import java.util.TreeSet; + import java.util.Vector; ++import java.util.regex.Pattern; + + + /** +@@ -256,7 +265,7 @@ import java.util.Vector; + * The XStream instance is thread-safe. That is, once the XStream instance has been created and + * configured, it may be shared across multiple threads allowing objects to be + * serialized/deserialized concurrently. Note, that this only applies if annotations are not +- * auto-detected on -the-fly. ++ * auto-detected on-the-fly. + *

+ *

Implicit collections

+ *

+@@ -292,6 +301,7 @@ public class XStream { + private ImmutableTypesMapper immutableTypesMapper; + private ImplicitCollectionMapper implicitCollectionMapper; + private LocalConversionMapper localConversionMapper; ++ private SecurityMapper securityMapper; + private AnnotationConfiguration annotationConfiguration; + + private transient JVM jvm = new JVM(); +@@ -440,6 +450,7 @@ public class XStream { + this.mapper = mapper == null ? buildMapper() : mapper; + + setupMappers(); ++ setupSecurity(); + setupAliases(); + setupDefaultImplementations(); + setupConverters(); +@@ -470,6 +481,7 @@ public class XStream { + } + mapper = new LocalConversionMapper(mapper); + mapper = new ImmutableTypesMapper(mapper); ++ mapper = new SecurityMapper(mapper); + if (JVM.is15()) { + mapper = buildMapperDynamically( + ANNOTATION_MAPPER_TYPE, +@@ -521,9 +533,19 @@ public class XStream { + .lookupMapperOfType(ImmutableTypesMapper.class); + localConversionMapper = (LocalConversionMapper)this.mapper + .lookupMapperOfType(LocalConversionMapper.class); ++ securityMapper = (SecurityMapper)this.mapper ++ .lookupMapperOfType(SecurityMapper.class); + annotationConfiguration = (AnnotationConfiguration)this.mapper + .lookupMapperOfType(AnnotationConfiguration.class); + } ++ ++ protected void setupSecurity() { ++ if (securityMapper == null) { ++ return; ++ } ++ ++ addPermission(AnyTypePermission.ANY); ++ } + + protected void setupAliases() { + if (classAliasingMapper == null) { +@@ -1620,4 +1642,126 @@ public class XStream { + return this; + } + ++ /** ++ * Add a new security permission. ++ * ++ *

++ * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or ++ * {@link AnyTypePermission} will implicitly wipe any existing permission. ++ *

++ * ++ * @param permission the permission to add ++ * @since upcoming ++ */ ++ public void addPermission(TypePermission permission) { ++ if (securityMapper != null) { ++ securityMapper.addPermission(permission); ++ } ++ } ++ ++ /** ++ * Add security permission for explicit types by name. ++ * ++ * @param names the type names to allow ++ * @since upcoming ++ */ ++ public void allowTypes(String... names) { ++ addPermission(new ExplicitTypePermission(names)); ++ } ++ ++ /** ++ * Add security permission for types matching one of the specified regular expressions. ++ * ++ * @param regexps the regular expressions to allow type names ++ * @since upcoming ++ */ ++ public void allowTypesByRegExp(String... regexps) { ++ addPermission(new RegExpTypePermission(regexps)); ++ } ++ ++ /** ++ * Add security permission for types matching one of the specified regular expressions. ++ * ++ * @param regexps the regular expressions to allow type names ++ * @since upcoming ++ */ ++ public void allowTypesByRegExp(Pattern... regexps) { ++ addPermission(new RegExpTypePermission(regexps)); ++ } ++ ++ /** ++ * Add security permission for types matching one of the specified wildcard patterns. ++ *

++ * Supported are patterns with path expressions using dot as separator: ++ *

++ *
    ++ *
  • ?: one non-control character except separator, e.g. for 'java.net.Inet?Address'
  • ++ *
  • *: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'
  • ++ *
  • **: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'
  • ++ *
++ * ++ * @param patterns the patterns to allow type names ++ * @since upcoming ++ */ ++ public void allowTypesByWildcard(String... patterns) { ++ addPermission(new WildcardTypePermission(patterns)); ++ } ++ ++ /** ++ * Add security permission denying another one. ++ * ++ * @param permission the permission to deny ++ * @since upcoming ++ */ ++ public void denyPermission(TypePermission permission) { ++ addPermission(new NoPermission(permission)); ++ } ++ ++ /** ++ * Add security permission forbidding explicit types by name. ++ * ++ * @param names the type names to forbid ++ * @since upcoming ++ */ ++ public void denyTypes(String... names) { ++ denyPermission(new ExplicitTypePermission(names)); ++ } ++ ++ /** ++ * Add security permission forbidding types matching one of the specified regular expressions. ++ * ++ * @param regexps the regular expressions to forbid type names ++ * @since upcoming ++ */ ++ public void denyTypesByRegExp(String... regexps) { ++ denyPermission(new RegExpTypePermission(regexps)); ++ } ++ ++ /** ++ * Add security permission forbidding types matching one of the specified regular expressions. ++ * ++ * @param regexps the regular expressions to forbid type names ++ * @since upcoming ++ */ ++ public void denyTypesByRegExp(Pattern... regexps) { ++ denyPermission(new RegExpTypePermission(regexps)); ++ } ++ ++ /** ++ * Add security permission forbidding types matching one of the specified wildcard patterns. ++ *

++ * Supported are patterns with path expressions using dot as separator: ++ *

++ *
    ++ *
  • ?: one non-control character except separator, e.g. for 'java.net.Inet?Address'
  • ++ *
  • *: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'
  • ++ *
  • **: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'
  • ++ *
++ * ++ * @param patterns the patterns to forbid names ++ * @since upcoming ++ */ ++ public void denyTypesByWildcard(String... patterns) { ++ denyPermission(new WildcardTypePermission(patterns)); ++ } + } +diff --git a/xstream/src/java/com/thoughtworks/xstream/core/util/Primitives.java b/xstream/src/java/com/thoughtworks/xstream/core/util/Primitives.java +index f6e988e..e0f5fbf 100644 +--- a/xstream/src/java/com/thoughtworks/xstream/core/util/Primitives.java ++++ b/xstream/src/java/com/thoughtworks/xstream/core/util/Primitives.java +@@ -48,4 +48,8 @@ public final class Primitives { + static public Class unbox(final Class type) { + return (Class)UNBOX.get(type); + } ++ ++ static public boolean isBoxed(final Class type) { ++ return UNBOX.keySet().contains(type); ++ } + } +diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java +index 7e865c7..a90c194 100644 +--- a/xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java ++++ b/xstream/src/java/com/thoughtworks/xstream/mapper/CachingMapper.java +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2005 Joe Walnes. +- * Copyright (C) 2006, 2007, 2008 XStream Committers. ++ * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2014 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD +@@ -18,6 +18,9 @@ import java.util.Collections; + import java.util.HashMap; + import java.util.Map; + ++import com.thoughtworks.xstream.XStreamException; ++import com.thoughtworks.xstream.security.ForbiddenClassException; ++ + /** + * Mapper that caches which names map to which classes. Prevents repetitive searching and class loading. + * +@@ -43,15 +46,26 @@ public class CachingMapper extends MapperWrapper { + public Class realClass(String elementName) { + WeakReference reference = (WeakReference) realClassCache.get(elementName); + if (reference != null) { +- Class cached = (Class) reference.get(); ++ Object cached = reference.get(); + if (cached != null) { +- return cached; ++ if (cached instanceof Class) { ++ return (Class)cached; ++ } ++ throw (XStreamException)cached; + } + } +- +- Class result = super.realClass(elementName); +- realClassCache.put(elementName, new WeakReference(result)); +- return result; ++ ++ try { ++ Class result = super.realClass(elementName); ++ realClassCache.put(elementName, result); ++ return result; ++ } catch (ForbiddenClassException e) { ++ realClassCache.put(elementName, e); ++ throw e; ++ } catch (CannotResolveClassException e) { ++ realClassCache.put(elementName, e); ++ throw e; ++ } + } + + private Object readResolve() { +diff --git a/xstream/src/java/com/thoughtworks/xstream/mapper/SecurityMapper.java b/xstream/src/java/com/thoughtworks/xstream/mapper/SecurityMapper.java +new file mode 100644 +index 0000000..151bb91 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/mapper/SecurityMapper.java +@@ -0,0 +1,75 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 08. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.mapper; ++ ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++ ++import com.thoughtworks.xstream.security.AnyTypePermission; ++import com.thoughtworks.xstream.security.ForbiddenClassException; ++import com.thoughtworks.xstream.security.NoTypePermission; ++import com.thoughtworks.xstream.security.TypePermission; ++ ++ ++/** ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class SecurityMapper extends MapperWrapper { ++ ++ private final List permissions; ++ ++ /** ++ * Construct a SecurityMapper. ++ * ++ * @param wrapped the mapper chain ++ * @since upcoming ++ */ ++ public SecurityMapper(final Mapper wrapped) { ++ this(wrapped, (TypePermission[])null); ++ } ++ ++ /** ++ * Construct a SecurityMapper. ++ * ++ * @param wrapped the mapper chain ++ * @param permissions the predefined permissions ++ * @since upcoming ++ */ ++ public SecurityMapper(final Mapper wrapped, final TypePermission... permissions) { ++ super(wrapped); ++ this.permissions = permissions == null // ++ ? new ArrayList() ++ : new ArrayList(Arrays.asList(permissions)); ++ } ++ ++ /** ++ * Add a new permission. ++ *

++ * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or ++ * {@link AnyTypePermission} will implicitly wipe any existing permission. ++ *

++ * ++ * @param permission the permission to add. ++ * @since upcoming ++ */ ++ public void addPermission(final TypePermission permission) { ++ if (permission.equals(NoTypePermission.NONE) || permission.equals(AnyTypePermission.ANY)) ++ permissions.clear(); ++ permissions.add(permission); ++ } ++ ++ @Override ++ public Class realClass(final String elementName) { ++ final Class type = super.realClass(elementName); ++ for (final TypePermission permission : permissions) ++ if (permission.allows(type)) ++ return type; ++ throw new ForbiddenClassException(type); ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/AnyTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/AnyTypePermission.java +new file mode 100644 +index 0000000..a5ac1b6 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/AnyTypePermission.java +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 08. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * Permission for any type and null. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class AnyTypePermission implements TypePermission { ++ /** ++ * @since upcoming ++ */ ++ public static final TypePermission ANY = new AnyTypePermission(); ++ ++ @Override ++ public boolean allows(Class type) { ++ return true; ++ } ++ ++ @Override ++ public int hashCode() { ++ return 3; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ return obj != null && obj.getClass() == AnyTypePermission.class; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/ArrayTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/ArrayTypePermission.java +new file mode 100644 +index 0000000..0b425ef +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/ArrayTypePermission.java +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * Permission for any array type. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class ArrayTypePermission implements TypePermission { ++ /** ++ * @since upcoming ++ */ ++ public static final TypePermission ARRAYS = new ArrayTypePermission(); ++ ++ @Override ++ public boolean allows(Class type) { ++ return type != null && type.isArray(); ++ } ++ ++ @Override ++ public int hashCode() { ++ return 13; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ return obj != null && obj.getClass() == ArrayTypePermission.class; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/ExplicitTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/ExplicitTypePermission.java +new file mode 100644 +index 0000000..4294d72 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/ExplicitTypePermission.java +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++import java.util.Arrays; ++import java.util.Collections; ++import java.util.HashSet; ++import java.util.Set; ++ ++/** ++ * Explicit permission for a type with a name matching one in the provided list. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class ExplicitTypePermission implements TypePermission { ++ ++ final Set names; ++ ++ /** ++ * @since upcoming ++ */ ++ public ExplicitTypePermission(String...names) { ++ this.names = names == null ? Collections.emptySet() : new HashSet(Arrays.asList(names)); ++ } ++ ++ @Override ++ public boolean allows(Class type) { ++ if (type == null) ++ return false; ++ return names.contains(type.getName()); ++ } ++ ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java b/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java +new file mode 100644 +index 0000000..041e47a +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 08. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++import com.thoughtworks.xstream.XStreamException; ++ ++/** ++ * Exception thrown for a forbidden class. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class ForbiddenClassException extends XStreamException { ++ ++ /** ++ * Construct a ForbiddenClassException. ++ * @param type the forbidden class ++ * @since upcoming ++ */ ++ public ForbiddenClassException(Class type) { ++ super(type == null ? "null" : type.getName()); ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/NoPermission.java b/xstream/src/java/com/thoughtworks/xstream/security/NoPermission.java +new file mode 100644 +index 0000000..4845112 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/NoPermission.java +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * Wrapper to negate another type permission. ++ *

++ * If the wrapped {@link TypePermission} allows the type, this instance will throw a {@link ForbiddenClassException} ++ * instead. An instance of this permission cannot be used to allow a type. ++ *

++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class NoPermission implements TypePermission { ++ ++ private final TypePermission permission; ++ ++ /** ++ * Construct a NoPermission. ++ * ++ * @param permission the permission to negate or null to forbid any type ++ * @since upcoming ++ */ ++ public NoPermission(final TypePermission permission) { ++ this.permission = permission; ++ } ++ ++ @Override ++ public boolean allows(final Class type) { ++ if (permission == null || permission.allows(type)) { ++ throw new ForbiddenClassException(type); ++ } ++ return false; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/NoTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/NoTypePermission.java +new file mode 100644 +index 0000000..bfaa7e7 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/NoTypePermission.java +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 08. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * No permission for any type. ++ *

++ * Can be used to skip any existing default permission. ++ *

++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class NoTypePermission implements TypePermission { ++ ++ /** ++ * @since upcoming ++ */ ++ public static final TypePermission NONE = new NoTypePermission(); ++ ++ @Override ++ public boolean allows(Class type) { ++ throw new ForbiddenClassException(type); ++ } ++ ++ @Override ++ public int hashCode() { ++ return 1; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ return obj != null && obj.getClass() == NoTypePermission.class; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/NullPermission.java b/xstream/src/java/com/thoughtworks/xstream/security/NullPermission.java +new file mode 100644 +index 0000000..a1b0372 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/NullPermission.java +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++import com.thoughtworks.xstream.mapper.Mapper; ++ ++/** ++ * Permission for null or XStream's null replacement type. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class NullPermission implements TypePermission { ++ /** ++ * @since upcoming ++ */ ++ public static final TypePermission NULL = new NullPermission(); ++ ++ @Override ++ public boolean allows(Class type) { ++ return type == null || type == Mapper.Null.class; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/PrimitiveTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/PrimitiveTypePermission.java +new file mode 100644 +index 0000000..58257c2 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/PrimitiveTypePermission.java +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++import com.thoughtworks.xstream.core.util.Primitives; ++ ++/** ++ * Permission for any primitive type and its boxed counterpart. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class PrimitiveTypePermission implements TypePermission { ++ /** ++ * @since upcoming ++ */ ++ public static final TypePermission PRIMITIVES = new PrimitiveTypePermission(); ++ ++ @Override ++ public boolean allows(Class type) { ++ return type != null && type.isPrimitive() || Primitives.isBoxed(type); ++ } ++ ++ @Override ++ public int hashCode() { ++ return 7; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ return obj != null && obj.getClass() == PrimitiveTypePermission.class; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/RegExpTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/RegExpTypePermission.java +new file mode 100644 +index 0000000..d5c8b57 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/RegExpTypePermission.java +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++import java.util.regex.Pattern; ++ ++ ++/** ++ * Permission for any type with a name matching one of the provided regular expressions. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class RegExpTypePermission implements TypePermission { ++ ++ private final Pattern[] patterns; ++ ++ public RegExpTypePermission(final String... patterns) { ++ this(getPatterns(patterns)); ++ } ++ ++ public RegExpTypePermission(final Pattern... patterns) { ++ this.patterns = patterns == null ? new Pattern[0] : patterns; ++ } ++ ++ @Override ++ public boolean allows(final Class type) { ++ if (type != null) { ++ final String name = type.getName(); ++ for (final Pattern pattern : patterns) ++ if (pattern.matcher(name).matches()) ++ return true; ++ } ++ return false; ++ } ++ ++ private static Pattern[] getPatterns(final String... patterns) { ++ if (patterns == null) ++ return null; ++ final Pattern[] array = new Pattern[patterns.length]; ++ for (int i = 0; i < array.length; ++i) ++ array[i] = Pattern.compile(patterns[i]); ++ return array; ++ } ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/TypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/TypePermission.java +new file mode 100644 +index 0000000..7246c22 +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/TypePermission.java +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 08. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * Definition of a type permission. ++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public interface TypePermission { ++ /** ++ * Check permission for a provided type. ++ * ++ * @param type the type to check ++ * @return true if provided type is allowed, false if permission does not handle the type ++ * @throws ForbiddenClassException if provided type is explicitly forbidden ++ * @since upcoming ++ */ ++ boolean allows(Class type); ++} +diff --git a/xstream/src/java/com/thoughtworks/xstream/security/WildcardTypePermission.java b/xstream/src/java/com/thoughtworks/xstream/security/WildcardTypePermission.java +new file mode 100644 +index 0000000..ffa93de +--- /dev/null ++++ b/xstream/src/java/com/thoughtworks/xstream/security/WildcardTypePermission.java +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.security; ++ ++/** ++ * Permission for any type with a name matching one of the provided wildcard expressions. ++ * ++ *

++ * Supported are patterns with path expressions using dot as separator: ++ *

++ *
    ++ *
  • ?: one non-control character except separator, e.g. for 'java.net.Inet?Address'
  • ++ *
  • *: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'
  • ++ *
  • **: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'
  • ++ *
++ *

++ * The complete range of UTF-8 characters is supported except control characters. ++ *

++ * ++ * @author Jörg Schaible ++ * @since upcoming ++ */ ++public class WildcardTypePermission extends RegExpTypePermission { ++ ++ /** ++ * @since upcoming ++ */ ++ public WildcardTypePermission(final String... patterns) { ++ super(getRegExpPatterns(patterns)); ++ } ++ ++ private static String[] getRegExpPatterns(final String... wildcards) { ++ if (wildcards == null) ++ return null; ++ final String[] regexps = new String[wildcards.length]; ++ for (int i = 0; i < wildcards.length; ++i) { ++ final String wildcardExpression = wildcards[i]; ++ final StringBuilder result = new StringBuilder(wildcardExpression.length() * 2); ++ result.append("(?u)"); ++ final int length = wildcardExpression.length(); ++ for (int j = 0; j < length; j++) { ++ final char ch = wildcardExpression.charAt(j); ++ switch (ch) { ++ case '\\': ++ case '.': ++ case '+': ++ case '|': ++ case '[': ++ case ']': ++ case '(': ++ case ')': ++ case '^': ++ case '$': ++ result.append('\\').append(ch); ++ break; ++ ++ case '?': ++ result.append('.'); ++ break; ++ ++ case '*': ++ // see "General Category Property" in http://www.unicode.org/reports/tr18/ ++ if (j + 1 < length && wildcardExpression.charAt(j + 1) == '*') { ++ result.append("[\\P{C}]*"); ++ j++; ++ } else { ++ result.append("[\\P{C}&&[^").append('.').append("]]*"); ++ } ++ break; ++ ++ default: ++ result.append(ch); ++ break; ++ } ++ } ++ regexps[i] = result.toString(); ++ } ++ return regexps; ++ } ++} +diff --git a/xstream/src/test/com/thoughtworks/xstream/mapper/SecurityMapperTest.java b/xstream/src/test/com/thoughtworks/xstream/mapper/SecurityMapperTest.java +new file mode 100644 +index 0000000..ebde756 +--- /dev/null ++++ b/xstream/src/test/com/thoughtworks/xstream/mapper/SecurityMapperTest.java +@@ -0,0 +1,207 @@ ++/* ++ * Copyright (C) 2014 XStream Committers. ++ * All rights reserved. ++ * ++ * Created on 09. January 2014 by Joerg Schaible ++ */ ++package com.thoughtworks.xstream.mapper; ++ ++import java.net.URL; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++ ++import com.thoughtworks.xstream.core.JVM; ++import com.thoughtworks.xstream.core.util.QuickWriter; ++import com.thoughtworks.xstream.security.AnyTypePermission; ++import com.thoughtworks.xstream.security.ArrayTypePermission; ++import com.thoughtworks.xstream.security.ExplicitTypePermission; ++import com.thoughtworks.xstream.security.ForbiddenClassException; ++import com.thoughtworks.xstream.security.NoTypePermission; ++import com.thoughtworks.xstream.security.NullPermission; ++import com.thoughtworks.xstream.security.PrimitiveTypePermission; ++import com.thoughtworks.xstream.security.RegExpTypePermission; ++import com.thoughtworks.xstream.security.TypePermission; ++import com.thoughtworks.xstream.security.WildcardTypePermission; ++ ++import junit.framework.TestCase; ++ ++ ++/** ++ * Tests the {@link SecurityMapper} and the {@link TypePermission} implementations. ++ * ++ * @author Jörg Schaible ++ */ ++public class SecurityMapperTest extends TestCase { ++ ++ private SecurityMapper mapper; ++ private Map> classMap; ++ ++ @Override ++ protected void setUp() throws Exception { ++ super.setUp(); ++ ++ classMap = new HashMap>(); ++ mapper = new SecurityMapper(new MapperWrapper(null) { ++ @Override ++ public Class realClass(final String elementName) { ++ return classMap.get(elementName); ++ } ++ }); ++ } ++ ++ private void register(final Class... types) { ++ for (final Class type : types) { ++ classMap.put(type.getName(), type); ++ } ++ } ++ ++ public void testAnyType() { ++ register(String.class, URL.class, List.class); ++ mapper.addPermission(NoTypePermission.NONE); ++ mapper.addPermission(AnyTypePermission.ANY); ++ assertSame(String.class, mapper.realClass(String.class.getName())); ++ assertSame(List.class, mapper.realClass(List.class.getName())); ++ assertNull(mapper.realClass(null)); ++ } ++ ++ public void testNoType() { ++ register(String.class, URL.class, List.class); ++ mapper.addPermission(NoTypePermission.NONE); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ try { ++ mapper.realClass(null); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals("null", e.getMessage()); ++ } ++ } ++ ++ public void testNullType() { ++ register(String.class, Mapper.Null.class); ++ mapper.addPermission(NullPermission.NULL); ++ assertSame(Mapper.Null.class, mapper.realClass(Mapper.Null.class.getName())); ++ assertNull(mapper.realClass(null)); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ } ++ ++ public void testPrimitiveTypes() { ++ register(String.class, int.class, Integer.class, char[].class, Character[].class); ++ mapper.addPermission(PrimitiveTypePermission.PRIMITIVES); ++ assertSame(int.class, mapper.realClass(int.class.getName())); ++ assertSame(Integer.class, mapper.realClass(Integer.class.getName())); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ try { ++ mapper.realClass(null); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals("null", e.getMessage()); ++ } ++ try { ++ mapper.realClass(char[].class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(char[].class.getName(), e.getMessage()); ++ } ++ } ++ ++ public void testArrayTypes() { ++ register(String.class, int.class, Integer.class, char[].class, Character[].class); ++ mapper.addPermission(ArrayTypePermission.ARRAYS); ++ assertSame(char[].class, mapper.realClass(char[].class.getName())); ++ assertSame(Character[].class, mapper.realClass(Character[].class.getName())); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ try { ++ mapper.realClass(null); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals("null", e.getMessage()); ++ } ++ try { ++ mapper.realClass(int.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(int.class.getName(), e.getMessage()); ++ } ++ } ++ ++ public void testExplicitTypes() { ++ register(String.class, List.class); ++ mapper.addPermission(new ExplicitTypePermission(String.class.getName(), List.class.getName())); ++ assertSame(String.class, mapper.realClass(String.class.getName())); ++ assertSame(List.class, mapper.realClass(List.class.getName())); ++ try { ++ mapper.realClass(null); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals("null", e.getMessage()); ++ } ++ } ++ ++ public void testNamesWithRegExps() { ++ class Foo$_0 {} ++ final Class anonymous = new Object() {}.getClass(); ++ register(String.class, JVM.class, QuickWriter.class, Foo$_0.class, anonymous, DefaultClassMapperTest.class); ++ mapper.addPermission(new RegExpTypePermission(".*Test", ".*\\.core\\..*", ".*SecurityMapperTest\\$.+")); ++ assertSame(DefaultClassMapperTest.class, mapper.realClass(DefaultClassMapperTest.class.getName())); ++ assertSame(JVM.class, mapper.realClass(JVM.class.getName())); ++ assertSame(QuickWriter.class, mapper.realClass(QuickWriter.class.getName())); ++ assertSame(Foo$_0.class, mapper.realClass(Foo$_0.class.getName())); ++ assertSame(anonymous, mapper.realClass(anonymous.getName())); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ } ++ ++ public void testNamesWithWildcardPatterns() { ++ class Foo$_0 {} ++ class Foo$_1 {} ++ final Class anonymous = new Object() {}.getClass(); ++ register(String.class, JVM.class, QuickWriter.class, Foo$_0.class, Foo$_1.class, anonymous); ++ mapper.addPermission(new WildcardTypePermission("**.*_0", "**.core.*", "**.SecurityMapperTest$?")); ++ assertSame(JVM.class, mapper.realClass(JVM.class.getName())); ++ assertSame(Foo$_0.class, mapper.realClass(Foo$_0.class.getName())); ++ assertSame(anonymous, mapper.realClass(anonymous.getName())); ++ try { ++ mapper.realClass(String.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(String.class.getName(), e.getMessage()); ++ } ++ try { ++ mapper.realClass(QuickWriter.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(QuickWriter.class.getName(), e.getMessage()); ++ } ++ try { ++ mapper.realClass(Foo$_1.class.getName()); ++ fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); ++ } catch (final ForbiddenClassException e) { ++ assertEquals(Foo$_1.class.getName(), e.getMessage()); ++ } ++ } ++} +-- +1.8.4.2 + diff --git a/xstream.spec b/xstream.spec index 548ca3a..975720c 100644 --- a/xstream.spec +++ b/xstream.spec @@ -36,7 +36,7 @@ Name: xstream Version: 1.3.1 -Release: 8%{?dist} +Release: 9%{?dist} Summary: Java XML serialization library Group: Development/Libraries @@ -44,6 +44,9 @@ License: BSD URL: http://xstream.codehaus.org/ Source0: http://repository.codehaus.org/com/thoughtworks/%{name}/%{name}-distribution/%{version}/%{name}-distribution-%{version}-src.zip +# Backported from upstream revision 2210 +Patch0: %{name}-CVE-2013-7285.patch + BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: jpackage-utils >= 0:1.7.2 @@ -105,6 +108,7 @@ Requires: jpackage-utils %prep %setup -qn %{name}-%{version} +%patch0 -p1 find . -name "*.jar" -delete %if %with test @@ -195,6 +199,10 @@ dummy = posix.readlink(dir) and os.remove(dir) %changelog +* Tue Feb 11 2014 Mikolaj Izdebski - 1.3.1-9 +- Apply upstream security patch +- Resolves: CVE-2013-7285 + * Sun Aug 04 2013 Fedora Release Engineering - 1.3.1-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild