diff --git a/jspwiki-main/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java b/jspwiki-main/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java index e7dfdb5a80..2d609e0633 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java @@ -238,7 +238,24 @@ public Pattern getPluginPattern() { * @throws ClassNotFoundException if no such class exists. */ private Class< ? > findPluginClass( final String classname ) throws ClassNotFoundException { - return ClassUtil.findClass( m_searchPath, m_externalJars, classname ); + final Class< ? > clazz = ClassUtil.findClass( m_searchPath, m_externalJars, classname ); + if( !Plugin.class.isAssignableFrom( clazz ) || !isInSearchPath( clazz ) ) { + // Reject with the same error as an unknown class, so that page markup cannot be used to + // load or instantiate arbitrary classpath classes, nor to probe for their presence. + throw new ClassNotFoundException( "Class '" + classname + "' not found in search path!" ); + } + return clazz; + } + + /** + * Checks whether the package of the given class is one of the packages on the plugin search path. + * + * @param clazz The class to check. + * @return true, if the class' package is on the plugin search path. + */ + private boolean isInSearchPath( final Class< ? > clazz ) { + final Package pkg = clazz.getPackage(); + return pkg != null && m_searchPath.contains( pkg.getName() ); } /** Outputs an HTML-formatted version of a stack trace. */ diff --git a/jspwiki-main/src/test/java/org/apache/wiki/plugin/DefaultPluginManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/plugin/DefaultPluginManagerTest.java index fd6879048d..86b78b55ec 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/plugin/DefaultPluginManagerTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/plugin/DefaultPluginManagerTest.java @@ -22,6 +22,7 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.wiki.TestEngine; import org.apache.wiki.api.core.Context; import org.apache.wiki.api.core.Engine; +import org.apache.wiki.api.exceptions.PluginException; import org.apache.wiki.api.exceptions.ProviderException; import org.apache.wiki.api.spi.Wiki; import org.apache.wiki.pages.PageManager; @@ -153,4 +154,16 @@ public void testParserPlugin() throws Exception { Assertions.assertTrue( SamplePlugin.c_rendered ); } + /** Page markup must not load or instantiate arbitrary classpath classes; only plugins on the search path. */ + @Test + public void testInsertArbitraryClass() { + final PluginException timer = Assertions.assertThrows( PluginException.class, + () -> manager.execute( context, "{INSERT java.util.Timer}" ) ); + final PluginException missing = Assertions.assertThrows( PluginException.class, + () -> manager.execute( context, "{INSERT java.util.NoSuchClass}" ) ); + // Rejected with the same "could not find" error as a nonexistent class, so markup + // cannot be used as a classpath-probing oracle either. + Assertions.assertEquals( missing.getMessage().replace( "NoSuchClass", "Timer" ), timer.getMessage() ); + } + }