This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch OPENNLP-1890 in repository https://gitbox.apache.org/repos/asf/opennlp.git
commit 8aa9061550a39af5ea71668edce62dcc7f626c3d Author: subbudvk <[email protected]> AuthorDate: Thu Jul 16 18:55:33 2026 +0530 Allowlist based class Instantiation (#1178) * Using allowlist based class loading * Using allowlist based class loading (cherry picked from commit 9796b1a094b314ce1a76a10ea3c04bbbf3b68243) --- .../tools/util/featuregen/GeneratorFactory.java | 45 ++++++++------------- .../util/featuregen/GeneratorFactoryTest.java | 46 +++++++++++++++++++++- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/opennlp-tools/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java b/opennlp-tools/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java index f5795cca4..59c81ba75 100644 --- a/opennlp-tools/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java +++ b/opennlp-tools/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java @@ -19,8 +19,6 @@ package opennlp.tools.util.featuregen; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -41,6 +39,7 @@ import org.xml.sax.SAXException; import opennlp.tools.util.InvalidFormatException; import opennlp.tools.util.XmlUtil; +import opennlp.tools.util.ext.ExtensionLoader; import opennlp.tools.util.model.ArtifactSerializer; /** @@ -137,18 +136,13 @@ public class GeneratorFactory { throw new InvalidFormatException("generator must have class attribute"); } else { try { - final Class<?> factoryClass = Class.forName(className); - try { - final Constructor<?> constructor = factoryClass.getConstructor(); - final AbstractXmlFeatureGeneratorFactory factory = - (AbstractXmlFeatureGeneratorFactory) constructor.newInstance(); - factory.init(generatorElement, resourceManager); - return factory.create(); - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | - InvalidFormatException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } catch (ClassNotFoundException e) { + final AbstractXmlFeatureGeneratorFactory factory = + ExtensionLoader.instantiateExtension(AbstractXmlFeatureGeneratorFactory.class, className); + factory.init(generatorElement, resourceManager); + return factory.create(); + } catch (InvalidFormatException e) { + throw e; + } catch (Exception e) { throw new RuntimeException(e); } } @@ -257,22 +251,15 @@ public class GeneratorFactory { final String className = element.getAttribute("class"); if (!className.isBlank()) { try { - final Class<?> factoryClass = Class.forName(className); - try { - final Constructor<?> constructor = factoryClass.getConstructor(); - final AbstractXmlFeatureGeneratorFactory factory = - (AbstractXmlFeatureGeneratorFactory) constructor.newInstance(); - factory.init(element, null); - final Map<String, ArtifactSerializer<?>> map = factory.getArtifactSerializerMapping(); - if (map != null) { - mapping.putAll(map); - } - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException - | IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvalidFormatException ignored) { + final AbstractXmlFeatureGeneratorFactory factory = + ExtensionLoader.instantiateExtension(AbstractXmlFeatureGeneratorFactory.class, className); + factory.init(element, null); + final Map<String, ArtifactSerializer<?>> map = factory.getArtifactSerializerMapping(); + if (map != null) { + mapping.putAll(map); } - } catch (ClassNotFoundException e) { + } catch (InvalidFormatException ignored) { + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/opennlp-tools/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java b/opennlp-tools/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java index 8ccac7d0b..c2866c406 100644 --- a/opennlp-tools/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java +++ b/opennlp-tools/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java @@ -17,8 +17,10 @@ package opennlp.tools.util.featuregen; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -28,12 +30,13 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import opennlp.tools.util.InvalidFormatException; +import opennlp.tools.util.ext.ExtensionNotLoadedException; import opennlp.tools.util.model.ArtifactSerializer; import opennlp.tools.util.model.DictionarySerializer; public class GeneratorFactoryTest { - static class TestParametersFeatureGeneratorFactory extends + public static class TestParametersFeatureGeneratorFactory extends GeneratorFactory.AbstractXmlFeatureGeneratorFactory { public TestParametersFeatureGeneratorFactory() { @@ -240,6 +243,47 @@ public class GeneratorFactoryTest { } } + /** + * Positive: class attribute referencing an opennlp.* factory loads without error. + * opennlp.* is in the default allowlist and the type matches AbstractXmlFeatureGeneratorFactory. + */ + @Test + void testClassAttributeAllowedOpennlpPackage() throws Exception { + try (InputStream descIn = getClass().getResourceAsStream( + "/opennlp/tools/util/featuregen/TestParametersConfig.xml")) { + Assertions.assertNotNull(descIn); + Assertions.assertDoesNotThrow(() -> GeneratorFactory.create(descIn, null)); + } + } + + /** + * Negative: class attribute referencing a non-opennlp.* class is rejected by + * the ExtensionLoader allowlist before Class.forName() is called. + */ + @Test + void testClassAttributeBlockedPackageThrows() { + String xml = "<?xml version=\"1.0\"?><featureGenerators>" + + "<generator class=\"com.malicious.Exploit\"/></featureGenerators>"; + InputStream descIn = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); + RuntimeException ex = Assertions.assertThrows(RuntimeException.class, + () -> GeneratorFactory.create(descIn, null)); + Assertions.assertInstanceOf(ExtensionNotLoadedException.class, ex.getCause()); + } + + /** + * Negative: class attribute referencing an opennlp.* class that does NOT extend + * AbstractXmlFeatureGeneratorFactory is rejected by the isAssignableFrom type check. + */ + @Test + void testClassAttributeWrongTypeThrows() { + String xml = "<?xml version=\"1.0\"?><featureGenerators>" + + "<generator class=\"opennlp.tools.util.featuregen.TokenFeatureGenerator\"/></featureGenerators>"; + InputStream descIn = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); + RuntimeException ex = Assertions.assertThrows(RuntimeException.class, + () -> GeneratorFactory.create(descIn, null)); + Assertions.assertInstanceOf(ExtensionNotLoadedException.class, ex.getCause()); + } + @Test void testInsertCachedFeatureGenerator() throws Exception { InputStream generatorDescriptorIn = getClass().getResourceAsStream(
