CVE-2026-40296: Phpoffice/phpspreadsheet Cross-Site Scripting Vulnerability
MODERATECVE-2026-40296 is a MODERATE (CVSS 5.4) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting Phpoffice/phpspreadsheet libraries in the Packagist ecosystem. The issue involves phpspreadsheet has xss via number format code with @ text placeholder bypasses htmlspecialchars in html writer. Remediation is available by upgrading affected packages to version 5.7.0, 3.10.5, 2.4.5, 2.1.16, 1.30.4.
CVE-2026-40296 at a Glance
Security Impact
CVSS: 5.4
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N).
How to Fix CVE-2026-40296
To remediate this vulnerability, upgrade all affected Phpoffice/phpspreadsheet dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade phpoffice/phpspreadsheet to 5.7.0
Upgrade phpoffice/phpspreadsheet to 3.10.5
Upgrade phpoffice/phpspreadsheet to 2.4.5
Upgrade phpoffice/phpspreadsheet to 2.1.16
Upgrade phpoffice/phpspreadsheet to 1.30.4
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for phpoffice/phpspreadsheet
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-40296, inspect your project dependencies for Phpoffice/phpspreadsheet:
inspect dependencies for phpoffice/phpspreadsheet
Analyze the terminal output to locate any version of Phpoffice/phpspreadsheet in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.7.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In Phpoffice/phpspreadsheet, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-40296 Affected Phpoffice/phpspreadsheet Versions
5 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
phpoffice/phpspreadsheet
|
Packagist |
≥ 4.0.0
< 5.7.0
|
5.7.0
|
|
phpoffice/phpspreadsheet
|
Packagist |
≥ 3.3.0
< 3.10.5
|
3.10.5
|
|
phpoffice/phpspreadsheet
|
Packagist |
≥ 2.2.0
< 2.4.5
|
2.4.5
|
|
phpoffice/phpspreadsheet
|
Packagist |
≥ 2.0.0
< 2.1.16
|
2.1.16
|
|
phpoffice/phpspreadsheet
|
Packagist |
< 1.30.4
|
1.30.4
|
References
Frequently Asked Questions
What is CVE-2026-40296?
Which Phpoffice/phpspreadsheet versions are affected?
What version fixes CVE-2026-40296?
Is phpoffice/phpspreadsheet affected?
What is the CVSS score of CVE-2026-40296?
What CWE is associated with CVE-2026-40296?
How can I check Packagist dependencies for CVE-2026-40296?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hrmw-qprp-wgmc",
"modified" : "2026-05-08T19:32:34Z",
"published" : "2026-04-28T22:57:13Z",
"aliases" : [ "CVE-2026-40296" ],
"summary" : "PhpSpreadsheet has XSS via number format code with @ text placeholder bypasses htmlspecialchars in HTML writer",
"details" : "It was discovered that there is a way to bypass HTML escaping in the HTML writer using custom number format codes.\n\n## The Problem\n\nIn `Writer/Html.php` around line 1592, the code checks if the formatted cell data equals the original data to decide whether to apply `htmlspecialchars()`:\n\n```php\nif ($cellData === $origData) {\n $cellData = htmlspecialchars($cellData, ...);\n}\n```\n\nWhen a cell has a custom number format containing `@` (text placeholder) with any additional literal characters, the formatter replaces `@` with the cell value and adds the extra characters. This makes `$cellData !== $origData`, so `htmlspecialchars()` is **skipped entirely**.\n\nEven a single trailing space in the format (`@ `) is enough to bypass the escape.\n\n## Proof of Concept\n\n```php\nuse PhpOffice\\PhpSpreadsheet\\Spreadsheet;\nuse PhpOffice\\PhpSpreadsheet\\Writer\\Html;\nuse PhpOffice\\PhpSpreadsheet\\Cell\\DataType;\n\n$spreadsheet = new Spreadsheet();\n$sheet = $spreadsheet->getActiveSheet();\n\n// XSS payload with malicious number format\n$sheet->setCellValueExplicit('A1', '<img src=x onerror=alert(document.cookie)>', DataType::TYPE_STRING);\n$sheet->getStyle('A1')->getNumberFormat()->setFormatCode('. @');\n\n$writer = new Html($spreadsheet);\n$writer->save('output.html');\n```\n\nThe generated HTML contains:\n```html\n<td>. <img src=x onerror=alert(document.cookie)></td>\n```\n\nThe XSS payload is **completely unescaped**.\n\n## Tested Bypass Formats\n\n| Format Code | Result | Escaped? |\n|---|---|---|\n| `General` (default) | Original value | YES (safe) |\n| `. @` | `. ` + value | **NO (XSS!)** |\n| `@ ` (trailing space) | value + ` ` | **NO (XSS!)** |\n| `x@` | `x` + value | **NO (XSS!)** |\n\nThis was tested with PhpSpreadsheet 4.5.0 and confirmed the XSS executes in the browser.\n\n## Impact\n\nAny application that:\n1. Accepts uploaded XLSX files from users\n2. Converts them to HTML using PhpSpreadsheet's HTML writer\n3. Displays the HTML to other users\n\n...is vulnerable to stored XSS. The attacker embeds the payload in a cell value and sets a custom number format in the XLSX file's `xl/styles.xml`.\n\n## Suggested Fix\n\nAlways apply `htmlspecialchars()` regardless of whether formatting changed the value:\n\n```php\n// Instead of conditional escaping:\n$cellData = htmlspecialchars($cellData, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');\n```\n\nOr escape AFTER formatting, not conditionally based on equality.\n\n## Reporter\nKeyvan Hardani",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "4.0.0"
}, {
"fixed" : "5.7.0"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 5.6.0"
}
}, {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "3.3.0"
}, {
"fixed" : "3.10.5"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 3.10.4"
}
}, {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "2.2.0"
}, {
"fixed" : "2.4.5"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 2.4.4"
}
}, {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "2.0.0"
}, {
"fixed" : "2.1.16"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 2.1.15"
}
}, {
"package" : {
"ecosystem" : "Packagist",
"name" : "phpoffice/phpspreadsheet"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.30.4"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 1.30.3"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-hrmw-qprp-wgmc"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-40296"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/PHPOffice/PhpSpreadsheet"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-79" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-04-28T22:57:13Z",
"nvd_published_at" : "2026-05-06T22:16:25Z"
}
}