Skip to content

Commit

Permalink
Set additional security features on SecureSaxParserFactory. (#4472)
Browse files Browse the repository at this point in the history
Provide a way to override

Signed-off-by: Jan Supol <jan.supol@oracle.com>
  • Loading branch information
jansupol committed May 18, 2020
1 parent 0e7eb12 commit 549fc68
Show file tree
Hide file tree
Showing 15 changed files with 610 additions and 26 deletions.
10 changes: 9 additions & 1 deletion media/jaxb/pom.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -110,6 +110,14 @@
<unpackBundle>true</unpackBundle>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>2</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jaxb;

import org.glassfish.jersey.spi.Contract;

import javax.xml.parsers.SAXParserFactory;
import java.util.Collections;
import java.util.Map;

/** This supplier is used to set the features on the instances of the supported classes:
* <p><ul>
* <li>{@link javax.xml.parsers.SAXParserFactory}</li>
* <li>{@link javax.xml.transform.TransformerFactory}</li>
* </ul></p> using one of the methods:
* <p><ul>
* <li>{@link javax.xml.parsers.SAXParserFactory#setFeature(String, boolean)}</li>
* <li>{@link javax.xml.transform.TransformerFactory#setFeature(String, boolean)}</li>
* </ul></p>
*
* @since 2.31
*/
@Contract
public interface FeatureSupplier {

/**
* Define whether the feature set is for the instances of the given class.
* @param factoryClass the class for which instance the feature set is to be applied.
* @return true if this contract implementation is for the given class.
*/
boolean isFor(Class<?> factoryClass);

/**
* The feature set to be applied.
* @return the feature set {@code Map} with keys and {@code Boolean} values.
*/
Map<String, Boolean> getFeatures();

/**
* Supply a feature that disables disallow-doctype-decl feature and allows the ENTITY in the xml DOCTYPE.
* Registering this feature will override the settings of the secure {@link SAXParserFactory}.
* @return A feature that sets {@code http://apache.org/xml/features/disallow-doctype-decl} feature to false.
*/
static FeatureSupplier allowDoctypeDeclFeature() {
return new FeatureSupplier() {
@Override
public boolean isFor(Class<?> factoryClass) {
return SAXParserFactory.class == factoryClass;
}

@Override
public Map<String, Boolean> getFeatures() {
return Collections.singletonMap("http://apache.org/xml/features/disallow-doctype-decl", false);
}
};
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jaxb;

import org.glassfish.jersey.spi.Contract;

import java.util.Map;


/** This supplier is used to set the properties on the instances of the supported classes:
* <p><ul>
* <li>{@link javax.xml.parsers.DocumentBuilderFactory}</li>
* <li>{@link javax.xml.parsers.SAXParser}</li>
* <li>{@link javax.xml.stream.XMLInputFactory}</li>
* <li>{@link javax.xml.transform.TransformerFactory}</li>
* </ul></p> using of the methods
* <p><ul>
* <li>{@link javax.xml.parsers.DocumentBuilderFactory#setAttribute(String, Object)}</li>
* <li>{@link javax.xml.parsers.SAXParser#setProperty(String, Object)}</li>
* <li>{@link javax.xml.stream.XMLInputFactory#setProperty(String, Object)}</li>
* <li>{@link javax.xml.transform.TransformerFactory#setAttribute(String, Object)}</li>
* </ul></p>
*
* @since 2.31
*/
@Contract
public interface PropertySupplier {
/**
* Define whether the property set is for the instances of the given class.
* @param factoryOrParserClass the class for which instance the property set is to be applied.
* @return true if this contract implementation is for given class.
*/
boolean isFor(Class<?> factoryOrParserClass);

/**
* The properties to be applied.
* @return the property {@code Map} with keys and {@code Object} values to be applied.
*/
Map<String, Object> getProperties();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -16,6 +16,8 @@

package org.glassfish.jersey.jaxb.internal;

import org.glassfish.jersey.internal.inject.InjectionManager;

import javax.ws.rs.core.Configuration;

import javax.inject.Inject;
Expand All @@ -41,16 +43,21 @@ public DocumentBuilderFactoryInjectionProvider(final Configuration config) {
super(config);
}

@Inject
private InjectionManager injectionManager;

@Override
public DocumentBuilderFactory get() {
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();

f.setNamespaceAware(true);

if (!isXmlSecurityDisabled()) {
f.setExpandEntityReferences(false);
}

JaxbFeatureUtil.setProperties(injectionManager, DocumentBuilderFactory.class, f::setAttribute);

return f;
}

Expand Down
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jaxb.internal;

import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.jaxb.FeatureSupplier;
import org.glassfish.jersey.jaxb.PropertySupplier;
import org.glassfish.jersey.model.internal.RankedComparator;

import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;

/**
* Utility class that sets features and properties
*/
final class JaxbFeatureUtil {

private static final Logger LOGGER = Logger.getLogger(JaxbFeatureUtil.class.getName());
private static final RankedComparator<PropertySupplier> PROPERTY_COMPARATOR
= new RankedComparator<>(RankedComparator.Order.DESCENDING);
private static final RankedComparator<FeatureSupplier> FEATURE_COMPARATOR
= new RankedComparator<>(RankedComparator.Order.DESCENDING);

private JaxbFeatureUtil() {
}

static void setFeatures(InjectionManager injectionManager, Class<?> clazz, Settable<Boolean> consumer) {
if (injectionManager != null) {
final Iterable<FeatureSupplier> featureSuppliers
= Providers.getAllProviders(injectionManager, FeatureSupplier.class, FEATURE_COMPARATOR);
for (FeatureSupplier featureSupplier : featureSuppliers) {
if (featureSupplier.isFor(clazz)) {
for (Map.Entry<String, Boolean> entry : featureSupplier.getFeatures().entrySet()) {
setFeature(clazz, entry, consumer);
}
}
}
}
}

static void setProperties(InjectionManager injectionManager, Class<?> clazz, Settable<Object> consumer) {
if (injectionManager != null) {
final Iterable<PropertySupplier> propertySuppliers
= Providers.getAllProviders(injectionManager, PropertySupplier.class, PROPERTY_COMPARATOR);
for (PropertySupplier propertySupplier : propertySuppliers) {
if (propertySupplier.isFor(clazz)) {
for (Map.Entry<String, Object> entry : propertySupplier.getProperties().entrySet()) {
setProperty(clazz, entry, consumer);
}
}
}
}
}

static <T> void setProperty(Class<?> clazz, Map.Entry<String, T> settable, Settable<T> consumer) {
Optional<Exception> exception = consumer.accept(settable.getKey(), settable.getValue());
exception.ifPresent((ex) -> LOGGER.warning(LocalizationMessages.CANNOT_SET_PROPERTY(
settable.getKey(), settable.getValue(), clazz.getName(), ex)));
}

private static <T> void setFeature(Class<?> clazz, Map.Entry<String, T> settable, Settable<T> consumer) {
Optional<Exception> exception = consumer.accept(settable.getKey(), settable.getValue());
exception.ifPresent((ex) -> LOGGER.warning(LocalizationMessages.CANNOT_SET_FEATURE(
settable.getKey(), settable.getValue(), clazz.getName(), ex)));
}


@FunctionalInterface
static interface Settable<T> {
void set(String key, T t) throws javax.xml.parsers.ParserConfigurationException,
org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException,
javax.xml.transform.TransformerConfigurationException;

default Optional<Exception> accept(String key, T t) {
try {
set(key, t);
return Optional.empty();
} catch (Exception e) {
return Optional.of(e);
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -16,14 +16,26 @@

package org.glassfish.jersey.jaxb.internal;

import javax.ws.rs.core.Configuration;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.jaxb.FeatureSupplier;

import javax.inject.Inject;
import javax.ws.rs.core.Configuration;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* Thread-scoped injection provider of {@link SAXParserFactory SAX parser factories}.
*
* If {@link org.glassfish.jersey.message.MessageProperties#XML_SECURITY_DISABLE} is not set,
* the {@link SecureSaxParserFactory} is returned. By default, the {@code http://apache.org/xml/features/disallow-doctype-decl}
* feature is set to {@code TRUE}. To override this settings, it is possible to register the
* {@link FeatureSupplier#allowDoctypeDeclFeature()}.
*
* @see FeatureSupplier
*
* @author Paul Sandoz
* @author Marek Potociar
* @author Martin Matula
Expand All @@ -41,15 +53,21 @@ public SaxParserFactoryInjectionProvider(final Configuration config) {
super(config);
}

@Inject
private InjectionManager injectionManager;

@Override
public SAXParserFactory get() {
SAXParserFactory factory = SAXParserFactory.newInstance();
final SecureSaxParserFactory factory
= new SecureSaxParserFactory(SAXParserFactory.newInstance(), !isXmlSecurityDisabled());

factory.setNamespaceAware(true);

if (!isXmlSecurityDisabled()) {
factory = new SecureSaxParserFactory(factory);
}
final Map<String, Object> saxParserProperties = new LinkedHashMap<>();
JaxbFeatureUtil.setProperties(injectionManager, SAXParser.class, saxParserProperties::put);
factory.setSaxParserProperties(saxParserProperties);

JaxbFeatureUtil.setFeatures(injectionManager, SAXParserFactory.class, factory::setFeature);

return factory;
}
Expand Down

0 comments on commit 549fc68

Please sign in to comment.