Java vulnerabilities of the week: 1 to 7 October 2026
A vulnerability feed gives you a CVE id, a severity, a version range and a sentence saying an attacker may cause a denial of service. The useful part is the mechanism, the length field nobody checked, the object two threads were holding at the same moment, because that’s what I can go and look for in my own code, and it’s worth far more then the score. That’s why this series exists, and the mindset behind it is in the CVE stigma post.
Most of this week is one idea in different clothes: the other side gets to decide something it shouldn’t. A Thrift peer says how many elements a list has, an LDAP server says how big the next element is and how many bcrypt rounds a password check will cost, a Struts form field picks the scale of a decimal and with it the size of the page, a request header picks where a Logback appender writes. The two deep dives are the exceptions: a Struts bug that needs no attacker at all, and XML hardening in Camel Quarkus that had no effect because the implementation underneath doesn’t support it.
The week in five
-
Apache Struts, four CVEs, disclosed 5 October 2026, fixed in 6.12.0 and 7.4.0.
CVE-2026-104711(S2-075, moderate) is an OGNL injection through the legacyRestfulActionMapper, which used the action name from the URL without the allowlist checkDefaultActionMapperapplies. On Struts 7 it needs the OGNL allowlist disabled, which is not the default.CVE-2026-104712(S2-076, moderate) is an amplification: aBigDecimalbound from a request parameter and rendered by the tag library was formatted with unlimited fraction digits, and with the same JDK calls Struts makes, the ten characters1E-1000000render as 1,000,002 characters. The fix caps rendering at 340 fraction digits.CVE-2026-104713(S2-077, important) is the REST plugin reading request bodies into memory with no limit, now 2 MB by default.CVE-2026-104714(S2-078) is the first deep dive. Both have been on Maven Central since 17 September. -
Apache Camel Quarkus,
CVE-2026-88789, CVSS 8.6, disclosed 1 October 2026, fixed in 3.33.3 and 3.40.0. An XXE in the XSLT support extension,camel-quarkus-support-xalan, which supplies a Xalan-backedTransformerFactoryto the xslt component and registers it as the JAXP default. Xalan-J 2.7.x doesn’t honour the JAXP 1.5 external access attributes, so a document that reaches the transformer as ajavax.xml.transform.Sourcecan read local files or reach internal hosts through an external entity. It comes in withcamel-quarkus-xslt,camel-quarkus-xslt-saxon,camel-quarkus-tikaorcamel-quarkus-xmlsecurity, and for the last three only code using the JAXP default factory is exposed. Second deep dive below. -
Apache Directory LDAP API, six CVEs, disclosed 2 October 2026, fixed in 2.1.9. The Java LDAP library under Apache Directory Server. Three are critical: a small BER-encoded response makes the decoder allocate a large buffer before any data arrives (
CVE-2026-102731), a deeply nested search filter overflows the server decoder’s stack before bind (CVE-2026-103552), and a rogue server, or a man in the middle before TLS, answersloadSchema()with schema elements carrying Java bytecode that the client loads and instantiates (CVE-2026-103877, now off unless a system property enables it). Then plaintext responses injected between a StartTLS request and the end of the handshake were accepted (CVE-2026-103878), a stored bcrypt hash carries its own cost factor, so a$2a$30$value costs hours of CPU per bind (CVE-2026-103880, capped at 16 by default), and crafted telephone numbers pin a CPU core (CVE-2026-103885). Two records give the range as 1.2.0 before 1.2.9, a line not on Maven Central; the other four say 2.1.0 before 2.1.9, and 2.1.9 carries all six fixes. -
Apache Thrift 0.25.0, 61 CVEs, announced 1 October 2026. The release shipped on 30 September and the batch was summarised on oss-security on 4 October. Most are in other bindings. In
libthrift, the Java library,TSaslNonblockingServerallocated SASL frames without an upper bound before authentication (CVE-2026-61373, with two follow-ups in the same server,CVE-2026-94639andCVE-2026-94651),TSaslTransporthad no size limit on data frames after authentication (CVE-2026-61374), list, set and map headers declare an element count that was not checked against the bytes left in the message (CVE-2026-82458), andTJSONProtocolaccepted a single string or number above the configured limit (CVE-2026-66055). Everything before 0.25.0 is affected. -
Logback,
CVE-2026-104721, announced with 1.6.5 on 30 September, CVE record published 2 October 2026. ASiftingAppenderwithMDCBasedDiscriminatorbuilds a nested appender for each MDC value, usually with the value in the file name, so an attacker who influences that value, through an HTTP header for example, can create and append to log files outside the intended directory. It followsCVE-2026-19880, for which 1.6.3 removed/and\from the value. In 1.6.5 the discriminator rejects instead of editing: an empty value, one longer than 64 characters, or one containing..or characters such as$,{,},%,,and@falls back to theDefaultValue, which also covers variable substitution like${file.separator}. The record covers 0.9.14 through 1.6.4, so no 1.5 release has the fix.
Honourable mention for Apache Commons BCEL 6.13.0, CVE-2026-94114 and CVE-2026-105111, disclosed 6 October 2026. The class repositories cached a parsed class under the this_class name inside its own bytes rather then the name it was requested by, so one class file could take over another’s cache entry, a mismatch the JDK’s defineClass() refuses. The record for 94114 is titled as recursion in ClassParser, while its description, CVSS vector and referenced commit all describe the cache issue. 105111 is a stored XSS in Class2HTML. One older item may have reached your scanner in the last few days: four Jackson denial-of-service advisories from 22 September, which entered the GitHub Advisory Database on 30 September and 1 October, fixed in 2.18.11, 2.21.7, 2.22.3, 3.1.7 and 3.2.3.
Apache Struts: a cached formatter is shared mutable state
Struts renders localized messages through an application-wide text provider. A message like Your order shipped on {0,date,long} is a java.text.MessageFormat pattern, and building a MessageFormat means parsing the pattern and creating a sub-format for each argument. Nobody wants to do that on every request, so AbstractLocalizedTextProvider.buildMessageFormat() caches the result by pattern and locale.
Up to 7.3.0 it returned the cached instance itself, the same object to every request. MessageFormat is not thread-safe, its Javadoc asks for one instance per thread or external synchronization. The date sub-format is a SimpleDateFormat, which keeps a Calendar in a field and formats a date by setting that calendar and then reading the year, month and day back out of it. Two requests formatting the same message at the same moment share that calendar, so one can set it while the other is reading, and the second prints a date that isn’t its own, a mix of the two, or throws from inside the calendar code as a server error.
I tried the JDK part on its own: one shared MessageFormat with a {0,date,yyyy-MM-dd} argument, four threads, two formatting 1 January 1999 and two formatting 7 October 2026. In one run more than half of 800,000 calls returned a wrong date and a few thousand threw ArrayIndexOutOfBoundsException. The numbers moved between runs. With a fresh copy for each call they stayed at zero.
The S2-078 bulletin says that triggering it requires no malicious request: ordinary concurrent traffic is enough, and the users harmed are the application’s own. Two customers loading the same page at the same moment is the whole trigger, and one customer’s appointment or delivery date can show up on the other’s screen. No message shipped with Struts formats a date, so the exposure is in your own resource bundles, and the workaround is to format the date yourself and pass the finished string.
Fix in 7.4.0 and 6.12.0 is one line: buildMessageFormat() now returns format.clone(), with a comment saying the cached instance is a template that is never formatted directly. Cloning a MessageFormat clones its sub-formats, and cloning a DateFormat clones its calendar, so the expensive parse stays cached and every caller gets its own state.
Why I find it interesting
This is CWE-362, a race on a shared resource, in a shape most Java web developers meet eventually: a mutable object that isn’t thread-safe, kept somewhere that outlives a request. A servlet field, a field on a Spring singleton, a static final SimpleDateFormat, and here a cache. The cache version is the easiest to miss, because a cache reads like a performance decision, and handing the same mutable object to every caller is sharing whatever the map is called. Cloning from a cached template is a good pattern for anything expensive to build and unsafe to share, and for dates java.time.format.DateTimeFormatter is immutable and thread-safe, so if you still have a SimpleDateFormat constant in code that serves requests, its worth replacing. An attacker can’t aim this bug. It shows up first as an odd support ticket, a customer seeing a date that isn’t theirs, long before anyone calls it a vulnerability.
Apache Camel Quarkus: XML hardening only counts if the implementation honours it
Camel Quarkus is part of the Camel family I work in, and this one was found by internal analysis, so it’s close to home.
JAXP is a set of interfaces with pluggable implementations. TransformerFactory.newInstance() returns whatever the JVM is configured to provide, the JDK’s built-in XSLTC, Saxon or Xalan, depending on system properties and the classpath, and hardening goes through the same interfaces. JAXP 1.5, in Java 7u40 and Java 8, added XMLConstants.ACCESS_EXTERNAL_DTD and ACCESS_EXTERNAL_STYLESHEET. Set both to an empty string and the factory won’t fetch external DTDs, entities or stylesheets, which is what stops <!ENTITY x SYSTEM "file:///etc/passwd"> from pulling the file into the output. Camel sets both on the factories it creates.
An attribute is a request to the implementation, and an implementation can only honour the attributes it knows. Xalan-J 2.7.x predates JAXP 1.5. According to the fix commit, its setAttribute() throws IllegalArgumentException for both attributes, and its FEATURE_SECURE_PROCESSING limits extension functions without the external access restrictions the JDK ties to the same feature. Hardening code commonly wraps the call in a try/catch that ignores the exception, so an implementation without support doesn’t break startup, and Camel’s own XmlConverter does that after calling TransformerFactory.newInstance(). With Xalan registered as the JAXP default, that code completes without an error and returns a factory with none of the restrictions, and any other library in the application that calls newInstance() gets a Xalan factory too.
On the xslt component path only bodies that already are a javax.xml.transform.Source were exposed, since Camel converts other body types itself with external entities disabled. The fix stops asking Xalan for something it can’t do: XalanTransformerFactory now parses input documents with an XMLReader that resolves no external entities and loads no external DTDs, and refuses resources requested through the XSLT document() function unless the application’s own URIResolver resolves them. Until you upgrade, leave the body as a String, byte[] or InputStream. The advisory notes that convertBodyTo on an existing Source doesn’t help, because that conversion is an identity transform through the same factory.
Why I find it interesting
The class is XXE, CWE-611, and the lesson is how a hardening step fails open. The try/catch around the attribute call has a good reason behind it, and the consequence is that when the protection can’t be applied, execution carries on as if it had been. And the classpath picks the implementation: a dependency that registers a TransformerFactory changes what newInstance() returns for every library in the JVM, and none of them are told.
Two habits I’d keep from this. When a security setting can’t be applied, log it at warn or fail, don’t swallow it, which is what the follow-up commit does for secure processing on the input reader. And check which implementation you actually got: print TransformerFactory.newInstance().getClass() once in the running application, or ask for the JDK one explicitly, as the advisory recommends, by naming com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl. On Java 9 and later TransformerFactory.newDefaultInstance() does the same without naming an internal class. A security property is only as strong as the implementation that receives it.
What I take away from this week
First, who patches. Struts, Thrift, the LDAP API and BCEL are libraries you declare or inherit transitively, and no framework BOM will move them for you, so start from mvn dependency:tree: Struts 6.12.0 or 7.4.0, libthrift 0.25.0, LDAP API 2.1.9, BCEL 6.13.0. Camel Quarkus moves with the platform, 3.40.0, or 3.33.3 on the LTS stream. Logback needs a closer look on Spring Boot: Boot 4.1.1 and 4.0.8 manage Logback 1.5.38, the 4.2 milestones are on 1.6.x, and no 1.5 release has the fix. Most applications never configure a SiftingAppender, but if yours sifts on an MDC value from a request, the fix is 1.6.5 as an explicit, tested override, or keeping request data out of that key.
Second, look at the fixes rather than the bugs. Thrift bounds a declared element count by the bytes actually available, the LDAP API caps the bcrypt cost at 16, Struts caps fraction digits at 340 and REST bodies at 2 MB, Logback caps a discriminator value at 64 characters. Each one is a limit the receiver owns. For every length, count, cost or depth you parse off the wire, the review question is what it’s checked against, and who chose that number.
Third, the volume: 61 CVEs in one Thrift release, six in the LDAP API, four in Struts, and several advisories name a tool next to the people. The LDAP API and BCEL records credit Claude Security as a tool alongside the ASF, Thrift’s CVE-2026-61373 lists Claude (Anthropic Research) among its finders, and the Thrift summary on oss-security says it was drafted with AI assistance and reviewed and sent by Jens Geyer for the PMC. I wrote yesterday about what the human in the loop is actually doing. For a reader of advisories the practical side is simpler: with more CVEs per release, the count tells you less and the mechanism tells you more.
References
- oss-security: CVE-2026-88789, Apache Camel Quarkus - Openwall oss-security
- Apache Camel security advisory CVE-2026-88789 - Apache Camel
- Camel Quarkus: apply Camel’s JAXP restrictions in the xslt extension - apache/camel-quarkus on GitHub
- Camel Quarkus: skip external DTDs and parameter entities in XSLT input - apache/camel-quarkus on GitHub
- Camel XmlConverter, TransformerFactory creation and hardening - apache/camel on GitHub
- TransformerFactory, Java SE 21 API - Oracle
- oss-security daily index for 2 October 2026, Apache Directory LDAP API - Openwall oss-security
- Directory LDAP API: require a system property to load schema bytecode - apache/directory-ldap-api on GitHub
- Directory LDAP API: no clear text operations during StartTLS - apache/directory-ldap-api on GitHub
- Directory LDAP API: limit the bcrypt work factor - apache/directory-ldap-api on GitHub
- oss-security: Apache Thrift 0.25.0, 61 CVEs fixed - Openwall oss-security
- CVE-2026-61373, Apache Thrift TSaslNonblockingServer SASL frame allocation - Apache Thrift dev list
- CVE-2026-82458, Apache Thrift container element count - Apache Thrift user list
- oss-security daily index for 5 October 2026, Apache Struts - Openwall oss-security
- S2-075 - Apache Struts wiki
- S2-076 - Apache Struts wiki
- S2-077 - Apache Struts wiki
- S2-078 - Apache Struts wiki
- Struts WW-5724, hand out a clone of the cached MessageFormat - apache/struts on GitHub
- Struts WW-5711, bound fraction digits when formatting BigDecimal - apache/struts on GitHub
- MessageFormat, Java SE 21 API - Oracle
- Logback news, release of 1.6.5 - QOS.ch
- CVE-2026-104721 - CVE.org
- Logback: MDCBasedDiscriminator rejects unsafe MDC values - qos-ch/logback on GitHub
- oss-security: CVE-2026-94114, Apache Commons BCEL - Openwall oss-security
- Commons BCEL: refuse a class whose name doesn’t match the request - apache/commons-bcel on GitHub
- GHSA-7hhh-6rmp-j9qf, jackson-core - GitHub Advisory Database
- GHSA-p6pp-m3f8-5c89, jackson-core - GitHub Advisory Database
- GHSA-cxp5-3px4-pw24, jackson-databind - GitHub Advisory Database
- GHSA-wv8q-qhhj-9h54, jackson-databind - GitHub Advisory Database
- Spring Boot 4.1.1 dependency management, Logback 1.5.38 - Maven Central
- Is there really a human in the loop? - oscerd.github.io
- The CVE stigma, we need a new security culture - oscerd.github.io