---
id: "CVE-2026-107224"
page: "https://infrastack.ai/live/CVE-2026-107224"
kind: "vulnerability"
aliases: ["CVE-2026-107224", "GHSA-fw94-4wwp-w8pw", "GO-2026-6679"]
title: "Excelize: A Zip64 uncompressed-size of 2^63 panics OpenFile/OpenReader"
severity: "medium"
cvss: 6.5
epss: 0.00306
exploited: false
status: "open"
category: "package"
products: [{"name": "github.com/360EntSecGroup-Skylar/excelize/v2", "fixed": [], "affected": ">= 2.1.0", "ecosystem": "go"}, {"name": "github.com/xuri/excelize/v2", "fixed": ["2.11.1-0.20260805032953-db93f8d89de7"], "affected": "< 2.11.1-0.20260805032953-db93f8d89de7", "ecosystem": "go"}, {"name": "excelize", "fixed": [], "affected": ">= 2.1.0, <= 2.11.0", "ecosystem": "vendor"}, {"name": "github.com/xuri/excelize/v2", "fixed": ["2.11.1-0.20260805032953-db93f8d89de7"], "affected": ">= 2.1.0, < 2.11.1-0.20260805032953-db93f8d89de7", "ecosystem": "go"}]
ecosystems: ["go", "vendor"]
tags: ["cwe-190", "cwe-681", "excelize", "go", "package", "spreadsheet-upload"]
not_affected_if: ["trusted-files-only"]
published_at: "2026-10-07T19:17:35+00:00"
first_seen_at: "2026-11-06T00:09:03+00:00"
updated_at: "2026-11-06T00:19:08+00:00"
sources: [{"source": "govuln", "url": "https://pkg.go.dev/vuln/GO-2026-6679", "first_seen_at": "2026-11-06T00:09:03+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-107224", "first_seen_at": "2026-11-06T00:19:01+00:00"}, {"source": "ghsa", "url": "https://github.com/advisories/GHSA-fw94-4wwp-w8pw", "first_seen_at": "2026-11-06T00:19:08+00:00"}]
exploit_sources: [{"type": "epss", "score": 0.00306, "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-107224"}]
---

# Excelize: A Zip64 uncompressed-size of 2^63 panics OpenFile/OpenReader

Link for people: https://infrastack.ai/live/CVE-2026-107224 (this .md is for agents)

A crafted XLSX file with a Zip64 uncompressed size of 2^63 makes the Excelize Go library panic when opened with OpenFile or OpenReader. The likely result is a crash of the program that parses the file, a denial of service; the sources do not say whether code execution or data exposure is possible.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- go:github.com/360EntSecGroup-Skylar/excelize/v2 affected >= 2.1.0 fixed no fixed version listed
- go:github.com/xuri/excelize/v2 affected < 2.11.1-0.20260805032953-db93f8d89de7 fixed 2.11.1-0.20260805032953-db93f8d89de7
- vendor:excelize affected >= 2.1.0, <= 2.11.0 fixed no fixed version listed
- go:github.com/xuri/excelize/v2 affected >= 2.1.0, < 2.11.1-0.20260805032953-db93f8d89de7 fixed 2.11.1-0.20260805032953-db93f8d89de7

## How to check

- Look for `github.com/xuri/excelize/v2` or `github.com/360EntSecGroup-Skylar/excelize/v2` in `go.mod` and check the version.
- Run `go list -m github.com/xuri/excelize/v2` in the module to see the resolved version.
- Check whether the program opens untrusted XLSX uploads with OpenFile or OpenReader.

## Fix

- `github.com/xuri/excelize/v2` fixed at `2.11.1-0.20260805032953-db93f8d89de7`
- `github.com/360EntSecGroup-Skylar/excelize/v2`: fixed version unknown

## Timeline

- 2026-11-06T00:09:03+00:00 govuln https://pkg.go.dev/vuln/GO-2026-6679
- 2026-11-06T00:19:01+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-107224
- 2026-11-06T00:19:08+00:00 ghsa https://github.com/advisories/GHSA-fw94-4wwp-w8pw

## Documents

- [advisory] Vendor advisory: github.com https://github.com/qax-os/excelize/security/advisories/GHSA-fw94-4wwp-w8pw
- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/qax-os/excelize/security-advisories/GHSA-fw94-4wwp-w8pw
- [fix] Patch: github.com https://github.com/qax-os/excelize/commit/db93f8d89de71589069a54d1b998af02e3c5f7cd
- [fix] Patch: github.com https://github.com/qax-os/excelize/pull/2369

## From the sources

### Summary

A Zip64 uncompressed-size of 2^63 casts to a negative int64 that bypasses the unzip-size guard and reaches make([]byte, 0, negativeCap)

A Zip64 uncompressed-size in the range [2^63, 2^64) casts to a negative `int64` in `zip.File.FileInfo().Size()`, and `ReadZipReader` does signed arithmetic on that value, so the total-decompression guard is bypassed and the negative size reaches `make([]byte, 0, size)` in `readFile`. A 159-byte crafted file panics `OpenFile` and `OpenReader` with `runtime error: makeslice: cap out of range`.

The guard, at `lib.go:44-46` on HEAD `ae2113b`:

```go
fileSize := v.FileInfo().Size()
unzipSize += fileSize
if unzipSize > f.options.UnzipSizeLimit {
	return fileList, worksheets, newUnzipSizeLimitError(f.options.UnzipSizeLimit)
}
```

`FileInfo().Size()` returns `int64(UncompressedSize64)`. `UncompressedSize64` is read from the Zip64 extended-information extra record in the central directory and is fully attacker controlled, so any value with the top bit set arrives as a negative `int64`. `unzipSize` then goes negative and the comparison against `UnzipSizeLimit` (default `1000 << 24`, `templates.go:193`) is false no matter how many such entries the archive contains.

The same negative value also fails the two stream-to-temp checks at `lib.go:53` and `lib.go:64` (`fileSize > f.options.UnzipXMLSizeLimit`), so the entry is not diverted to a temp file and control reaches `readFile`:

```go
// lib.go:150
dat := make([]byte, 0, file.FileInfo().Size())
```

`make` with a negative capacity panics. There is no `recover()` in any non-test file in the library, so the panic leaves the public API and takes down the calling goroutine.

The asymmetry inside this same function is the clearest way to see it: `unzipToTemp` (`lib.go:83`) handles an entry with a plain `io.Copy` and never pre-allocates from the declared size, so it is indifferent to whatever the header claims. Only the pre-allocating branch trusts that number, and it trusts it after a signed comparison that a wrapped value walks straight through.

## Reproduction, executed against HEAD `ae2113b` (go1.26.5)

A 159-byte archive was built with one stored entry named `xl/worksheets/sheet1.xml`: truthful 1-byte local header, central-directory 32-bit uncompressed size set to the `0xFFFFFFFF` Zip64 sentinel, and a Zip64 extra record (tag `0x0001`, data size 8) declaring `UncompressedSize64 = 0x8000000000000000`.

```
entry "xl/worksheets/sheet1.xml" FileInfo().Size() = -9223372036854775808
excelize.OpenReader(bytes.NewReader(raw))  ->  panic: runtime error: makeslice: cap out of range
```

Control, the identical archive with `UncompressedSize64 = 0x7FFFFFFFFFFFFFFF`:

```
entry "xl/worksheets/sheet1.xml" FileInfo().Size() = 9223372036854775807
excelize.OpenReader(bytes.NewReader(raw))  ->  err = "unzip size exceeds the 16777216000 bytes limit"
```

The control isolates the defect to the sign flip rather than to size magnitude: the guard behaves correctly for any positive declared size and fails only once the value wraps. `archive/zip` itself accepts the archive without complaint, so nothing upstream of excelize rejects it.

## What I verified and what I did not

I ran both cases above myself and confirmed each cited line at HEAD. I caught the panic with a deliberate `recover` in my harness so I could print it; nothing in excelize recovers it, which I checked by grepping every non-test `.go` file. I did not separately run the `OpenFile` variant, since `OpenFile` reaches the same `ReadZipReader` loop, and I did not attempt to turn the bypassed size cap into a separate decompression-bomb primitive.

## Attacker model

Any service that opens a spreadsheet it did not produce. The payload is 159 bytes, needs no authentication, and is deterministic.

## Suggested fix

Compare against the limit in unsigned space, and bound the allocation independently rather than trusting the header:

```go
if v.UncompressedSize64 > uint64(f.options.UnzipSizeLimit) {
	return fileList, worksheets, newUnzipSizeLimitError(f.options.UnzipSizeLimit)
}
```

and in `readFile`, reject or clamp a declared size that is negative or larger than the limit before calling `make`. The same unsigned treatment applies to the two `UnzipXMLSizeLimit` comparisons, which currently route a wrapped size away from the safe streaming path and into the pre-allocating one.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
