{"id":"CVE-2026-107225","revision":null,"records":[{"revision":"d7b63bc2e559425186f809b49160b952","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-107225","aliases":[],"title":"Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml","description":"Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.GetStyle relies on extractStyleCondFuncs predicates that allow negative FillID, BorderID, and FontID values to reach slice indexing. When a crafted styles.xml supplies a negative fillId, borderId, or fontId and the application reads the style, a negative identifier passes the upper-bound-only predicate and becomes a negative slice index, allowing an attacker to panic while reading cell styling. No fixed version is available as of this review.","state":0,"publishedAt":"2026-10-07T18:55:55.603+00:00","modifiedAt":"2026-10-10T02:42:21.546+00:00","affected":[{"ecosystem":"Vendor product","name":"excelize","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"qax-os\",\n                    \"product\": \"excelize\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 2.8.0, <= 2.11.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65","https://github.com/qax-os/excelize/pull/2367","https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-107225","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-107225","identifiers":["CVE-2026-107225"]},"retained":false,"historicalAffected":[]},{"revision":"9672c678ac0d45da84dfcc7ef5ee7f42","observedAt":"2026-11-09T05:07:32.157+00:00","statement":{"source":"euvd","id":"EUVD-2026-94556","aliases":["CVE-2026-107225","GHSA-5h23-36rv-pm65"],"title":"Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.…","description":"Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.GetStyle relies on extractStyleCondFuncs predicates that allow negative FillID, BorderID, and FontID values to reach slice indexing. When a crafted styles.xml supplies a negative fillId, borderId, or fontId and the application reads the style, a negative identifier passes the upper-bound-only predicate and becomes a negative slice index, allowing an attacker to panic while reading cell styling. No fixed version is available as of this review.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","score":6.5,"severity":null}],"references":["https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65","https://github.com/qax-os/excelize/pull/2367","https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-94556","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 7, 2026, 6:55:55 PM","dateUpdated":"Oct 10, 2026, 2:42:21 AM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-94556","identifiers":["EUVD-2026-94556","CVE-2026-107225","GHSA-5h23-36rv-pm65"]},"retained":false,"historicalAffected":[]},{"revision":"b3b74260aa7d46f68fe3a1b594845887","observedAt":"2026-10-25T04:17:41.466+00:00","statement":{"source":"nvd","id":"CVE-2026-107225","aliases":[],"title":"Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.…","description":"Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.GetStyle relies on extractStyleCondFuncs predicates that allow negative FillID, BorderID, and FontID values to reach slice indexing. When a crafted styles.xml supplies a negative fillId, borderId, or fontId and the application reads the style, a negative identifier passes the upper-bound-only predicate and becomes a negative slice index, allowing an attacker to panic while reading cell styling. No fixed version is available as of this review.","state":0,"publishedAt":"2026-10-07T19:17:35.333+00:00","modifiedAt":"2026-10-13T13:08:51.83+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","score":6.5,"severity":"MEDIUM"}],"references":["https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22","https://github.com/qax-os/excelize/pull/2367","https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-107225","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-20","CWE-129"],"productCriteria":["cpe:2.3:a:excelize:excelize:*:*:*:*:*:go:*:*"],"sourceDates":{"published":"2026-10-07T19:17:35.333","lastModified":"2026-10-13T13:08:51.830"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-107225","identifiers":["CVE-2026-107225"]},"retained":false,"historicalAffected":[]},{"revision":"d74ded90a0094569acda9925020c2be2","observedAt":"2026-11-05T23:38:32.248+00:00","statement":{"source":"osv","id":"GHSA-5h23-36rv-pm65","aliases":["CVE-2026-107225","GO-2026-6674"],"title":"Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml","description":"## 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`.","state":0,"publishedAt":"2026-10-07T20:22:39+00:00","modifiedAt":"2026-11-05T23:26:03.3466934+00:00","affected":[{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"2.8.0"},{"kind":"fixed","version":"2.11.1-0.20260731010303-ae2113b410e5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65","https://github.com/qax-os/excelize/pull/2367","https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22","https://github.com/qax-os/excelize"],"sourceUrl":"https://github.com/advisories/GHSA-5h23-36rv-pm65","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-5h23-36rv-pm65","identifiers":["GHSA-5h23-36rv-pm65","CVE-2026-107225","GO-2026-6674"]},"retained":false,"historicalAffected":[]},{"revision":"a14fe9939462474a8d711a5322a0b74c","observedAt":"2026-11-05T23:38:32.248+00:00","statement":{"source":"osv","id":"GO-2026-6674","aliases":["CVE-2026-107225","GHSA-5h23-36rv-pm65"],"title":"Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml in github.com/xuri/excelize","description":"Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml in github.com/xuri/excelize","state":0,"publishedAt":"2026-11-05T22:53:46+00:00","modifiedAt":"2026-11-05T23:15:11.9012135+00:00","affected":[{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"2.8.0"},{"kind":"fixed","version":"2.11.1-0.20260731010303-ae2113b410e5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65","https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22","https://github.com/qax-os/excelize/pull/2367"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-6674","license":"OSV · upstream source license applies","key":"osv:GO-2026-6674","identifiers":["GO-2026-6674","CVE-2026-107225","GHSA-5h23-36rv-pm65"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-10T08:21:42.9806814+00:00","complete":true,"records":1773,"lastAttemptAt":"2026-11-10T08:21:42.9806814+00:00","lastSuccessAt":"2026-11-10T08:21:49.9716687+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:49.9722477+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-8859cf40d4337e24bd2f258cc7778fbad01bb24c79d057aaf34cf1dfc6f8917d-746bb7faca3153abeb3c809473890793eb06eef475a9980d578ad522a2f91b4d","complete":true,"records":421910,"lastAttemptAt":"2026-11-10T08:21:35.2839989+00:00","lastSuccessAt":"2026-11-10T08:02:16.532381+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:35.2839989+00:00","snapshotPublishedAt":"2026-11-10T07:28:26+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-2e6a476534094233b48f2f0b6da5cf35","complete":true,"records":13689,"lastAttemptAt":"2026-11-10T08:22:41.0210911+00:00","lastSuccessAt":"2026-11-10T06:05:35.03+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:05:35.03+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13742,"expectedIdentifiers":13742,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-10T08:21:42.9806814+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-10T08:21:42.9806814+00:00","lastSuccessAt":"2026-11-10T08:22:06.2444793+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:22:06.2444805+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-78b08a98b3ce49f188246fb2aae3c44d","complete":false,"records":13695,"lastAttemptAt":"2026-11-10T08:22:40.320966+00:00","lastSuccessAt":"2026-11-10T08:22:40.318+00:00","error":"100 identifier lookups failed; latest failures include request timeout. Other identifiers were checked independently.","lastVerifiedAt":"2026-11-10T08:22:40.318+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13642,"expectedIdentifiers":13742,"failedIdentifiers":100},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-cdffb4589856378aa42e8d1bd4a6c74e27a180d8160bf764c44715ef760f0a62","complete":true,"records":9932,"lastAttemptAt":"2026-11-10T08:21:42.331385+00:00","lastSuccessAt":"2026-11-10T00:04:19.8260565+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:42.331385+00:00","snapshotPublishedAt":"2026-11-09T23:48:01+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-26503967ae4379db8f374089589d6e03fb15bd31f820c9e992aaf93840c2236c","complete":true,"records":1971,"lastAttemptAt":"2026-11-10T08:21:38.6986948+00:00","lastSuccessAt":"2026-11-09T21:02:20.472473+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:38.6986948+00:00","snapshotPublishedAt":"2026-11-09T20:18:18+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-94169eec81ea3989aa551092397ff34ae53c104b4a6ce646e1524452e4ab2a39","complete":true,"records":3065,"lastAttemptAt":"2026-11-10T08:21:42.4406049+00:00","lastSuccessAt":"2026-11-10T08:04:45.5923716+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:42.4406049+00:00","snapshotPublishedAt":"2026-11-10T06:48:01+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-4331051b0b2d4ccdbdca3aa196b0be3818a465064a4b18dde4cf39c2250eb490","complete":true,"records":231599,"lastAttemptAt":"2026-11-10T08:21:40.047287+00:00","lastSuccessAt":"2026-11-10T08:04:37.1757322+00:00","error":null,"lastVerifiedAt":"2026-11-10T08:21:40.047287+00:00","snapshotPublishedAt":"2026-11-10T07:33:02+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}