This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opennlp.git
The following commit(s) were added to refs/heads/main by this push:
new 9796b1a09 Allowlist based class Instantiation (#1178)
9796b1a09 is described below
commit 9796b1a094b314ce1a76a10ea3c04bbbf3b68243
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
---
.../tools/util/featuregen/GeneratorFactory.java | 45 ++++++++-------------
.../util/featuregen/GeneratorFactoryTest.java | 46 +++++++++++++++++++++-
2 files changed, 61 insertions(+), 30 deletions(-)
diff --git
a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java
b/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java
index f5795cca4..59c81ba75 100644
---
a/opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/util/featuregen/GeneratorFactory.java
+++
b/opennlp-core/opennlp-runtime/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-core/opennlp-runtime/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java
b/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java
index 8ccac7d0b..c2866c406 100644
---
a/opennlp-core/opennlp-runtime/src/test/java/opennlp/tools/util/featuregen/GeneratorFactoryTest.java
+++
b/opennlp-core/opennlp-runtime/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(