Skip to content

Lakehouse Dependency CVE Ledger, November 2026: Arrow 26.0.1, Parquet Java 1.18.2 and What to Patch First

Arrow 26.0.1 fixes a critical bundled Brotli CVE in PyArrow wheels, and Parquet Java 1.18.2 bumps its shaded Jackson. What to patch first.

Cross-posted. This article’s canonical home is Data Lakehouse Hub.

Two lakehouse dependencies moved security-driven patches the week of November 2, 2026. Apache Arrow 26.0.1 passed its release vote with a fix for the Brotli library that PyArrow wheels and R binaries link statically. Apache Parquet Java 1.18.2 entered voting with a newer shaded Jackson to clear several high-severity Jackson CVEs. This ledger tracks each fix, the components affected and the order to upgrade.

The hard part of lakehouse security is rarely the headline CVE. It is the copy of a vulnerable library that hides inside something else. A PyArrow wheel carries its own Brotli. A parquet-jackson jar carries its own Jackson under a renamed package. Your Spark distribution carries its own Parquet. Your scanner flags the top-level brotli package, you upgrade it, the dashboard goes green, and the vulnerable decoder is still sitting inside libarrow.

That is why this page exists. It is a recurring, dated table of security-relevant patches across Apache Arrow, Apache Parquet, Apache Iceberg and Apache Polaris. Each row names the component that carries the vulnerable code, not just the project. I update the table on this page with a changelog, so the URL stays the place to check.

A note on scope. This ledger covers the open source projects. Commercial engines, including the one my employer ships, bundle these libraries and publish their own advisories. When an engine vendor’s advisory and this table disagree about your exposure, the vendor’s advisory for its build wins.

The Ledger, as of November 3, 2026

ProjectVersionIssueComponent carrying the codeStatusDateSource
Apache Arrow26.0.1CVE-2026-111497 (Brotli decoder out-of-bounds write)PyArrow wheels, R binaries (bundled Brotli 1.0.9)Vote passed, release blog publishedNov 3, 2026Arrow 26.0.1 release blog, GH-55165
Apache Arrow25.0.xSame CVE, backport requestedPyArrow 25.x wheelsRequest openNov 2026apache/arrow issue #55390
Apache Parquet Java1.18.2 RC0CVE-2026-89407, CVE-2026-89425 (jackson-core), plus jackson-databind advisoriesparquet-jackson shaded JacksonVote openedNov 3, 2026dev@parquet, PR #3846, issue #3857
Apache Parquet Java1.18.1CVE-2026-73334parquet-java readersReleasedEarlierParquet release notes
Apache Icebergn/aNone found in windown/an/aOct 27 to Nov 3dev@iceberg
Apache Polarisn/aNone found in windown/an/aOct 27 to Nov 3dev@polaris

“None found in window” means I found no security-driven release or vote for that project between October 27 and November 3. It does not mean the project has no open issues. It means nothing in this window changed what you need to patch.

Arrow 26.0.1 and the Bundled Brotli

What the vulnerability is

Brotli is a general-purpose compression format created at Google. It shows up in HTTP content encoding, in web fonts and, for lakehouse teams, as one of the compression codecs Parquet supports. CVE-2026-111497 is an out-of-bounds write in the Brotli decoder. Google published Brotli 1.2.1 with the fix, and the oss-security mailing list tracked downstream projects that bundle the decoder. Apache Arrow was on that list, alongside DuckDB, ClickHouse, Chromium, Firefox and many others.

An out-of-bounds write in a decoder is the dangerous kind of bug. The decoder runs on input you did not create. If an attacker controls the compressed bytes, they control what the decoder writes past its buffer. A backport request filed against Arrow describes the CVE as critical, with a CVSS score of 9.2. CVSS is the Common Vulnerability Scoring System, the standard 0 to 10 severity scale.

Why upgrading brotli does not fix PyArrow

Arrow’s issue tracker states the root cause plainly. The Arrow C++ build bundled Brotli 1.0.9, and PyPI wheels for PyArrow link that copy statically. The Arrow maintainers opened GH-55165 to take a newer Brotli, marked a duplicate report on October 27, and targeted the fix at the 26.0.1 milestone. On November 2, an Arrow release manager confirmed in the issue that the fix was going into 26.0.1, not 27.0.0.

Static linking means the decoder code lives inside the PyArrow shared library. Installing brotli==1.2.1 from PyPI updates the standalone Python package. It does nothing to the copy compiled into libarrow. A third-party write-up on the CVE made the same point and noted that common dependency scanners did not flag the bundled copy in PyArrow 26.0.0, because the scanners match package metadata, not compiled code.

What 26.0.1 changes

The Arrow 26.0.1 release blog, published November 3, says the release bumps the bundled Brotli version for PyArrow and for R builds, and that the Brotli CVE affected PyArrow wheels and R binaries. A downstream pull request upgrading to 26.0.1 reports that the release bundles Brotli 1.2.1.

The blog frames the scope precisely: the CVE affected PyArrow wheels and R binaries. If you build Arrow C++ yourself against a system Brotli, your exposure depends on that system Brotli, and your OS vendor’s advisory applies instead.

Where the exposure is real

The risk depends on whether you decode Brotli data you did not produce. For lakehouse workloads, that means three paths.

If every Parquet file you read comes from your own writers and none use Brotli, the practical risk is low. Patch anyway. The cost of the upgrade is small next to the cost of proving that no Brotli file ever reaches a reader.

The NumPy catch

PyArrow 26.0.0 dropped support for NumPy 1.x. That makes 26.0.1 a bigger step for teams still on PyArrow 25.x than a patch release suggests. A downstream project that upgraded on November 3 had to raise its minimum NumPy to 2.x, its pandas floor to 2.2 and its Python floor to 3.11, because the Arrow 26 wheel rejects NumPy 1.x at import.

That friction is why a user opened issue #55390 asking the Arrow project for a 25.0.x patch release carrying only the Brotli bump. As of this writing, that request is open. Watch it. If a 25.0.x patch ships, teams pinned to NumPy 1.x get a fix without a NumPy migration.

Parquet Java 1.18.2 and the Shaded Jackson

What the vulnerabilities are

Jackson is the most common JSON library on the JVM. Parquet Java does not depend on it in the normal Maven way. It ships a module called parquet-jackson that contains a shaded copy of jackson-core and jackson-databind, relocated under shaded.parquet.com.fasterxml.jackson. Shading renames the packages so Parquet’s Jackson never collides with the application’s Jackson.

Parquet 1.18.1 shades Jackson 2.22.2. The Parquet PR that bumped the master branch to 2.22.3 on October 4 states that 2.22.2 is affected by CVE-2026-89407 and CVE-2026-89425 in jackson-core, both fixed in 2.22.3. An issue filed against 1.18.1 lists four high-severity GitHub advisories found by the Grype scanner in the shaded jar, two in jackson-core and two in jackson-databind, with 2.22.3 as the fixed version.

The fix landed on the parquet-1.18.x release branch on October 10 as a backport, PR #3846. A later PR on the same branch, added to the 1.18.2 milestone on October 28, bumps Jackson again to 2.22.4. Check the RC’s pom.xml to see which Jackson version 1.18.2 actually ships. Either version clears the advisories listed for 2.22.2.

Why you cannot fix it from outside

Shading is great for avoiding version conflicts and terrible for patching. Your build cannot override the Jackson inside parquet-jackson with a dependency pin, because the shaded classes are not the Jackson artifact. They are Parquet’s own classes with renamed packages.

Excluding the jar does not work either. The 1.18.1 issue reports that excluding parquet-jackson breaks Parquet writes with NoClassDefFound errors, because parquet-hadoop references the shaded classes directly in its encryption key-tools code and in metadata handling.

The Parquet PR names the downstream cost directly. Projects that bundle parquet-jackson cannot upgrade the shaded copy themselves, and Apache Iceberg has had to ignore these findings in its Kafka Connect runtime CVE scan. Other projects did the same. One Iceberg writer added the two jackson-databind CVEs to its Trivy ignore file and documented why: Parquet only deserializes with the shaded copy in ParquetMetadata.fromJSON and in the crypto key tools, and that project calls neither.

How exposed are you, really

That reasoning is worth copying, with care. The shaded Jackson deserializes JSON in two places that matter: reading Parquet metadata from JSON and handling key material for Parquet modular encryption. If your code never calls ParquetMetadata.fromJSON and you do not use Parquet encryption with the Hadoop key-material store, an attacker has no obvious path to feed the shaded Jackson hostile JSON.

“No obvious path” is not “no path.” A scanner ignore is a documented risk acceptance, not a fix. Write down the reasoning, attach an expiry date, and remove the ignore when 1.18.2 is released.

Finding Transitive Copies

The ledger only helps if you can find where these components live in your estate. Here is a practical sweep. Each command is real and runs as shown.

# 1. Python: which PyArrow is installed in each environment?
python -c "import pyarrow; print(pyarrow.__version__)"

# List every environment's PyArrow on a host with many venvs
find / -path '*/site-packages/pyarrow/__init__.py' 2>/dev/null \
  | xargs -I{} dirname {} \
  | while read d; do
      grep -m1 '__version__' "$d/_generated_version.py" 2>/dev/null \
        && echo "  in $d"
    done

# 2. JVM: which parquet-jackson jars ship with Spark, Flink or Trino?
find /opt -name 'parquet-jackson-*.jar' 2>/dev/null

# 3. Maven builds: who pulls Parquet in transitively?
mvn dependency:tree -Dincludes=org.apache.parquet

# 4. Gradle builds: same question
./gradlew dependencies --configuration runtimeClasspath \
  | grep -E 'parquet-(jackson|hadoop|column)'

# 5. Scan a directory of jars and a container image with Grype
grype dir:/opt/spark/jars
grype my-registry/etl-image:latest

# 6. Same scan with Trivy, which reads inside jars
trivy fs --scanners vuln /opt/spark/jars

Step one asks the running interpreter directly, which beats reading a requirements file that nobody re-synced. The find loop in step one matters on shared hosts where notebooks, schedulers and services each run their own virtual environment. PyArrow writes its version to _generated_version.py, so the loop reports every copy without importing each one.

Step two finds the jar by name. Engine distributions put their jars in a jars/ or lib/ directory, and the version is in the filename. Steps three and four answer the question for your own builds, where Parquet usually arrives through iceberg-parquet, parquet-avro or a Spark dependency rather than directly.

Steps five and six run Grype and Trivy, two widely used open source scanners. Both inspect jar contents and both flagged the shaded Jackson in the issue reports cited above. Neither is guaranteed to flag a statically linked C library inside a Python wheel, which is why step one exists. For PyArrow, trust the version number, not the scanner.

Where the copies usually hide

Finding Brotli-Compressed Files Before They Find You

Knowing which libraries carry the vulnerable decoder is half the picture. The other half is knowing whether your data uses Brotli at all. Parquet records the compression codec for every column chunk in the file footer, so you can answer the question without decompressing a single page.

import sys
import pyarrow.parquet as pq

def brotli_columns(path):
    """Return (row_group, column) pairs compressed with Brotli."""
    meta = pq.ParquetFile(path).metadata
    hits = []
    for rg in range(meta.num_row_groups):
        row_group = meta.row_group(rg)
        for col in range(row_group.num_columns):
            chunk = row_group.column(col)
            if chunk.compression.upper() == "BROTLI":
                hits.append((rg, chunk.path_in_schema))
    return hits

for path in sys.argv[1:]:
    hits = brotli_columns(path)
    status = f"{len(hits)} Brotli column chunks" if hits else "no Brotli"
    print(f"{path}: {status}")

The script opens each file’s footer with pq.ParquetFile and walks the metadata. Every row group holds one column chunk per column, and each chunk reports its own compression value, because Parquet lets a writer pick a codec per column. That detail matters. A file can use Zstandard for most columns and Brotli for one, so checking only the first column misses it.

One caution. Reading the footer is safe because the footer itself is Thrift-encoded metadata, not Brotli data. The decoder only runs when a reader decompresses a page. Running this check with an unpatched PyArrow does not trigger the bug. It tells you whether a later read will.

For Iceberg tables, you can skip the file-by-file sweep. Iceberg records the table’s default codec in the write.parquet.compression-codec table property. Query the table properties in your engine or load the table with PyIceberg and read table.properties. Tables without that property use the engine’s default, which is Zstandard in current Iceberg releases. Old tables written before that default changed still hold Gzip or Snappy files, but Brotli almost always appears because someone chose it on purpose, which makes it easy to find.

Triage: Ranking a Lakehouse CVE in Five Questions

Every week brings new advisories, and most do not deserve an emergency. These five questions sort them quickly. I apply them to every row before it enters the ledger.

Does the vulnerable code parse untrusted input? Decoders, deserializers and file readers sit on the attack surface. Build tools and test utilities rarely do. The Brotli bug lives in a decoder that reads file contents, so it ranks high. The shaded Jackson bugs live in a deserializer that Parquet calls only in narrow paths, so they rank lower for most readers.

Is the code reachable from your workloads? A vulnerable function you never call is a scanner finding, not an incident. Trace the call path. If a Parquet reader never deserializes JSON metadata and never touches encryption key material, the shaded Jackson path is closed for that reader.

Is the code bundled or linked dynamically? Dynamically linked libraries get fixed by the OS package manager. Bundled and shaded copies wait for the bundling project. Bundled copies add days to your timeline, so start tracking them earlier.

Is a fixed release available? A released fix gets scheduled. An RC gets watched. An open issue gets a mitigation and a date to revisit.

What does the upgrade break? The PyArrow 26 line requires NumPy 2. A Parquet upgrade inside Spark requires a Spark release. Knowing the upgrade cost up front keeps you from promising a same-day patch you cannot deliver.

A CVE that answers yes, yes, bundled, released and cheap is a this-week patch. One that answers no, no, dynamic, unreleased and expensive is a documented watch item. Most fall in between, and the five answers tell you where.

A Policy for Untrusted File Intake

Patching fixes this week’s bug. An intake policy limits the next one. Lakehouse platforms read files from many sources, and the riskiest are the ones you did not produce.

Route untrusted files through a quarantine zone. Land uploads, partner drops and public datasets in a separate bucket or prefix. Do not register them in the production catalog until they pass checks.

Validate before you decompress at scale. Read the footer, check codecs against an allowlist, check the schema against an expected contract and reject anything unexpected. A footer read is cheap and touches no compressed page data.

Rewrite on admission. Once a file passes checks, rewrite it with your own writer and your chosen codec, then register the rewritten file. Downstream readers only ever see files your platform produced. A codec allowlist of Zstandard and Snappy, for example, keeps Brotli off every production read path regardless of what arrives at the door.

Isolate the readers that touch quarantine. Run them in separate containers with no credentials to production storage. If a decoder bug is triggered, the blast radius ends at the quarantine zone.

None of this replaces patching. It turns the next decoder CVE from an emergency into a scheduled upgrade.

Upgrade Order and What to Test

Order matters, because each upgrade has a different blast radius.

First: PyArrow in services that read untrusted Parquet or Arrow IPC. This is the critical-severity, decoder-path bug with a fix already released. Start with ingestion services, public-facing APIs that accept file uploads, and notebooks that read partner data. Upgrade to 26.0.1.

Test three things. Import PyArrow and confirm the version. Round-trip a Brotli-compressed Parquet file through pyarrow.parquet.write_table with compression='brotli' and read it back. Run your test suite under NumPy 2.x if you came from 25.x, because NumPy 2 changed default integer types on some platforms and removed several deprecated aliases.

Second: PyArrow everywhere else, and R arrow binaries. Internal pipelines that read only their own files can follow on a normal cadence. If NumPy 2.x blocks you, track issue #55390 for a possible 25.0.x patch, and in the meantime block Brotli-compressed input at the boundary.

Third: Parquet Java, once 1.18.2 is released. Do not deploy an RC to production. When the release lands, upgrade your own builds first, because you control their dependency trees. Then remove any scanner ignores you added for the shaded Jackson.

For engine distributions, Spark, Flink and Trino each need their own release that picks up Parquet 1.18.2. Track those. Do not swap Parquet jars inside an engine distribution unless the engine’s maintainers document that the swap is supported. Mismatched Parquet versions inside Spark cause subtle reader bugs that are worse than a documented, mitigated CVE.

Test Parquet upgrades with a read of every Parquet file shape you produce: each compression codec, files with and without page indexes, files with bloom filters and encrypted files if you use Parquet modular encryption. The Jackson bump does not change Parquet’s file format, but the encryption key tools use the shaded Jackson directly, so encrypted reads deserve an explicit test.

Fourth: housekeeping. Remove expired scanner ignores, rebuild fat jars and container images that captured old versions, and record the date of each change.

Building the Ledger Into Your Own Process

A ledger like this is only useful if someone checks it before an incident, not during one. Three habits make that happen.

Subscribe to the right lists. Every Apache project announces releases on its dev list with a [VOTE] thread, a [RESULT][VOTE] thread and an [ANNOUNCE] email. Security fixes often show up first as an issue or a PR, then as a release vote. Following dev@arrow.apache.org, dev@parquet.apache.org, dev@iceberg.apache.org and dev@polaris.apache.org costs a few minutes a week. The Apache Software Foundation also publishes security advisories through its security team, and those reach the project’s announcement channels.

Keep a software bill of materials per image. A software bill of materials (SBOM) lists every component in a build. Generate one with Syft or Trivy for every container image and every engine distribution you deploy. When a new CVE lands, query the SBOMs instead of logging into hosts. The query takes seconds. The host sweep takes days.

Separate “fixed” from “mitigated.” For every open CVE in your estate, record which of the two it is. Fixed means the vulnerable code is gone. Mitigated means you documented why it cannot be reached and set a date to revisit. Mitigations without dates turn into permanent risk acceptances that nobody remembers approving.

Why These Bugs Keep Happening

Both CVEs in this week’s ledger share a root cause: a library bundling another library. Arrow bundles Brotli so PyArrow wheels install anywhere without a system dependency. Parquet shades Jackson so it never conflicts with an application’s Jackson. Both are good engineering choices for portability. Both turn a single upstream fix into a release dependency on the bundling project.

The projects know this. The Parquet issue tracker has an open item to audit third-party code bundled in published jars. Arrow’s issue tracker shows maintainers responding to a downstream CVE within days and cutting a patch release around it. That is the system working. It still adds a hop between the upstream fix and your fix, and this ledger is meant to make that hop visible.

The lakehouse makes the problem wider than most stacks. One table format touches Python, Java, Rust and C++ readers. A Parquet file written by Spark is read by PyArrow in a notebook, by DuckDB on a laptop and by a JVM engine in production. Each reader carries its own decoders. A file-format CVE is never a single upgrade.

There is a second reason the problem grows. Agents and notebooks now read lakehouse files directly far more often than they did two years ago. A data agent that loads a Parquet file a user pointed it at is an untrusted-input reader by design. So is a notebook that pulls a public dataset over HTTPS. Every one of those readers runs PyArrow or a similar library, usually in an environment someone created months ago and never rebuilt. The ledger matters more as more of these readers appear, because each one is a copy of the decoder that nobody tracks centrally.

Watch List for Next Week

Changelog

Conclusion

This week’s two patches look small. One bumps a bundled compression library. The other bumps a shaded JSON library. Both matter because the vulnerable code hides inside components your scanner reports as something else, and because the fix arrives only when the bundling project ships a release.

Patch PyArrow first, starting with every service that reads files you did not write, and plan for the NumPy 2 step if you are coming from 25.x. Prepare your Parquet Java upgrade now so you can roll it out the day 1.18.2 ships, and remove the scanner ignores when you do. Keep an SBOM for every image so the next sweep takes minutes. And check this page next week, because the ledger will move again.

Keep Going

If this piece was useful, I have written a lot more on the file formats and libraries underneath the lakehouse. Architecting an Apache Iceberg Lakehouse (Manning) covers how Parquet, Arrow, Iceberg and the catalog fit together, which is the map you need to know where a dependency CVE reaches. You can find every book I have written, across lakehouse architecture, Apache Iceberg, Apache Polaris, and AI, at books.alexmerced.com.

Work with Alex