Skip to content
Permalink
Browse files Browse the repository at this point in the history
XWIKI-5168: Don't allow some methods in velocity introspector (#127)
  • Loading branch information
surli committed Jan 6, 2021
1 parent 75aadf3 commit 215951c
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 29 deletions.
Expand Up @@ -19,7 +19,11 @@
*/
package org.xwiki.velocity.introspection;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.velocity.util.introspection.SecureIntrospectorImpl;
Expand All @@ -33,7 +37,8 @@
*/
public class SecureIntrospector extends SecureIntrospectorImpl
{
private final Set<String> secureClassMethods = new HashSet<>();
private static final String GETNAME = "getname";
private final Map<Class, Set<String>> whitelistedMethods;

/**
* @param badClasses forbidden classes
Expand All @@ -44,41 +49,82 @@ public SecureIntrospector(String[] badClasses, String[] badPackages, Logger log)
{
super(badClasses, badPackages, log);

this.secureClassMethods.add("getname");
this.secureClassMethods.add("getName");
this.secureClassMethods.add("getsimpleName");
this.secureClassMethods.add("getSimpleName");
this.whitelistedMethods = new HashMap<>();
this.prepareWhitelistClass();
this.prepareWhiteListFile();
}

this.secureClassMethods.add("isarray");
this.secureClassMethods.add("isArray");
this.secureClassMethods.add("isassignablefrom");
this.secureClassMethods.add("isAssignableFrom");
this.secureClassMethods.add("isenum");
this.secureClassMethods.add("isEnum");
this.secureClassMethods.add("isinstance");
this.secureClassMethods.add("isInstance");
this.secureClassMethods.add("isinterface");
this.secureClassMethods.add("isInterface");
this.secureClassMethods.add("islocalClass");
this.secureClassMethods.add("isLocalClass");
this.secureClassMethods.add("ismemberclass");
this.secureClassMethods.add("isMemberClass");
this.secureClassMethods.add("isprimitive");
this.secureClassMethods.add("isPrimitive");
this.secureClassMethods.add("issynthetic");
this.secureClassMethods.add("isSynthetic");
this.secureClassMethods.add("getEnumConstants");
private void prepareWhitelistClass()
{
Set<String> whitelist = new HashSet<>(Arrays.asList(
GETNAME,
"getsimpleName",
"isarray",
"isassignablefrom",
"isenum",
"isinstance",
"isinterface",
"islocalclass",
"ismemberclass",
"isprimitive",
"issynthetic",
"getenumconstants"
));
this.whitelistedMethods.put(Class.class, whitelist);
}

// TODO: add more when needed
private void prepareWhiteListFile()
{
Set<String> whitelist = new HashSet<>(Arrays.asList(
"canexecute",
"canread",
"canwrite",
"compareto",
"createtempfile",
"equals",
"getabsolutefile",
"getabsolutepath",
"getcanonicalfile",
"getcanonicalpath",
"getfreespace",
GETNAME,
"getparent",
"getparentfile",
"getpath",
"gettotalspace",
"getusablespace",
"hashcode",
"isabsolute",
"isdirectory",
"isfile",
"ishidden",
"lastmodified",
"length",
"topath",
"tostring",
"touri",
"tourl",
"getclass"
));
this.whitelistedMethods.put(File.class, whitelist);
}

@Override
public boolean checkObjectExecutePermission(Class clazz, String methodName)
{
if (Class.class.isAssignableFrom(clazz) && methodName != null && this.secureClassMethods.contains(methodName)) {
return true;
} else {
return super.checkObjectExecutePermission(clazz, methodName);
Boolean result = null;
if (methodName != null) {
for (Map.Entry<Class, Set<String>> classSetEntry : this.whitelistedMethods.entrySet()) {
if (classSetEntry.getKey().isAssignableFrom(clazz)) {
result = classSetEntry.getValue().contains(methodName.toLowerCase());
break;
}
}
}

if (result == null) {
result = super.checkObjectExecutePermission(clazz, methodName);
}
return result;
}
}
@@ -0,0 +1,80 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.velocity.introspection;

import java.io.File;
import java.util.ArrayList;

import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.slf4j.Logger;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Tests for {@link SecureIntrospector}.
*
* @version $Id$
*/
class SecureIntrospectorTest
{
@Mock
private Logger logger;

class CustomFile extends File
{
public CustomFile(String s)
{
super(s);
}
}

@Test
void checkObjectExecutePermissionWithClass()
{
SecureIntrospector secureIntrospector = new SecureIntrospector(new String[] {}, new String[] {}, this.logger);
assertTrue(secureIntrospector.checkObjectExecutePermission(Class.class, "isLocalClass"));
}

@Test
void checkObjectExecutePermissionWithFile()
{
SecureIntrospector secureIntrospector = new SecureIntrospector(new String[] {}, new String[] {}, this.logger);
assertTrue(secureIntrospector.checkObjectExecutePermission(File.class, "toString"));
assertFalse(secureIntrospector.checkObjectExecutePermission(File.class, "mkdir"));

assertTrue(secureIntrospector.checkObjectExecutePermission(File.class, "tostring"));
assertFalse(secureIntrospector.checkObjectExecutePermission(File.class, "renameto"));
assertFalse(secureIntrospector.checkObjectExecutePermission(File.class, "renameTo"));

assertTrue(secureIntrospector.checkObjectExecutePermission(CustomFile.class, "toString"));
assertFalse(secureIntrospector.checkObjectExecutePermission(CustomFile.class, "mkdir"));
}

@Test
void checkObjectExecutePermissionBlacklistedClass()
{
SecureIntrospector secureIntrospector = new SecureIntrospector(
new String[] { "java.util.ArrayList" }, new String[] {}, this.logger);
assertTrue(secureIntrospector.checkObjectExecutePermission(File.class, "toString"));
assertFalse(secureIntrospector.checkObjectExecutePermission(ArrayList.class, "toString"));
}
}

0 comments on commit 215951c

Please sign in to comment.