{"id":"CVE-2026-107225","kind":"vulnerability","aliases":["CVE-2026-107225","GHSA-5h23-36rv-pm65","GO-2026-6674"],"related":[],"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`.","summary":"The Excelize Go library panics in GetStyle when a spreadsheet's styles.xml contains a negative fillId, borderId or fontId. An attacker who supplies a crafted .xlsx file can crash a program that reads it, causing a denial of service.","severity":"medium","cvss":6.5,"epss":0.0027,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/xuri/excelize/v2","fixed":["2.11.1-0.20260731010303-ae2113b410e5"],"affected":">= 2.8.0, < 2.11.1-0.20260731010303-ae2113b410e5","ecosystem":"go"},{"name":"excelize","fixed":[],"affected":">= 2.8.0, <= 2.11.0","ecosystem":"vendor"}],"ecosystems":["go","vendor"],"tags":["cwe-129","cwe-20","excelize","go","package","spreadsheet-parsing"],"not_affected_if":["no-untrusted-xlsx","trusted-input-only"],"how_to_check":"- Check the version of `github.com/xuri/excelize/v2` in `go.mod` or with `go list -m github.com/xuri/excelize/v2`\n- Look for code that opens untrusted .xlsx files with Excelize and calls GetStyle","fix":"- `github.com/xuri/excelize/v2` `2.11.1-0.20260731010303-ae2113b410e5`","status":"open","published_at":"2026-10-07T19:17:35+00:00","first_seen_at":"2026-11-06T00:09:02+00:00","triaged_at":"2026-11-06T00:10:50+00:00","updated_at":"2026-11-06T00:19:08+00:00","category":"package","confidence":0.7,"triage":{"at":"2026-11-06T00:10:50Z","model":"claude-haiku-5-5","output":{"fix":"- `github.com/xuri/excelize/v2` `2.11.1-0.20260731010303-ae2113b410e5`","reach":"unknown","title":"Excelize: crafted styles.xml crashes Go spreadsheet reader with a panic","impact":"low","summary":"The Excelize Go library panics in GetStyle when a spreadsheet's styles.xml contains a negative fillId, borderId or fontId. An attacker who supplies a crafted .xlsx file can crash a program that reads it, causing a denial of service.","audience":"developers","category":"package","nicknames":[],"who_cares":["go","excelize","spreadsheet-parsing"],"confidence":0.7,"fix_status":"available","needs_auth":"unknown","worst_case":"Program panics and crashes, causing denial of service","analyst_note":"","how_to_check":"- Check the version of `github.com/xuri/excelize/v2` in `go.mod` or with `go list -m github.com/xuri/excelize/v2`\n- Look for code that opens untrusted .xlsx files with Excelize and calls GetStyle","plain_action":"","needs_analyst":false,"not_affected_if":["trusted-input-only","no-untrusted-xlsx"],"who_is_affected":"Any Go service that parses .xlsx files supplied by users or other untrusted sources with Excelize and calls GetStyle is exposed to a crash. Programs that only write spreadsheets they generate, or that handle only trusted files, are unlikely to be affected. unknown whether other Excelize functions are affected.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"low"},"cost_usd":0.000712,"input_tokens":4269,"output_tokens":570,"severity_before":"unknown"},"analyst_status":null,"analyst_note":null,"search":"'-1':114C,116C,119C,385C,401C,409C,411C,419C,421C,429C '-107225':15A,26A '-129':32B '-20':34B '-2026':14A,22A,25A '-6674':23A '/xuri/excelize/v2':29A '0':311C,395C,577C '1171':226C '1185':228C '1677':307C,568C '1683':185C '1686':186C '1689':188C '1783':324C '2366':357C '36rv':19A '48hm':149C '4h8j':150C '500':533C '58fg':151C '5h23':18A 'add':167C,555C 'alreadi':298C,569C 'anywher':521C 'appear':466C 'applic':485C 'attack':61B 'attempt':458C 'attribut':276C,376C 'avail':551C 'bool':240C 'border':85C,205C,286C 'borderid':8A,57B,115C,263C,273C,410C,585C 'bound':106C,171C,229C,558C 'broken':451C 'build':367C 'call':139C,389C 'caller':538C 'carri':112C 'caus':75B 'cell':493C 'cellxf':110C,379C 'chang':592C 'check':102C 'class':145C 'cli':525C 'concept':346C 'condit':98C,223C,289C,564C 'confirm':432C 'contain':53B 'control':393C,431C 'copi':506C 'correct':295C,439C 'corrupt':546C 'craft':65B 'crash':69B,495C 'current':361C 'cve':13A,24A 'cwe':31B,33B 'defect':144C 'denial':77B 'differ':160C 'direct':269C 'disclosur':550C 'entri':111C,380C 'err':399C 'error':404C,414C,424C 'excel':1A,35B,42B,127C,373C,523C 'excelize':30A 'execut':347C 'exit':528C 'extractfont':219C 'extractstylecondfunc':194C,204C,214C,594C 'f.extractborders':208C 'f.extractfills':198C 'f98df08':351C 'file':67B,161C,283C,438C 'file.getstyle':81C 'fill':84C,195C,234C 'fillid':7A,56B,113C,262C,272C,400C,464C 'fine':473C 'first':457C 'fix':165C,554C 'fixtur':452C 'font':87C,215C,288C 'fontid':10A,59B,118C,265C,275C,420C,587C 'func':235C 'function':302C 'gate':99C 'getcellstyl':390C,500C 'getstyl':2A,47B,135C,183C,392C,496C 'ghsa':17A,148C 'ghsa-48hm-4h8j-58fg':147C 'ghsa-5h23-36rv-pm65':16A 'github.com':28A 'github.com/xuri/excelize/v2':27A 'go':21A,36B,43B,189C,233C,308C,575C 'guard':331C 'har':366C,434C,461C 'histor':364C 'id':338C,447C 'idx':192C,310C,316C,320C 'impact':483C 'index':82C,124C,157C,328C,405C,415C,425C,574C 'inform':549C 'insid':381C 'instal':539C 'int':267C 'lack':176C 'lead':340C 'leav':134C 'len':252C,255C,314C,579C 'librari':44B 'line':184C,225C,304C,567C,591C 'lookup':101C 'lower':170C,557C 'master':349C 'match':565C 'memori':545C 'mention':454C 'merg':355C 'middlewar':543C 'miss':169C,481C 'negat':6A,55B,122C,153C,260C,446C 'newinvalidstyleid':319C 'nil':243C,245C,248C,250C,313C 'normal':369C 'ok':396C 'one':163C,375C,590C 'one-lin':589C 'open':487C 'ordinari':515C 'packag':37B 'panic':3A,45B,126C,133C,402C,412C,422C,443C 'pars':40B 'patch':462C 'path':509C,517C 'pattern':296C 'per':534C 'pm65':20A 'process':140C 'program':71B 'proof':344C 'rang':408C,418C,428C 'rather':362C,448C 'reach':120C,498C 'read':73B,435C,492C,516C 'recov':130C,520C 'recoveri':542C 'render':508C 'reopen':386C 'replac':478C 'request':535C 'return':241C,317C,532C 'rewrit':374C 'runtim':403C,413C,423C 's.borders.border':209C 's.cellxfs':312C 's.cellxfs.xf':191C,315C 's.fills':249C 's.fills.fill':199C,253C,580C 's.fonts.font':220C 'satisfi':257C 'say':284C 'servic':79B,531C 'shape':293C 'share':155C 'shared-str':154C 'show':468C 'side':334C 'simpli':480C 'sit':512C 'site':174C 'six':303C 'slice':123C 'spreadsheet':39B,50B 'spreadsheet-pars':38B 'still':175C 'straight':92C 'string':156C 'style':202C,212C,318C,327C,370C,397C,494C,505C,573C 'style-copi':504C 'style.font':218C 'styles.go':181C 'styles.xml':12A,52B 'suggest':553C 'summari':80C 'suppli':63B 'tabl':88C 'take':137C 'taken':91C 'three':173C,337C,442C,563C,588C 'top':232C 'true':398C 'two':471C 'unless':536C 'unmarshal':268C 'untrust':489C 'upper':105C 'valid':437C 'valu':90C,261C,279C 'whatev':281C 'whose':109C 'workbook':108C,371C 'worker':527C 'worth':453C 'xf':190C,196C,206C,216C,236C 'xf.applyfill':242C,244C,246C 'xf.borderid':210C 'xf.fillid':200C,247C,251C,254C,576C,578C 'xf.fontid':221C 'xl/styles.xml':95C 'xlsx':66B,490C 'xlsxstylesheet':239C 'xlsxxf':237C 'zip':383C","nicknames":[],"mitigations":[],"who_affected":"Any Go service that parses .xlsx files supplied by users or other untrusted sources with Excelize and calls GetStyle is exposed to a crash. Programs that only write spreadsheets they generate, or that handle only trusted files, are unlikely to be affected. unknown whether other Excelize functions are affected.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["excelize","githubcomxuriexcelizev2"],"ptokens":["com","excelize","github","v2","xuri"],"labels":["cwe129","cwe20","excelize","go","medium","package","spreadsheetparsing","vendor"],"campaign_type":null,"first_public_at":"2026-07-31T01:05:45+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"govuln","external_id":"GO-2026-6674","url":"https://pkg.go.dev/vuln/GO-2026-6674","severity":null,"published_at":"2026-11-05T22:53:46+00:00","modified_at":"2026-11-05T22:53:46+00:00","first_seen_at":"2026-11-06T00:09:02+00:00","fetched_at":"2026-11-06T00:09:02+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-107225","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-107225","severity":null,"published_at":"2026-10-07T19:17:35+00:00","modified_at":"2026-10-13T13:08:51+00:00","first_seen_at":"2026-11-06T00:19:01+00:00","fetched_at":"2026-11-06T00:19:01+00:00","note":null},{"source":"ghsa","external_id":"GHSA-5h23-36rv-pm65","url":"https://github.com/advisories/GHSA-5h23-36rv-pm65","severity":"medium","published_at":"2026-10-07T20:22:39+00:00","modified_at":"2026-10-07T20:22:41+00:00","first_seen_at":"2026-11-06T00:19:08+00:00","fetched_at":"2026-11-06T00:19:08+00:00","note":null}],"documents":[{"id":81842,"url":"https://github.com/qax-os/excelize/commit/ae2113b410e51f6a141c396a59eda8c42b91bc22","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":81843,"url":"https://github.com/qax-os/excelize/pull/2367","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":81844,"url":"https://github.com/qax-os/excelize/security/advisories/GHSA-5h23-36rv-pm65","title":"Vendor advisory: github.com","kind":"vendor","source":"nvd","publisher":"vendor","text":null,"published_at":"2026-07-31T01:05:45+00:00","relation":"advisory","confidence":null,"note":"Vendor advisory: github.com"},{"id":81898,"url":"https://api.github.com/repos/qax-os/excelize/security-advisories/GHSA-5h23-36rv-pm65","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"}],"exploit_sources":[{"type":"epss","score":0.0027,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-107225"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}