XStreamUtils.java

/*
 * Copyright 2017 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.kie.soup.xstream;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.core.ClassLoaderReference;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import com.thoughtworks.xstream.security.AnyTypePermission;
import com.thoughtworks.xstream.security.WildcardTypePermission;

import static com.thoughtworks.xstream.XStream.setupDefaultSecurity;

public class XStreamUtils {

    private static final String[] ALLOWLISTED_PACKAGES = new String[] {
        "org.kie.soup.project.datamodel.imports.Import",
        "org.drools.workbench.models.datamodel.imports.Import",
        "org.drools.core.command.**",
        "org.drools.core.base.*AgendaFilter",
        "org.drools.core.runtime.impl.ExecutionResultImpl",
        "org.drools.core.runtime.rule.impl.FlatQueryResults",
        "org.drools.core.factmodel.Fact",
        "org.drools.core.factmodel.Field",
        "org.drools.core.common.DefaultFactHandle",
        "org.drools.core.common.EventFactHandle",
        "org.drools.core.rule.KieModuleMetaInfo",
        "org.drools.core.rule.TypeMetaInfo",
        "org.drools.core.runtime.help.impl.XStreamXMLTest$Message"
    };

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    public static XStream createXStream() {
        return internalCreateXStream( new XStream() );
    }

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    public static XStream createXStream(HierarchicalStreamDriver hierarchicalStreamDriver) {
        return internalCreateXStream( new XStream(hierarchicalStreamDriver) );
    }

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    public static XStream createXStream(HierarchicalStreamDriver hierarchicalStreamDriver, ClassLoader classLoader) {
        return internalCreateXStream( new XStream(null, hierarchicalStreamDriver, new ClassLoaderReference( classLoader )) );
    }

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    public static XStream createXStream(ReflectionProvider reflectionProvider ) {
        return internalCreateXStream( new XStream(reflectionProvider) );
    }

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    public static XStream createXStream(ReflectionProvider reflectionProvider, Function<MapperWrapper, MapperWrapper> mapper ) {
        return internalCreateXStream( new XStream(reflectionProvider) {
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return mapper.apply( next );
            }
        });
    }

    /**
     * Vulnerable to CVE-210137285 variants. Do not use. Will be removed in the next few days!
     * @deprecated in favor of {@link #createTrustingXStream()} and {@link #createNonTrustingXStream()}
     */
    @Deprecated
    private static XStream internalCreateXStream( XStream xstream ) {
        setupDefaultSecurity(xstream);
        xstream.addPermission( new WildcardTypePermission( new String[] {
                "java.**", "javax.**", "org.kie.**", "org.drools.**", "org.jbpm.**", "org.optaplanner.**", "org.appformer.**"
        } ) );
        return xstream;
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream() {
        return internalCreateTrustingXStream( new XStream() );
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream(HierarchicalStreamDriver hierarchicalStreamDriver) {
        return internalCreateTrustingXStream( new XStream(hierarchicalStreamDriver) );
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream(HierarchicalStreamDriver hierarchicalStreamDriver, ClassLoader classLoader) {
        return internalCreateTrustingXStream( new XStream(null, hierarchicalStreamDriver, new ClassLoaderReference( classLoader )) );
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream(ReflectionProvider reflectionProvider ) {
        return internalCreateTrustingXStream( new XStream(reflectionProvider) );
    }
    
    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver hierarchicalStreamDriver ) {
        return internalCreateTrustingXStream( new XStream(reflectionProvider, hierarchicalStreamDriver) );
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    public static XStream createTrustingXStream(ReflectionProvider reflectionProvider, Function<MapperWrapper, MapperWrapper> mapper ) {
        return internalCreateTrustingXStream( new XStream(reflectionProvider) {
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return mapper.apply( next );
            }
        });
    }

    /**
     * Only use for XML or JSON that comes from a 100% trusted source.
     * The XML/JSON must be as safe as executable java code.
     * Otherwise, you MUST use {@link #createNonTrustingXStream()}.
     */
    private static XStream internalCreateTrustingXStream( XStream xstream ) {
        setupDefaultSecurity(xstream);
        // Presumes the XML content comes from a trusted source!
        xstream.addPermission(new AnyTypePermission());
        return xstream;
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream() {
        return internalCreateNonTrustingXStream( new XStream() );
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(HierarchicalStreamDriver hierarchicalStreamDriver) {
        return internalCreateNonTrustingXStream( new XStream(hierarchicalStreamDriver) );
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(HierarchicalStreamDriver hierarchicalStreamDriver, ClassLoader classLoader) {
        return internalCreateNonTrustingXStream( new XStream(null, hierarchicalStreamDriver, new ClassLoaderReference( classLoader )) );
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(HierarchicalStreamDriver hierarchicalStreamDriver,
                                                   ClassLoader classLoader,
                                                   BiFunction<HierarchicalStreamDriver, ClassLoaderReference, XStream> builder) {
        return internalCreateNonTrustingXStream(builder.apply(hierarchicalStreamDriver, new ClassLoaderReference(classLoader)));
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(ReflectionProvider reflectionProvider ) {
        return internalCreateNonTrustingXStream( new XStream(reflectionProvider) );
    }
    
    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(ReflectionProvider reflectionProvider, HierarchicalStreamDriver hierarchicalStreamDriver ) {
        return internalCreateNonTrustingXStream( new XStream(reflectionProvider, hierarchicalStreamDriver) );
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(ReflectionProvider reflectionProvider,
                                                   HierarchicalStreamDriver hierarchicalStreamDriver,
                                                   UnaryOperator<MapperWrapper> mapper) {
        return internalCreateNonTrustingXStream(new XStream(reflectionProvider, hierarchicalStreamDriver) {
            @Override
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return mapper.apply(next);
            }
        });
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    public static XStream createNonTrustingXStream(ReflectionProvider reflectionProvider, Function<MapperWrapper, MapperWrapper> mapper ) {
        return internalCreateNonTrustingXStream( new XStream(reflectionProvider) {
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return mapper.apply( next );
            }
        });
    }

    /**
     * Use for XML or JSON that might not come from a trusted source (such as REST services payloads, ...).
     * Automatically allowlists all classes with an {@link XStreamAlias} annotation.
     * Often requires allowlisting additional domain specific classes, which you'll need to expose in your API's.
     */
    private static XStream internalCreateNonTrustingXStream( XStream xstream ) {
        setupDefaultSecurity(xstream);
        // TODO remove if setupDefaultSecurity already does this.
        // See comment in https://github.com/x-stream/xstream/pull/99
        xstream.addPermission( new AnyAnnotationTypePermission());
        xstream.addPermission( new WildcardTypePermission( ALLOWLISTED_PACKAGES ) );
        // Do not add root permissions for "java", "org.kie" or the like here because that creates a security problem.
        // For more information, see http://x-stream.github.io/security.html and various xstream dev list conversations.
        // Instead, embrace a allowlist approach and expose that in your API's.
        return xstream;
    }

}