Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
MEDIUM severity

CVE-2026-107225 — excelize

MEDIUMFix: qax-os/excelize@ae2113b

CVE-2026-107225 is a medium-severity (CVSS 6.5) Improper Input Validation vulnerability in github.com/xuri/excelize/v2. A fix is available for github.com/xuri/excelize/v2 — see the affected versions and patch details below.

Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml

Also known asGHSA-5h23-36rv-pm65
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 22, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-107225.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs16th percentile — riskier than 16% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-107225 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹github.com/xuri/excelize/v2

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

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.

Same 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.

Where it is

styles.go, in GetStyle, lines 1683, 1686 and 1689:

xf := s.CellXfs.Xf[idx]
if extractStyleCondFuncs["fill"](xf, s) {
    f.extractFills(s.Fills.Fill[*xf.FillID], s, style)
}
if extractStyleCondFuncs["border"](xf, s) {
    f.extractBorders(s.Borders.Border[*xf.BorderID], s, style)
}
if extractStyleCondFuncs["font"](xf, s) {
    style.Font = extractFont(s.Fonts.Font[*xf.FontID])
}

The conditions, at lines 1171 to 1185, bound only the top:

"fill": func(xf xlsxXf, s *xlsxStyleSheet) bool {
    return (xf.ApplyFill == nil || (xf.ApplyFill != nil && *xf.ApplyFill)) &&
        xf.FillID != nil && s.Fills != nil &&
        *xf.FillID < len(s.Fills.Fill)
},

*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.

The correct pattern is already in the same function, six lines above at 1677:

if idx < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= idx {
    return style, newInvalidStyleID(idx)
}

and again at 1783. So the style index itself is guarded on both sides while the three ids it leads to are not.

Proof of concept

Executed 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:

control (all >= 0)           ok style=true err=<nil>
fillId=-1                    PANIC: runtime error: index out of range [-1]
borderId=-1                  PANIC: runtime error: index out of range [-1]
fontId=-1                    PANIC: runtime error: index out of range [-1]

The 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.

Impact

Any 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.

No memory corruption and no information disclosure. Availability only.

Suggested fix

Add the lower bound to each of the three conditions, matching what line 1677 already does for the style index:

*xf.FillID >= 0 && *xf.FillID < len(s.Fills.Fill)

and the same for BorderID and FontID. Three one-line changes in extractStyleCondFuncs.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/xuri/excelize/v2≥ 2.8.0&&< 2.11.1-0.20260731010303-ae2113b410e52.11.1-0.20260731010303-ae2113b410e5go get github.com/xuri/excelize/v2@v2.11.1-0.20260731010303-ae2113b410e5

Affected Products

1 product · 1 configurations
Application
excelizeexcelize
≥ 2.8.0 && ≤ 2.11.0
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/xuri/excelize/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/xuri/excelize/v2 to 2.11.1-0.20260731010303-ae2113b410e5 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-107225 is resolved across your whole dependency graph.

  3. Workarounds

    Stop feeding it untrusted input: reject or quarantine files and payloads from unverified sources until you can upgrade, restrict accepted formats to the ones you actually need, and run the parsing or decoding step in a least-privileged sandbox or short-lived worker so a crash or corrupted read cannot reach the rest of the process.

Frequently Asked Questions

## Summary `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. Same 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
O3 Security · Impact-Aware SCA

Is CVE-2026-107225 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-107225: excelize Info Disclosure — Fix Available