[xstream/f20] Apply upstream security patch

Mikolaj Izdebski mizdebsk at fedoraproject.org
Tue Feb 11 12:08:19 UTC 2014


commit 7711271576d9b9d1c4345ddf10ce843a1a2841bb
Author: Mikolaj Izdebski <mizdebsk at redhat.com>
Date:   Tue Feb 11 12:52:15 2014 +0100

    Apply upstream security patch
    
    - Resolves: CVE-2013-7285

 xstream-CVE-2013-7285.patch | 1136 +++++++++++++++++++++++++++++++++++++++++++
 xstream.spec                |   10 +-
 2 files changed, 1145 insertions(+), 1 deletions(-)
---
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 <mizdebsk at redhat.com>
+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 @@
+     </target>
+ 
+     <target name="compile:java3" depends="compile:init" description="Compile all minimum Java 1.3 specific">
+-        <javac srcdir="${java.src.dir}" destdir="${java.build.dir}" debug="${java.compile.debug}" source="1.3" target="1.3">
++        <javac srcdir="${java.src.dir}" destdir="${java.build.dir}" debug="${java.compile.debug}" source="1.5" target="1.5">
+             <exclude name="com/thoughtworks/xstream/converters/reflection/HarmonyReflectionProvider.java"/>
+             <excludesfile name="jdk-1.5-minimum.txt" />
+             <excludesfile name="jdk-1.5-specific.txt" unless="jdk1.5.available" />
+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. <em>Note, that this only applies if annotations are not 
+- * auto-detected on -the-fly.</em>
++ * auto-detected on-the-fly.</em>
+  * </p>
+  * <h3>Implicit collections</h3>
+  * <p/>
+@@ -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.
++     * 
++     * <p>
++     * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or
++     * {@link AnyTypePermission} will implicitly wipe any existing permission.
++     * </p>
++     * 
++     * @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.
++     * <p>
++     * Supported are patterns with path expressions using dot as separator:
++     * </p>
++     * <ul>
++     * <li>?: one non-control character except separator, e.g. for 'java.net.Inet?Address'</li>
++     * <li>*: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'</li>
++     * <li>**: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'</li>
++     * </ul>
++     * 
++     * @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.
++     * <p>
++     * Supported are patterns with path expressions using dot as separator:
++     * </p>
++     * <ul>
++     * <li>?: one non-control character except separator, e.g. for 'java.net.Inet?Address'</li>
++     * <li>*: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'</li>
++     * <li>**: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'</li>
++     * </ul>
++     * 
++     * @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&ouml;rg Schaible
++ * @since upcoming
++ */
++public class SecurityMapper extends MapperWrapper {
++
++    private final List<TypePermission> 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<TypePermission>()
++            : new ArrayList<TypePermission>(Arrays.asList(permissions));
++    }
++
++    /**
++     * Add a new permission.
++     * <p>
++     * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or
++     * {@link AnyTypePermission} will implicitly wipe any existing permission.
++     * </p>
++     * 
++     * @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 <code>null</code>.
++ * 
++ * @author J&ouml;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&ouml;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&ouml;rg Schaible
++ * @since upcoming
++ */
++public class ExplicitTypePermission implements TypePermission {
++
++    final Set<String> names;
++    
++    /**
++     * @since upcoming
++     */
++    public ExplicitTypePermission(String...names) {
++        this.names = names == null ? Collections.<String>emptySet() : new HashSet<String>(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&ouml;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.
++ * <p>
++ * 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.
++ * </p>
++ * 
++ * @author J&ouml;rg Schaible
++ * @since upcoming
++ */
++public class NoPermission implements TypePermission {
++
++    private final TypePermission permission;
++
++    /**
++     * Construct a NoPermission.
++     * 
++     * @param permission the permission to negate or <code>null</code> 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.
++ * <p>
++ * Can be used to skip any existing default permission.
++ * </p>
++ * 
++ * @author J&ouml;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 <code>null</code> or XStream's null replacement type.
++ * 
++ * @author J&ouml;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&ouml;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&ouml;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&ouml;rg Schaible
++ * @since upcoming
++ */
++public interface TypePermission {
++    /**
++     * Check permission for a provided type.
++     * 
++     * @param type the type to check
++     * @return <code>true</code> if provided type is allowed, <code>false</code> 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.
++ * 
++ * <p>
++ * Supported are patterns with path expressions using dot as separator:
++ * </p>
++ * <ul>
++ * <li>?: one non-control character except separator, e.g. for 'java.net.Inet?Address'</li>
++ * <li>*: arbitrary number of non-control characters except separator, e.g. for types in a package like 'java.lang.*'</li>
++ * <li>**: arbitrary number of non-control characters including separator, e.g. for types in a package and subpackages like 'java.lang.**'</li>
++ * </ul>
++ * <p>
++ * The complete range of UTF-8 characters is supported except control characters.
++ * </p>
++ * 
++ * @author J&ouml;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&ouml;rg Schaible
++ */
++public class SecurityMapperTest extends TestCase {
++
++    private SecurityMapper mapper;
++    private Map<String, Class<?>> classMap;
++
++    @Override
++    protected void setUp() throws Exception {
++        super.setUp();
++
++        classMap = new HashMap<String, Class<?>>();
++        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 <mizdebsk at redhat.com> - 1.3.1-9
+- Apply upstream security patch
+- Resolves: CVE-2013-7285
+
 * Sun Aug 04 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 1.3.1-8
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
 


More information about the scm-commits mailing list