Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package opennlp for openSUSE:Factory checked in at 2026-07-24 22:07:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/opennlp (Old) and /work/SRC/openSUSE:Factory/.opennlp.new.2004 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "opennlp" Fri Jul 24 22:07:06 2026 rev:6 rq:1367685 version:1.9.5 Changes: -------- --- /work/SRC/openSUSE:Factory/opennlp/opennlp.changes 2026-07-15 22:15:20.166709218 +0200 +++ /work/SRC/openSUSE:Factory/.opennlp.new.2004/opennlp.changes 2026-07-24 22:07:58.811235070 +0200 @@ -1,0 +2,9 @@ +Fri Jul 24 14:34:26 UTC 2026 - Fridrich Strba <[email protected]> + +- Added patch: + * opennlp-CVE-2026-63317.patch + + Backport of 2.x fixes for bsc#1272520, CVE-2026-63317: + Arbitrary Class Instantiation via XML Feature Generator + Descriptor and Format Name in Apache OpenNLP + +------------------------------------------------------------------- New: ---- opennlp-CVE-2026-63317.patch ----------(New B)---------- New:- Added patch: * opennlp-CVE-2026-63317.patch + Backport of 2.x fixes for bsc#1272520, CVE-2026-63317: ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ opennlp.spec ++++++ --- /var/tmp/diff_new_pack.dzILGK/_old 2026-07-24 22:08:00.279286325 +0200 +++ /var/tmp/diff_new_pack.dzILGK/_new 2026-07-24 22:08:00.279286325 +0200 @@ -23,6 +23,7 @@ License: Apache-2.0 URL: https://opennlp.apache.org/ Source0: https://archive.apache.org/dist/opennlp/%{name}-%{version}/apache-%{name}-%{version}-src.tar.gz +Patch0: opennlp-CVE-2026-63317.patch BuildRequires: fdupes BuildRequires: java-devel >= 1.8 BuildRequires: maven-local @@ -77,6 +78,8 @@ find . -name '*.jar' -print -delete find . -name '*.bat' -print -delete find . -name '*.class' -print -delete +# Patches +%patch -P 0 -p1 # use latest OSGi implementation %pom_change_dep -r :org.osgi.core org.osgi:osgi.core ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.dzILGK/_old 2026-07-24 22:08:00.335288280 +0200 +++ /var/tmp/diff_new_pack.dzILGK/_new 2026-07-24 22:08:00.339288419 +0200 @@ -1,6 +1,6 @@ -mtime: 1784101949 -commit: b9bc41e9993394a12f99e9eab9d003bb45d43b1818f558c915e1c674d2f0e2aa +mtime: 1784904848 +commit: 4f250b3ecad70439da69846bd12f01f4788cac2c612da6ccb562dc74d7c5e8b3 url: https://src.opensuse.org/java-packages/opennlp -revision: b9bc41e9993394a12f99e9eab9d003bb45d43b1818f558c915e1c674d2f0e2aa +revision: 4f250b3ecad70439da69846bd12f01f4788cac2c612da6ccb562dc74d7c5e8b3 projectscmsync: https://src.opensuse.org/java-packages/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-07-24 16:54:08.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ opennlp-CVE-2026-63317.patch ++++++ diff --git a/opennlp-tools/src/main/java/opennlp/tools/cmdline/StreamFactoryRegistry.java b/opennlp-tools/src/main/java/opennlp/tools/cmdline/StreamFactoryRegistry.java index db95a4f4..eb4c15f3 100644 --- a/opennlp-tools/src/main/java/opennlp/tools/cmdline/StreamFactoryRegistry.java +++ b/opennlp-tools/src/main/java/opennlp/tools/cmdline/StreamFactoryRegistry.java @@ -65,6 +65,8 @@ import opennlp.tools.formats.nkjp.NKJPSentenceSampleStreamFactory; import opennlp.tools.formats.ontonotes.OntoNotesNameSampleStreamFactory; import opennlp.tools.formats.ontonotes.OntoNotesPOSSampleStreamFactory; import opennlp.tools.formats.ontonotes.OntoNotesParseSampleStreamFactory; +import opennlp.tools.util.ext.ExtensionLoader; +import opennlp.tools.util.ext.ExtensionNotLoadedException; /** * Registry for object stream factories. @@ -193,12 +195,18 @@ public final class StreamFactoryRegistry { } /** - * Returns a factory which reads format named <code>formatName</code> and - * instantiates streams producing objects of <code>sampleClass</code> class. + * Returns a factory which reads format named {@code formatName} and + * instantiates streams producing objects of {@code sampleClass} class. + * <p> + * If no factory is registered for {@code formatName}, an attempt is made to + * interpret {@code formatName} as the fully qualified class name of an + * {@link ObjectStreamFactory} implementation and load it via the + * {@link ExtensionLoader}. Such a class must reside in an allowed package, + * see {@link ExtensionLoader#registerAllowedPackage(String)}. * * @param sampleClass class of the objects, produced by the streams instantiated by the factory * @param formatName name of the format, if null, assumes OpenNLP format - * @return factory instance + * @return factory instance, or {@code null} if no factory could be found or loaded */ @SuppressWarnings("unchecked") public static <T> ObjectStreamFactory<T> getFactory(Class<T> sampleClass, @@ -215,18 +223,9 @@ public final class StreamFactoryRegistry { } else { try { - Class<?> factoryClazz = Class.forName(formatName); - - // TODO: Need to check if it can produce the desired output - // Otherwise there will be class cast exceptions later in the flow - - try { - return (ObjectStreamFactory<T>) factoryClazz.newInstance(); - } catch (InstantiationException | IllegalAccessException e) { - return null; - } - - } catch (ClassNotFoundException e) { + return (ObjectStreamFactory<T>) + ExtensionLoader.instantiateExtension(ObjectStreamFactory.class, formatName); + } catch (ExtensionNotLoadedException e) { return null; } } 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 71b5822d..18eb2628 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; @@ -529,18 +527,13 @@ public class GeneratorFactory { } else { try { - Class factoryClass = Class.forName(className); - try { - Constructor constructor = factoryClass.getConstructor(); - AbstractXmlFeatureGeneratorFactory factory = - (AbstractXmlFeatureGeneratorFactory)constructor.newInstance(); - factory.init(generatorElement, resourceManager); - return factory.create(); - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException - | 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); } } @@ -621,21 +614,15 @@ public class GeneratorFactory { String className = element.getAttribute("class"); if (className != null) { try { - Class factoryClass = Class.forName(className); - try { - Constructor constructor = factoryClass.getConstructor(); - AbstractXmlFeatureGeneratorFactory factory = - (AbstractXmlFeatureGeneratorFactory)constructor.newInstance(); - factory.init(element, null); - 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/main/java/opennlp/tools/util/model/BaseModel.java b/opennlp-tools/src/main/java/opennlp/tools/util/model/BaseModel.java index a6a9a226..67106b6c 100644 --- a/opennlp-tools/src/main/java/opennlp/tools/util/model/BaseModel.java +++ b/opennlp-tools/src/main/java/opennlp/tools/util/model/BaseModel.java @@ -24,9 +24,11 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; +import java.io.ObjectStreamClass; import java.io.Serializable; import java.net.URL; import java.nio.file.Path; @@ -625,6 +627,44 @@ public abstract class BaseModel implements ArtifactProvider, Serializable { return isLoadedFromSerialized; } + /** + * Deserializes a {@link BaseModel} from the given {@link InputStream} using the + * standard {@link Serializable} mechanism, with class validation applied + * to reject any class outside the allowlist - including the top-level object itself. + * <p> + * Prefer this method (or the {@code BaseModel(String, InputStream|File|Path|URL)} + * constructors) over calling {@code new ObjectInputStream(in).readObject()} directly, + * since overriding {@link ObjectInputStream#resolveClass} ensures that unauthorized classes + * are rejected before their bytecode is loaded or instantiated. + * + * @param modelClass The expected {@link BaseModel} subclass. + * @param in The {@link InputStream} to read from. Must not be {@code null}. + * @param <T> The type of the expected model. + * @return A valid {@link BaseModel} instance of type {@code T}. + * + * @throws IOException Thrown if IO errors occurred or an unauthorized class was encountered. + * @throws ClassNotFoundException Thrown if required classes are not found. + */ + + public static <T extends BaseModel> T deserialize(Class<T> modelClass, InputStream in) + throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = new ObjectInputStream(in) { + @Override + protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + boolean isAllowed = name.startsWith("opennlp.tools.") + || "java.util.HashMap".equals(name) + || "java.lang.String".equals(name); + if (!isAllowed) { + throw new InvalidClassException("Unauthorized deserialization attempt for class: " + name); + } + return super.resolveClass(desc); + } + }) { + return modelClass.cast(ois.readObject()); + } + } + // These methods are required to serialize/deserialize the model because // many of the included objects in this model are not Serializable. // An alternative to this solution is to make all included objects
