CVE-2026-107225: Excelize GetStyle Panics On A Negative Vulnerability
MODERATECVE-2026-107225 is a MODERATE (CVSS 6.5) getstyle panics on a negative vulnerability (CWE-20: Improper Input Validation) affecting Excelize libraries in the Go ecosystem. The issue involves getstyle panics on a negative fillid, borderid or fontid in styles.xml. Remediation is available by upgrading affected packages to version 2.11.1-0.20260731010303-ae2113b410e5.
CVE-2026-107225 at a Glance
Security Impact
CVSS: 6.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H).
How to Fix CVE-2026-107225
To remediate this vulnerability, upgrade all affected Excelize dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade github.com/xuri/excelize/v2 to 2.11.1-0.20260731010303-ae2113b410e5
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for github.com/xuri/excelize/v2
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-107225, inspect your project dependencies for Excelize:
inspect dependencies for github.com/xuri/excelize/v2
Analyze the terminal output to locate any version of Excelize in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.11.1-0.20260731010303-ae2113b410e5), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-20: Improper Input Validation, where the product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. In Excelize, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-107225 Affected Excelize Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
github.com/xuri/excelize/v2
|
Go |
≥ 2.8.0
< 2.11.1-0.20260731010303-ae2113b410e5
|
2.11.1-0.20260731010303-ae2113b410e5
|
References
Frequently Asked Questions
What is CVE-2026-107225?
Which Excelize versions are affected?
What version fixes CVE-2026-107225?
Is github.com/xuri/excelize/v2 affected?
What is the CVSS score of CVE-2026-107225?
What CWE is associated with CVE-2026-107225?
How can I check Go dependencies for CVE-2026-107225?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-5h23-36rv-pm65",
"modified" : "2026-10-07T20:22:39Z",
"published" : "2026-10-07T20:22:39Z",
"aliases" : [ "CVE-2026-107225" ],
"summary" : "Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml",
"details" : "## Summary\n\n`File.GetStyle` indexes the fill, border and font tables with values taken straight out of `xl/styles.xml`, and the conditions gating those lookups check only the upper bound. A workbook whose `cellXfs` entry carries `fillId=\"-1\"`, `borderId=\"-1\"` or `fontId=\"-1\"` reaches a negative slice index and panics. excelize has no `recover()`, so the panic leaves `GetStyle` and takes the calling process with it.\n\nSame defect class as GHSA-48hm-4h8j-58fg, the negative shared-string index, in a different file. That one was fixed by adding the missing lower bound; these three sites still lack it.\n\n## Where it is\n\n`styles.go`, in `GetStyle`, lines 1683, 1686 and 1689:\n\n```go\nxf := s.CellXfs.Xf[idx]\nif extractStyleCondFuncs[\"fill\"](xf, s) {\n f.extractFills(s.Fills.Fill[*xf.FillID], s, style)\n}\nif extractStyleCondFuncs[\"border\"](xf, s) {\n f.extractBorders(s.Borders.Border[*xf.BorderID], s, style)\n}\nif extractStyleCondFuncs[\"font\"](xf, s) {\n style.Font = extractFont(s.Fonts.Font[*xf.FontID])\n}\n```\n\nThe conditions, at lines 1171 to 1185, bound only the top:\n\n```go\n\"fill\": func(xf xlsxXf, s *xlsxStyleSheet) bool {\n return (xf.ApplyFill == nil || (xf.ApplyFill != nil && *xf.ApplyFill)) &&\n xf.FillID != nil && s.Fills != nil &&\n *xf.FillID < len(s.Fills.Fill)\n},\n```\n\n`*xf.FillID < len(...)` is satisfied by any negative value. `FillID`, `BorderID` and `FontID` are `*int` unmarshalled directly from the `fillId`, `borderId` and `fontId` attributes, so the value is whatever the file says. The border and font conditions have the same shape.\n\nThe correct pattern is already in the same function, six lines above at 1677:\n\n```go\nif idx < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= idx {\n return style, newInvalidStyleID(idx)\n}\n```\n\nand again at 1783. So the style index itself is guarded on both sides while the three ids it leads to are not.\n\n## Proof of concept\n\nExecuted against `master` at `f98df08`, which is the merge of #2366, so this is current rather than historical. The harness builds a normal styled workbook with excelize, rewrites one attribute of the `cellXfs` entry inside the zip to `-1`, reopens it and calls `GetCellStyle` then `GetStyle`:\n\n```\ncontrol (all >= 0) ok style=true err=<nil>\nfillId=-1 PANIC: runtime error: index out of range [-1]\nborderId=-1 PANIC: runtime error: index out of range [-1]\nfontId=-1 PANIC: runtime error: index out of range [-1]\n```\n\nThe control confirms the harness reads a valid file correctly, so the three panics are the negative ids rather than a broken fixture. Worth mentioning because my first attempt at this harness patched only `fillId` and appeared to show the other two were fine; they are not, the replacement had simply missed them.\n\n## Impact\n\nAny application that opens an untrusted `.xlsx` and reads cell styling crashes. `GetStyle` is reached through `GetCellStyle` and from the style-copying and rendering paths, so it sits on the ordinary read path. With no `recover()` anywhere in excelize, a CLI or worker exits and a service returns 500 per request unless the caller installed its own recovery middleware.\n\nNo memory corruption and no information disclosure. Availability only.\n\n## Suggested fix\n\nAdd the lower bound to each of the three conditions, matching what line 1677 already does for the style index:\n\n```go\n*xf.FillID >= 0 && *xf.FillID < len(s.Fills.Fill)\n```\n\nand the same for `BorderID` and `FontID`. Three one-line changes in `extractStyleCondFuncs`.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/xuri/excelize/v2"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "2.8.0"
}, {
"fixed" : "2.11.1-0.20260731010303-ae2113b410e5"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65"
}, {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/pull/2367"
}, {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/qax-os/excelize"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-129", "CWE-20" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-10-07T20:22:39Z",
"nvd_published_at" : null
}
}