Java vulnerabilities of the week: 11 to 16 September 2026

Most security news is useless to me as a developer: a CVE number, a score, one line saying an attacker may do something bad, and nothing about why. The part worth knowing is the mechanism, the exact queue with no limit, the exact name that turns out to be guessable. See the shape once and you spot it in your own code, which is worth far more then the score. I wrote about that in the CVE stigma post.

This week is coordinated batches, not one-off bugs: Netty fifteen advisories, Storm fifteen CVEs, Syncope twenty-two, ZooKeeper five. A pile of fixes on one day usually means someone swept the whole surface, and this week that someone was often an automated agent. The biggest item, the Netty batch, landed on 10 September, the day my last post went out, and missed it. It reaches more of you than anything else here, so it leads.

The week in five

An honourable mention for Apache Doris, CVE-2026-68570 and CVE-2026-72524, important, disclosed 14 September 2026, fixed in 4.0.8 and 4.1.4: authorization bypasses that let a low-privilege user read, write or drop tables they were never granted.

Netty: when the client stops reading, your server keeps allocating

Netty is the piece almost nobody depends on directly and almost everybody ships: it sits under Spring WebFlux, gRPC-Java, the Elasticsearch and Cassandra drivers, Vert.x, and a long tail of clients you never named in a pom.xml. So a fifteen-advisory batch reaches almost everyone.

HTTP/1.1 pipelining lets a client send request two before reading the response to request one, and the server must answer in order. HttpServerCodec remembers, per connection, which method each still-unanswered request used; the first thirty-two fit in a bit-packed long, the rest spill into an unbounded ArrayDeque. Pipeline thousands of requests and never read the responses, and that queue grows until the process falls over, because the responses that would drain it are stuck behind a client that will not read. The same shape appears in HttpContentEncoder and in the SMTP, QPACK and Bzip2 paths. Upgrade to 4.1.138.Final or 4.2.18.Final, both on Maven Central.

Why I find it interesting

This is CWE-770, no bound on state that grows with attacker-controlled work, and the lever is free: the attacker sends nothing malformed, they just decline to read. Pipelining decouples request arrival from response consumption, so any structure keyed to “requests currently in flight” is one the peer can size at will. Every per-connection buffer or queue whose size the other side influences needs an explicit cap and a backpressure story. “The client will read eventually” is an assumption, not a bound, and a hostile client’s whole job is to violate your assumptions.

Apache Storm: a predictable name is a capability you did not mean to hand out

A Storm cluster is multi-tenant: many users submit topologies that run as separate workers, often as different OS users, all sharing Nimbus and a blob store. This batch is a tour of where that isolation leaked.

The one to remember is CVE-2026-82428. When you run storm jar --artifacts, Storm stored each Maven dependency in the blob store under a key derived from its coordinates, dep-<group>-<artifact>-<version>.jar, identical and predictable for every user. When a blob already existed under that key, the uploader silently reused it with no check of owner or contents. So whoever uploaded a coordinate first controlled the bytes every later submitter loaded onto their worker classpath. Poison a commons-lang3 once and the next tenant who declares it runs your code. Thats cross-tenant remote code execution out of a naming decision. The fix in 3.1.0 uses UUID keys and lives in the submitting client, so every storm client has to upgrade, not just the cluster. Alongside it sit CVE-2026-82429, a TOCTOU race where the setuid-root worker-launcher re-resolves a pathname after FTS has classified it, so a tenant swaps a directory for a symlink and redirects a root-privileged chown, and CVE-2026-82426, where Nimbus accepts an uploadedJarLocation it never confirmed was uploaded, handing a tenant its own keytab.

Why I find it interesting

A predictable, shared identifier is a capability. A blob key computed from public Maven coordinates is a name anyone can compute and claim, the insecure-direct-object-reference family, and “reuse whatever is already here” turns it into a way to plant bytes for someone else to execute. The fix is two parts together: unguessable identifiers, and an ownership check on reuse. More broadly, in a multi-tenant system every point where one tenant’s input becomes another’s execution context is a boundary to enforce, not to assume was handled upstream.

What I take away from this week

Two things to act on. First, these are batches: when a project ships fifteen or twenty-two fixes at once it swept the whole surface and designed the fixes as a set, so take the batch release (Netty 4.1.138.Final or 4.2.18.Final, Storm 3.1.0, ZooKeeper 3.9.6 or 3.8.7, Syncope 4.0.8 or 4.1.3, OpenNLP 2.5.12) rather then cherry-picking the top score. Second, where the fix reaches you differs: Netty arrives transitively, so a Spring Boot, Camel or Quarkus BOM bump carries it, though check your resolved io.netty because pins lag; the rest (Storm, ZooKeeper, Syncope, Doris) are services you operate and patch by hand, and Syncope 3.0.x means a move to 4.x because there is no 3.0 fix.

One thing is worth stating plainly, because it is the factual news: many of the Storm advisories credit “the ASF using Claude Agents” as the finder. An agent walked every opcode and code path and produced a coherent batch that maintainers then triaged, fixed and shipped under their own review. The shift is coverage, and these are real, project-validated CVEs. The interesting question is still the mechanism, not who filed the report.

References