{"id":"CVE-2026-107223","kind":"vulnerability","aliases":["CVE-2026-107223","GHSA-fq3v-74gv-27gm","GO-2026-6678"],"related":[],"title":"Excelize: Unbounded <col max> attribute is loaded with no MaxColumns check and expanded per-column by flatCols(), so any column mutator hangs or OOMs the process","description":"### Summary\n\nThe `max` attribute of a `<col>` element in `xl/worksheets/sheetN.xml` is read verbatim on open with no check against `MaxColumns`, and `flatCols()` then walks `Min..Max` doing a `deepcopy.Copy` and `append` per iteration. Executed: `max=\"300000\"`, only about 18x past the real 16384 limit, cost 46.2 s of CPU and 122 MB on a single `SetColWidth` call, and the growth is linear in `max` up to 2^31-1.\n\nConfirmed at `ae2113b` (HEAD at the time of audit).\n\n### Details\n\n`col.go:547`, `flatCols()`, with the two loops at `:549` and `:564`:\n\n```go\nfor i := col.Min; i <= col.Max; i++ {\n...\nfor i := column.Min; i <= column.Max; i++ { ... fc = append(fc, deepcopy.Copy(...)) }\n```\n\n`column.Min` and `column.Max` are plain int attributes on `xmlWorksheet.go:279-280`, bound straight from the file. `MaxColumns` is 16384 (`templates.go:176`) and nothing in the parse path compares against it.\n\nThe contrast that makes this an oversight rather than a design decision is inside the callers themselves. `SetColWidth` validates its own column argument through `parseColRange`, which clamps to `MaxColumns`. But `flatCols` iterates `ws.Cols.Col`, the file-loaded slice, so the caller's validated argument never constrains the loop. The crafted column expands no matter which column the caller touches.\n\nThe row axis has the guard this axis is missing: GHSA-h69g-9hx6-f3v4 capped `checkSheet`'s row allocation at `TotalRows`, and GHSA-q5j5-6p94-4gwc capped streaming `Rows.Next`. There is no column-axis equivalent.\n\nPublic entry points that flatten the existing columns: `SetColWidth` (`col.go:498` into `:534`), `SetColStyle` (`col.go:431` into `:477`), `SetColVisible` (`col.go:282` into `:313`), `SetColOutlineLevel` (`col.go:376` into `:407`).\n\n### PoC\n\nExecuted in-package: crafted a real xlsx, rezipped with an injected `<cols>` block before `<sheetData>`, opened it with `OpenReader`, then called `SetColWidth`.\n\n```xml\n<cols><col min=\"1\" max=\"2147483647\" width=\"9\"/></cols>\n```\n\n```go\nf.SetColWidth(\"Sheet1\", \"A\", \"A\", 12)\n```\n\nMeasured:\n\n- `max=\"300000\"`: 46.2 s CPU, 122 MB allocated, `ws.Cols.Col` grew to 300000 entries, from one call.\n- `max=\"5000000\"`: did not complete in 110 s, killed by the test timeout.\n\nGrowth is linear in `max`. At `max=2147483647` that is roughly two billion `xlsxCol` allocations, which is hundreds of gigabytes and in practice a permanent hang ending in OOM.\n\n### Impact\n\nAny service that opens an untrusted spreadsheet and calls a column mutator. The input is a few dozen bytes of XML inside an otherwise ordinary workbook, no authentication is involved beyond whatever gates the upload, and the process is either wedged for minutes or killed by the OOM reaper. Availability only; no read or write primitive here.\n\n### Suggested fix\n\nValidate `col.Min` and `col.Max` against `MinColumns`/`MaxColumns` when parsing the `<col>` element, or at the top of `flatCols`, returning `ErrColumnNumber` for out-of-range values. That mirrors what `checkRowNum` already does on the row axis.\n\n### Why this is not GHSA-h69g-9hx6-f3v4 or GHSA-q5j5-6p94-4gwc\n\nBoth of those are row-axis allocation bounds, and both fixes cap at `TotalRows`. Neither touched `<col>` parsing or `flatCols`. This is the same weakness class on the column axis, and it is arguably worse than the two that were fixed, because those had a cap that was bypassed while this one has no cap at all.","summary":"A crafted spreadsheet with an oversized column max value makes the Go excelize library's column functions loop without a column limit. An attacker can cause a hang or out-of-memory crash in a service that opens and edits untrusted workbooks.","severity":"high","cvss":7.5,"epss":0.0037,"exploited":false,"exploited_at":null,"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.20260807015645-a54c578af309"],"affected":"< 2.11.1-0.20260807015645-a54c578af309","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.20260807015645-a54c578af309"],"affected":">= 2.1.0, < 2.11.1-0.20260807015645-a54c578af309","ecosystem":"go"}],"ecosystems":["go","vendor"],"tags":["backend","cwe-789","document-processing","excelize","go","package"],"not_affected_if":["no-column-mutators","trusted-input-only"],"how_to_check":"- Check the excelize version in `go.mod` for `github.com/xuri/excelize/v2`.\n- Check whether the code opens untrusted .xlsx files and calls column functions such as SetColWidth or SetColVisible.\n- The fork path github.com/360EntSecGroup-Skylar/excelize/v2 may also be in use; check `go.mod` for it.","fix":"- `github.com/xuri/excelize/v2` fixed in `2.11.1-0.20260807015645-a54c578af309`.\n- Fixed version for the 360EntSecGroup-Skylar fork: unknown.","status":"open","published_at":"2026-10-07T19:17:34+00:00","first_seen_at":"2026-11-06T00:09:03+00:00","triaged_at":"2026-11-06T00:10:43+00:00","updated_at":"2026-11-06T00:19:08+00:00","category":"package","confidence":0.6,"triage":{"at":"2026-11-06T00:10:43Z","model":"claude-haiku-5-5","output":{"fix":"- `github.com/xuri/excelize/v2` fixed in `2.11.1-0.20260807015645-a54c578af309`.\n- Fixed version for the 360EntSecGroup-Skylar fork: unknown.","reach":"unknown","title":"Excelize: crafted column range makes spreadsheet column edits hang or run out of memory","impact":"medium","summary":"A crafted spreadsheet with an oversized column max value makes the Go excelize library's column functions loop without a column limit. An attacker can cause a hang or out-of-memory crash in a service that opens and edits untrusted workbooks.","audience":"developers","category":"package","nicknames":[],"who_cares":["go","excelize","document-processing","backend"],"confidence":0.6,"fix_status":"available","needs_auth":"unknown","worst_case":"Service hangs or crashes from memory exhaustion while processing a file","analyst_note":"Confirm whether the 360EntSecGroup-Skylar fork path has a fixed version, and check https://pkg.go.dev/vuln/GO-2026-6678 for trigger details.","how_to_check":"- Check the excelize version in `go.mod` for `github.com/xuri/excelize/v2`.\n- Check whether the code opens untrusted .xlsx files and calls column functions such as SetColWidth or SetColVisible.\n- The fork path github.com/360EntSecGroup-Skylar/excelize/v2 may also be in use; check `go.mod` for it.","plain_action":"","needs_analyst":true,"not_affected_if":["trusted-input-only","no-column-mutators"],"who_is_affected":"Applications that parse or edit spreadsheets supplied by users or other untrusted sources with excelize are exposed, because the flaw is triggered by the content of the file. Services that only generate workbooks from trusted data, or that never handle column edits on untrusted input, can likely skip it. The sources do not say whether the fork path is affected by the same range; it is listed with an open-ended affected range and no fixed version.","user_interaction":"unknown","needs_special_setup":"unknown","severity_if_unknown":"medium"},"cost_usd":0.000799,"input_tokens":4313,"output_tokens":735,"severity_before":"unknown"},"analyst_status":null,"analyst_note":null,"search":"'-1':169C '-107223':28A,39A '-2026':27A,35A,38A '-280':216C '-6678':36A '-789':52B '/360entsecgroup-skylar/excelize/v2':42A '/xuri/excelize/v2':45A,49A '110':408C '12':384C '122':151C,391C '16384':143C,224C '18x':139C '2':167C '2147483647':422C '27gm':33A '300000':136C,387C,397C '31':168C '313':351C '407':355C '46.2':146C,388C '477':347C '4gwc':321C,553C '5000000':403C '534':343C '549':187C '564':189C '6p94':320C,552C '74gv':32A '9hx6':307C,546C 'ae2113b':172C 'alloc':313C,393C,429C,561C 'alreadi':533C 'append':131C,204C 'arguabl':587C 'argument':257C,278C 'attack':82B 'attribut':3A,105C,213C 'audit':178C 'authent':472C 'avail':494C 'axi':296C,301C,330C,538C,560C,583C 'backend':50B 'beyond':475C 'billion':427C 'block':369C 'bound':217C,562C 'bypass':602C 'byte':463C 'call':157C,376C,401C,453C 'caller':250C,275C,292C 'cap':309C,322C,566C,599C,608C 'caus':84B 'check':9A,118C 'checkrownum':532C 'checksheet':310C 'clamp':261C 'class':579C 'col.go:282':349C 'col.go:376':353C 'col.go:431':345C 'col.go:498':341C 'col.go:547':180C 'col.max':195C,507C 'col.min':193C,505C 'column':14A,19A,65B,74B,79B,256C,285C,290C,329C,339C,455C,582C 'column-axi':328C 'column.max':201C,209C 'column.min':199C,207C 'compar':232C 'complet':406C 'confirm':170C 'constrain':280C 'contrast':236C 'cost':145C 'cpu':149C,390C 'craft':60B,284C,361C 'crash':92B 'cve':26A,37A 'cwe':51B 'decis':246C 'deepcopy.copy':129C,206C 'design':245C 'detail':179C 'document':54B 'document-process':53B 'dozen':462C 'edit':99B 'either':484C 'element':108C,514C 'end':441C 'entri':333C,398C 'equival':331C 'errcolumnnumb':522C 'excel':1A,56B,71B 'excelize':46A 'execut':134C,357C 'exist':338C 'expand':11A,286C 'f.setcolwidth':380C 'f3v4':308C,547C 'fc':203C,205C 'file':221C,270C 'file-load':269C 'fix':503C,565C,594C 'flatcol':16A,122C,181C,265C,520C,573C 'flatten':336C 'fq3v':31A 'function':75B 'gate':477C 'ghsa':30A,305C,318C,544C,550C 'ghsa-fq3v-74gv-27gm':29A 'ghsa-h69g-9hx6-f3v4':304C,543C 'ghsa-q5j5-6p94-4gwc':317C,549C 'gigabyt':434C 'github.com':41A,44A,48A 'github.com/360entsecgroup-skylar/excelize/v2':40A 'github.com/xuri/excelize/v2':43A,47A 'go':34A,57B,70B,190C,379C 'grew':395C 'growth':160C,415C 'guard':299C 'h69g':306C,545C 'hang':21A,86B,440C 'head':173C 'hundr':432C 'impact':444C 'in-packag':358C 'inject':368C 'input':458C 'insid':248C,466C 'int':212C 'involv':474C 'iter':133C,266C 'kill':410C,489C 'librari':72B 'limit':80B,144C 'linear':162C,417C 'load':5A,271C 'loop':76B,185C,282C 'make':68B,238C 'matter':288C 'max':66B,104C,126C,135C,164C,386C,402C,419C,421C 'maxcolumn':8A,120C,222C,263C,510C 'mb':152C,392C 'measur':385C 'memori':91B 'min':125C 'mincolumn':509C 'minut':487C 'mirror':530C 'miss':303C 'mutat':20A,456C 'neither':569C 'never':279C 'noth':227C 'one':400C,605C 'oom':23A,443C,492C 'open':97B,115C,371C,448C 'openread':374C 'ordinari':469C 'otherwis':468C 'out-of-memori':88B 'out-of-rang':524C 'overs':64B 'oversight':241C 'packag':58B,360C 'pars':230C,512C,571C 'parsecolrang':259C 'past':140C 'path':231C 'per':13A,132C 'per-column':12A 'perman':439C 'plain':211C 'poc':356C 'point':334C 'practic':437C 'primit':500C 'process':25A,55B,482C 'public':332C 'q5j5':319C,551C 'rang':527C 'rather':242C 'read':112C,497C 'real':142C,363C 'reaper':493C 'return':521C 'rezip':365C 'rough':425C 'row':295C,312C,537C,559C 'row-axi':558C 'rows.next':324C 'servic':95B,446C 'setcoloutlinelevel':352C 'setcolstyl':344C 'setcolvis':348C 'setcolwidth':156C,252C,340C,377C 'sheet1':381C 'singl':155C 'slice':272C 'spreadsheet':61B,451C 'straight':218C 'stream':323C 'suggest':502C 'summari':102C 'templates.go:176':225C 'test':413C 'time':176C 'timeout':414C 'top':518C 'totalrow':315C,568C 'touch':293C,570C 'two':184C,426C,591C 'unbound':2A 'untrust':100B,450C 'upload':479C 'valid':253C,277C,504C 'valu':67B,528C 'verbatim':113C 'walk':124C 'weak':578C 'wedg':485C 'whatev':476C 'without':77B 'workbook':101B,470C 'wors':588C 'write':499C 'ws.cols.col':267C,394C 'xl/worksheets/sheetn.xml':110C 'xlsx':364C 'xlsxcol':428C 'xml':378C,465C 'xmlworksheet.go:279':215C","nicknames":[],"mitigations":[],"who_affected":"Applications that parse or edit spreadsheets supplied by users or other untrusted sources with excelize are exposed, because the flaw is triggered by the content of the file. Services that only generate workbooks from trusted data, or that never handle column edits on untrusted input, can likely skip it. The sources do not say whether the fork path is affected by the same range; it is listed with an open-ended affected range and no fixed version.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["excelize","githubcom360entsecgroupskylarexcelizev2","githubcomxuriexcelizev2"],"ptokens":["360entsecgroup","com","excelize","github","skylar","v2","xuri"],"labels":["backend","cwe789","documentprocessing","excelize","go","high","package","vendor"],"campaign_type":null,"first_public_at":"2026-08-07T01:07:55+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"govuln","external_id":"GO-2026-6678","url":"https://pkg.go.dev/vuln/GO-2026-6678","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:03+00:00","fetched_at":"2026-11-06T00:09:03+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-107223","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-107223","severity":null,"published_at":"2026-10-07T19:17:34+00:00","modified_at":"2026-10-13T14:18:46+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-fq3v-74gv-27gm","url":"https://github.com/advisories/GHSA-fq3v-74gv-27gm","severity":"high","published_at":"2026-10-07T20:22:49+00:00","modified_at":"2026-10-07T20:22:52+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":81830,"url":"https://github.com/qax-os/excelize/commit/a54c578af309fa81f448143ed2b7a91192cc58a7","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":81831,"url":"https://github.com/qax-os/excelize/pull/2370","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":81832,"url":"https://github.com/qax-os/excelize/security/advisories/GHSA-fq3v-74gv-27gm","title":"Vendor advisory: github.com","kind":"vendor","source":"nvd","publisher":"vendor","text":null,"published_at":"2026-08-07T01:07:55+00:00","relation":"advisory","confidence":null,"note":"Vendor advisory: github.com"},{"id":81914,"url":"https://api.github.com/repos/qax-os/excelize/security-advisories/GHSA-fq3v-74gv-27gm","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.0037,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-107223"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}