Skip to content

Commit

Permalink
SOLR-12450: Don't allow referal to external resources in various conf…
Browse files Browse the repository at this point in the history
…ig files

# Conflicts:
#	solr/CHANGES.txt
  • Loading branch information
uschindler committed Jun 17, 2018
1 parent 359fa15 commit e5407c5
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 128 deletions.
3 changes: 3 additions & 0 deletions solr/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ Bug Fixes
but 'bin/solr create' tells users to use the default action 'set-property', which fails
because the property is not editable. (Steve Rowe)

* SOLR-12450: Don't allow referal to external resources in various config files.
(Yuyang Xiao, Uwe Schindler)

Optimizations
----------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,31 @@
*/
package org.apache.solr.handler.extraction;

import javax.xml.parsers.DocumentBuilderFactory;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.util.SafeXMLParsing;
import org.apache.tika.parser.ParseContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ParseContextConfig {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private final Map<Class<?>, Object> entries = new HashMap<>();

/** Creates an empty Config without any settings (used as placeholder). */
Expand All @@ -54,9 +58,7 @@ public ParseContextConfig(SolrResourceLoader resourceLoader, String parseContext
}

private static Document loadConfigFile(SolrResourceLoader resourceLoader, String parseContextConfigLoc) throws Exception {
try (InputStream in = resourceLoader.openResource(parseContextConfigLoc)) {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in, parseContextConfigLoc);
}
return SafeXMLParsing.parseConfigXML(log, resourceLoader, parseContextConfigLoc);
}

private void extract(Element element, SolrResourceLoader loader) throws Exception {
Expand Down
101 changes: 42 additions & 59 deletions solr/core/src/java/org/apache/solr/schema/AbstractEnumField.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@
package org.apache.solr.schema;

import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
Expand All @@ -38,8 +36,10 @@
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.solr.common.EnumFieldValue;
import org.apache.solr.common.SolrException;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.response.TextResponseWriter;
import org.apache.solr.search.QParser;
import org.apache.solr.util.SafeXMLParsing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
Expand All @@ -51,7 +51,6 @@
* Abstract Field type for support of string values with custom sort order.
*/
public abstract class AbstractEnumField extends PrimitiveFieldType {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected EnumMapping enumMapping;

@Override
Expand All @@ -69,6 +68,8 @@ public EnumMapping getEnumMapping() {
* @lucene.internal
*/
public static final class EnumMapping {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

public static final String PARAM_ENUMS_CONFIG = "enumsConfig";
public static final String PARAM_ENUM_NAME = "enumName";
public static final Integer DEFAULT_VALUE = -1;
Expand Down Expand Up @@ -105,67 +106,49 @@ public EnumMapping(IndexSchema schema, FieldType fieldType, Map<String, String>
ftName + ": No enum name was configured.");
}

InputStream is = null;

final SolrResourceLoader loader = schema.getResourceLoader();
try {
is = schema.getResourceLoader().openResource(enumsConfigFile);
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
final Document doc = dbf.newDocumentBuilder().parse(is);
final XPathFactory xpathFactory = XPathFactory.newInstance();
final XPath xpath = xpathFactory.newXPath();
final String xpathStr = String.format(Locale.ROOT, "/enumsConfig/enum[@name='%s']", enumName);
final NodeList nodes = (NodeList) xpath.evaluate(xpathStr, doc, XPathConstants.NODESET);
final int nodesLength = nodes.getLength();
if (nodesLength == 0) {
String exceptionMessage = String.format
(Locale.ENGLISH, "%s: No enum configuration found for enum '%s' in %s.",
log.debug("Reloading enums config file from {}", enumsConfigFile);
Document doc = SafeXMLParsing.parseConfigXML(log, loader, enumsConfigFile);
final XPathFactory xpathFactory = XPathFactory.newInstance();
final XPath xpath = xpathFactory.newXPath();
final String xpathStr = String.format(Locale.ROOT, "/enumsConfig/enum[@name='%s']", enumName);
final NodeList nodes = (NodeList) xpath.evaluate(xpathStr, doc, XPathConstants.NODESET);
final int nodesLength = nodes.getLength();
if (nodesLength == 0) {
String exceptionMessage = String.format
(Locale.ENGLISH, "%s: No enum configuration found for enum '%s' in %s.",
ftName, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, exceptionMessage);
}
if (nodesLength > 1) {
if (log.isWarnEnabled())
log.warn("{}: More than one enum configuration found for enum '{}' in {}. The last one was taken.",
ftName, enumName, enumsConfigFile);
}
final Node enumNode = nodes.item(nodesLength - 1);
final NodeList valueNodes = (NodeList) xpath.evaluate("value", enumNode, XPathConstants.NODESET);
for (int i = 0; i < valueNodes.getLength(); i++) {
final Node valueNode = valueNodes.item(i);
final String valueStr = valueNode.getTextContent();
if ((valueStr == null) || (valueStr.length() == 0)) {
final String exceptionMessage = String.format
(Locale.ENGLISH, "%s: A value was defined with an no value in enum '%s' in %s.",
ftName, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, exceptionMessage);
}
if (nodesLength > 1) {
if (log.isWarnEnabled())
log.warn("{}: More than one enum configuration found for enum '{}' in {}. The last one was taken.",
ftName, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, exceptionMessage);
}
final Node enumNode = nodes.item(nodesLength - 1);
final NodeList valueNodes = (NodeList) xpath.evaluate("value", enumNode, XPathConstants.NODESET);
for (int i = 0; i < valueNodes.getLength(); i++) {
final Node valueNode = valueNodes.item(i);
final String valueStr = valueNode.getTextContent();
if ((valueStr == null) || (valueStr.length() == 0)) {
final String exceptionMessage = String.format
(Locale.ENGLISH, "%s: A value was defined with an no value in enum '%s' in %s.",
ftName, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, exceptionMessage);
}
if (enumStringToIntMap.containsKey(valueStr)) {
final String exceptionMessage = String.format
(Locale.ENGLISH, "%s: A duplicated definition was found for value '%s' in enum '%s' in %s.",
ftName, valueStr, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, exceptionMessage);
}
enumIntToStringMap.put(i, valueStr);
enumStringToIntMap.put(valueStr, i);
if (enumStringToIntMap.containsKey(valueStr)) {
final String exceptionMessage = String.format
(Locale.ENGLISH, "%s: A duplicated definition was found for value '%s' in enum '%s' in %s.",
ftName, valueStr, enumName, enumsConfigFile);
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, exceptionMessage);
}
enumIntToStringMap.put(i, valueStr);
enumStringToIntMap.put(valueStr, i);
}
catch (ParserConfigurationException | XPathExpressionException | SAXException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
ftName + ": Error parsing enums config.", e);
}
}
catch (IOException e) {
} catch (IOException | SAXException | XPathExpressionException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
ftName + ": Error while opening enums config.", e);
} finally {
try {
if (is != null) {
is.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
ftName + ": Error while parsing enums config.", e);
}

if ((enumStringToIntMap.size() == 0) || (enumIntToStringMap.size() == 0)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,21 @@

package org.apache.solr.schema;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.solr.common.SolrException;
import org.apache.solr.util.SafeXMLParsing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
Expand All @@ -46,6 +45,7 @@
*/
class FileExchangeRateProvider implements ExchangeRateProvider {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

protected static final String PARAM_CURRENCY_CONFIG = "currencyConfig";

// Exchange rate map, maps Currency Code -> Currency Code -> Rate
Expand Down Expand Up @@ -159,71 +159,49 @@ public Set<String> listAvailableCurrencies() {

@Override
public boolean reload() throws SolrException {
InputStream is = null;
Map<String, Map<String, Double>> tmpRates = new HashMap<>();
log.debug("Reloading exchange rates from file {}", currencyConfigFile);

try {
log.debug("Reloading exchange rates from file "+this.currencyConfigFile);

is = loader.openResource(currencyConfigFile);
javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
dbf.setXIncludeAware(true);
dbf.setNamespaceAware(true);
} catch (UnsupportedOperationException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "XML parser doesn't support XInclude option", e);
}
Document doc = SafeXMLParsing.parseConfigXML(log, loader, currencyConfigFile);
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();

// Parse exchange rates.
NodeList nodes = (NodeList) xpath.evaluate("/currencyConfig/rates/rate", doc, XPathConstants.NODESET);

try {
Document doc = dbf.newDocumentBuilder().parse(is);
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
for (int i = 0; i < nodes.getLength(); i++) {
Node rateNode = nodes.item(i);
NamedNodeMap attributes = rateNode.getAttributes();
Node from = attributes.getNamedItem("from");
Node to = attributes.getNamedItem("to");
Node rate = attributes.getNamedItem("rate");

if (from == null || to == null || rate == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Exchange rate missing attributes (required: from, to, rate) " + rateNode);
}

// Parse exchange rates.
NodeList nodes = (NodeList) xpath.evaluate("/currencyConfig/rates/rate", doc, XPathConstants.NODESET);
String fromCurrency = from.getNodeValue();
String toCurrency = to.getNodeValue();
Double exchangeRate;

for (int i = 0; i < nodes.getLength(); i++) {
Node rateNode = nodes.item(i);
NamedNodeMap attributes = rateNode.getAttributes();
Node from = attributes.getNamedItem("from");
Node to = attributes.getNamedItem("to");
Node rate = attributes.getNamedItem("rate");

if (from == null || to == null || rate == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Exchange rate missing attributes (required: from, to, rate) " + rateNode);
}

String fromCurrency = from.getNodeValue();
String toCurrency = to.getNodeValue();
Double exchangeRate;

if (null == CurrencyFieldType.getCurrency(fromCurrency)) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Specified 'from' currency not supported in this JVM: " + fromCurrency);
}
if (null == CurrencyFieldType.getCurrency(toCurrency)) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Specified 'to' currency not supported in this JVM: " + toCurrency);
}

try {
exchangeRate = Double.parseDouble(rate.getNodeValue());
} catch (NumberFormatException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Could not parse exchange rate: " + rateNode, e);
}

addRate(tmpRates, fromCurrency, toCurrency, exchangeRate);
if (null == CurrencyFieldType.getCurrency(fromCurrency)) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Specified 'from' currency not supported in this JVM: " + fromCurrency);
}
} catch (SAXException | XPathExpressionException | ParserConfigurationException | IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error parsing currency config.", e);
}
} catch (IOException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error while opening Currency configuration file "+currencyConfigFile, e);
} finally {
try {
if (is != null) {
is.close();
if (null == CurrencyFieldType.getCurrency(toCurrency)) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Specified 'to' currency not supported in this JVM: " + toCurrency);
}

try {
exchangeRate = Double.parseDouble(rate.getNodeValue());
} catch (NumberFormatException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Could not parse exchange rate: " + rateNode, e);
}
} catch (IOException e) {
e.printStackTrace();
addRate(tmpRates, fromCurrency, toCurrency, exchangeRate);
}
} catch (SAXException | IOException | XPathExpressionException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error while parsing currency configuration file "+currencyConfigFile, e);
}
// Atomically swap in the new rates map, if it loaded successfully
this.rates = tmpRates;
Expand Down
Loading

0 comments on commit e5407c5

Please sign in to comment.