Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/java/org/apache/commons/jxpath/ClassFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import org.apache.commons.jxpath.functions.ConstructorFunction;
import org.apache.commons.jxpath.functions.MethodFunction;
import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;
import org.apache.commons.jxpath.util.MethodLookupUtils;

/**
Expand Down Expand Up @@ -91,6 +93,21 @@ public Function getFunction(
final String namespace,
final String name,
Object[] parameters) {
return getFunction(namespace, name, parameters, new SystemPropertyJXPathFilter());
}

@Override
public Function getFunction(
final String namespace,
final String name,
Object[] parameters,
JXPathFilter jxPathFilter) {
if (!jxPathFilter.isClassNameExposed(functionClass.getName())) {
throw new JXPathException(
"Extension function is not allowed: " + (namespace != null ? namespace + ":" + name : name)
+ " (in " + functionClass.getName() + ")");
}

if (namespace == null) {
if (this.namespace != null) {
return null;
Expand Down
18 changes: 14 additions & 4 deletions src/main/java/org/apache/commons/jxpath/FunctionLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
*/
package org.apache.commons.jxpath;

import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -74,14 +77,20 @@ public Set getUsedNamespaces() {
* @return Function found
*/
@Override
public Function getFunction(final String namespace, final String name,
final Object[] parameters) {
public Function getFunction(final String namespace, final String name, final Object[] parameters) {
return getFunction(namespace, name, parameters, new SystemPropertyJXPathFilter());
}


@Override
public Function getFunction(final String namespace, final String name, final Object[] parameters, final JXPathFilter filter) {
final Object candidates = functionCache().get(namespace);
if (candidates instanceof Functions) {
return ((Functions) candidates).getFunction(
namespace,
name,
parameters);
parameters,
filter);
}
if (candidates instanceof List) {
final List list = (List) candidates;
Expand All @@ -91,7 +100,8 @@ public Function getFunction(final String namespace, final String name,
((Functions) list.get(i)).getFunction(
namespace,
name,
parameters);
parameters,
filter);
if (function != null) {
return function;
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/apache/commons/jxpath/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.apache.commons.jxpath;

import org.apache.commons.jxpath.ri.JXPathFilter;

import java.util.Set;

/**
Expand Down Expand Up @@ -43,4 +45,17 @@ public interface Functions {
* @return Function
*/
Function getFunction(String namespace, String name, Object[] parameters);

/**
* Returns a Function, if any, for the specified namespace,
* name and parameter types if the function is allowed by the filter.
* @param namespace ns
* @param name function name
* @param parameters Object[]
* @param filter JXPathFilter
* @return Function
*/
default Function getFunction(String namespace, String name, Object[] parameters, JXPathFilter filter) {
return getFunction(namespace, name, parameters);
}
}
21 changes: 21 additions & 0 deletions src/main/java/org/apache/commons/jxpath/JXPathContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.List;
import java.util.Locale;

import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;
import org.apache.commons.jxpath.util.KeyManagerUtils;

/**
Expand Down Expand Up @@ -422,6 +424,8 @@ public abstract class JXPathContext {
/** decimal format map */
protected HashMap decimalFormats;

protected JXPathFilter filter;

private Locale locale;
private boolean lenientSet = false;
private boolean lenient = false;
Expand Down Expand Up @@ -469,6 +473,7 @@ private static JXPathContextFactory getContextFactory () {
protected JXPathContext(final JXPathContext parentContext, final Object contextBean) {
this.parentContext = parentContext;
this.contextBean = contextBean;
this.filter = new SystemPropertyJXPathFilter();
}

/**
Expand Down Expand Up @@ -598,6 +603,22 @@ public synchronized Locale getLocale() {
return locale;
}

/**
* Returns the filter that specifies which classes are allowed.
* @return JXPathFilter
*/
public JXPathFilter getFilter() {
return filter;
}

/**
* Sets the filter for the context.
* @param filter JXPathFilter
*/
public void setFilter(JXPathFilter filter) {
this.filter = filter;
}

/**
* Sets {@link DecimalFormatSymbols} for a given name. The DecimalFormatSymbols
* can be referenced as the third, optional argument in the invocation of
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/org/apache/commons/jxpath/PackageFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

import org.apache.commons.jxpath.functions.ConstructorFunction;
import org.apache.commons.jxpath.functions.MethodFunction;
import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;
import org.apache.commons.jxpath.util.ClassLoaderUtil;
import org.apache.commons.jxpath.util.MethodLookupUtils;
import org.apache.commons.jxpath.util.TypeUtils;
Expand Down Expand Up @@ -116,6 +118,15 @@ public Function getFunction(
final String namespace,
final String name,
Object[] parameters) {
return getFunction(namespace, name, parameters, new SystemPropertyJXPathFilter());
}

@Override
public Function getFunction(final String namespace, final String name, Object[] parameters, final JXPathFilter filter) {
if (filter == null) {
throw new JXPathException("No extension function is allowed");
}

if (!Objects.equals(this.namespace, namespace)) {
return null;
}
Expand All @@ -124,6 +135,7 @@ public Function getFunction(
parameters = EMPTY_ARRAY;
}

String functionName = namespace != null ? namespace + ":" + name : name;
if (parameters.length >= 1) {
Object target = TypeUtils.convert(parameters[0], Object.class);
if (target != null) {
Expand All @@ -133,7 +145,7 @@ public Function getFunction(
name,
parameters);
if (method != null) {
return new MethodFunction(method);
return new MethodFunction(method, filter);
}

if (target instanceof NodeSet) {
Expand All @@ -146,7 +158,7 @@ public Function getFunction(
name,
parameters);
if (method != null) {
return new MethodFunction(method);
return new MethodFunction(method, filter);
}

if (target instanceof Collection) {
Expand All @@ -169,7 +181,7 @@ public Function getFunction(
name,
parameters);
if (method != null) {
return new MethodFunction(method);
return new MethodFunction(method, filter);
}
}
}
Expand All @@ -185,12 +197,11 @@ public Function getFunction(

Class functionClass;
try {
functionClass = ClassLoaderUtil.getClass(className, true);
functionClass = ClassLoaderUtil.getClass(className, true, filter);
}
catch (final ClassNotFoundException ex) {
throw new JXPathException(
"Cannot invoke extension function "
+ (namespace != null ? namespace + ":" + name : name),
"Cannot invoke extension function " + functionName,
ex);
}

Expand All @@ -208,7 +219,7 @@ public Function getFunction(
methodName,
parameters);
if (method != null) {
return new MethodFunction(method);
return new MethodFunction(method, filter);
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.apache.commons.jxpath.ExpressionContext;
import org.apache.commons.jxpath.Function;
import org.apache.commons.jxpath.JXPathInvalidAccessException;
import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;
import org.apache.commons.jxpath.util.TypeUtils;

/**
Expand All @@ -32,12 +34,19 @@ public class ConstructorFunction implements Function {

private final Constructor constructor;

private final JXPathFilter filter;

/**
* Create a new ConstructorFunction.
* @param constructor the constructor to call.
*/
public ConstructorFunction(final Constructor constructor) {
this(constructor, new SystemPropertyJXPathFilter());
}

public ConstructorFunction(final Constructor constructor, JXPathFilter filter) {
this.constructor = constructor;
this.filter = filter;
}

/**
Expand Down Expand Up @@ -66,6 +75,11 @@ public Object invoke(final ExpressionContext context, Object[] parameters) {
for (int i = 0; i < parameters.length; i++) {
args[i + pi] = TypeUtils.convert(parameters[i], types[i + pi]);
}

if (!filter.isClassNameExposed(constructor.getDeclaringClass().getName())) {
throw new Exception("Calling constructors is not allowed for class: " + constructor.getDeclaringClass().getName());
}

return constructor.newInstance(args);
}
catch (Throwable ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.apache.commons.jxpath.ExpressionContext;
import org.apache.commons.jxpath.Function;
import org.apache.commons.jxpath.JXPathInvalidAccessException;
import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.SystemPropertyJXPathFilter;
import org.apache.commons.jxpath.util.TypeUtils;
import org.apache.commons.jxpath.util.ValueUtils;

Expand All @@ -34,12 +36,24 @@ public class MethodFunction implements Function {
private final Method method;
private static final Object[] EMPTY_ARRAY = {};

private final JXPathFilter filter;

/**
* Create a new MethodFunction.
* @param method implementing Method
*/
public MethodFunction(final Method method) {
this(method, new SystemPropertyJXPathFilter());
}

/**
* Create a new MethodFunction.
* @param method implementing Method
* @param filter JXPathFilter
*/
public MethodFunction(final Method method, final JXPathFilter filter) {
this.method = ValueUtils.getAccessibleMethod(method);
this.filter = filter;
}

@Override
Expand Down Expand Up @@ -88,7 +102,11 @@ public Object invoke(final ExpressionContext context, Object[] parameters) {
}
}

if (!filter.isClassNameExposed(method.getDeclaringClass().getName())) {
throw new Exception("Calling method is not allowed: " + method.getDeclaringClass() + "." + method.getName() + "()");
}
return method.invoke(target, args);

}
catch (Throwable ex) {
if (ex instanceof InvocationTargetException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.jxpath.ri;

import java.util.Objects;
import java.util.Set;

public class AbstractJXPathFilter implements JXPathFilter {
protected final Set<String> allowedClasses;

public AbstractJXPathFilter(final Set<String> allowedClasses) {
this.allowedClasses = allowedClasses;
}

/**
* Specifies whether the Java class of the specified name be exposed via xpath
*
* @param className is the fully qualified name of the java class being checked.
* This will not be null. Only non-array class names will be passed.
* @return true if the java class can be exposed via xpath, false otherwise
*/
@Override
public boolean isClassNameExposed(String className) {
if (allowedClasses.isEmpty()) {
return false;
}

if (allowedClasses.contains("*")) {
return true;
}

return allowedClasses.stream().anyMatch(pattern -> {
if (Objects.equals(className, pattern)) {
return true;
}
else if (pattern.endsWith("*")) {
return className.startsWith(pattern.substring(0, pattern.length() - 1));
}
return false;
});
}
}
Loading