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(

Reply via email to