Overview
CVE-2026-78207 is a critical-severity prototype pollution vulnerability (CVSS 9.4) affecting exceljs-hardened before version 5.0.0. The flaw exists in the library's internal deepMerge helper function, which fails to reject reserved JavaScript keys — specifically __proto__, constructor, and prototype — when merging note objects parsed from XLSX cell data.
A remote attacker who can supply a crafted spreadsheet file containing a malicious __proto__ property in a cell note can corrupt Object.prototype globally within the Node.js process, potentially achieving remote code execution (RCE) or arbitrary property injection that cascades through the application.
Affected Versions
| Package | Affected | Fixed |
|---|---|---|
| exceljs-hardened (npm) | < 5.0.0 | 5.0.0+ |
Vulnerability Details
Root Cause
The deepMerge utility in exceljs-hardened recursively merges parsed JSON data from XLSX cell notes into JavaScript objects. Before v5.0.0, it did not sanitize or block keys like __proto__, constructor, or prototype. When an attacker provides an XLSX file with a cell note containing:
{
"__proto__": {
"isAdmin": true,
"polluted": "attacker-controlled-value"
}
}The merge operation walks the __proto__ key directly onto Object.prototype, affecting all objects in the process from that point forward.
Impact
Successful exploitation allows an attacker to:
- Corrupt
Object.prototype— all subsequently created objects inherit attacker-controlled properties - Achieve RCE — in environments where polluted properties influence code execution paths (e.g., template engines,
child_process.spawnoption resolution, configuration loading) - Bypass authorization logic — applications checking properties like
isAdminorhasPermissionmay evaluate totruefor all users after pollution
This vulnerability is particularly dangerous in server-side Node.js environments that parse user-supplied Excel files, such as document upload services, financial data processors, and ETL pipelines.
Attack Vector
The attack requires the victim application to:
- Accept an XLSX file as user input
- Parse the file using exceljs-hardened (e.g., via
workbook.xlsx.load()) - Process cell notes (the vulnerable code path)
No authentication is required if the XLSX upload endpoint is publicly accessible, making this exploitable by unauthenticated remote attackers in many real-world deployments.
Proof of Concept
The general technique involves crafting an XLSX file where a cell note's XML content deserializes to an object with a __proto__ key. Conceptually:
// Vulnerable code path (simplified, pre-patch)
function deepMerge(target, source) {
for (const key of Object.keys(source)) {
// BUG: no check for __proto__, constructor, prototype
if (typeof source[key] === 'object') {
target[key] = deepMerge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}After pollution, all newly created empty objects inherit attacker properties:
const obj = {};
console.log(obj.isAdmin); // true (after pollution)Remediation
Upgrade immediately to exceljs-hardened v5.0.0 or later.
The patch introduces key sanitization in deepMerge, explicitly rejecting __proto__, constructor, and prototype keys before any merge operation.
# npm
npm update exceljs-hardened
# yarn
yarn upgrade exceljs-hardened
# pnpm
pnpm update exceljs-hardenedWorkaround (if immediate upgrade is not possible)
If you cannot upgrade immediately, consider:
- Validating XLSX files through a separate sandboxed process before parsing
- Restricting XLSX upload endpoints to authenticated and trusted users only
- Using
Object.freeze(Object.prototype)at application startup (note: this may break other code)
Detection
Search your package.json and package-lock.json for exceljs-hardened:
npm list exceljs-hardenedIf the version is below 5.0.0, upgrade immediately. Audit your application for XLSX parsing code paths that process cell notes.
Timeline
| Date | Event |
|---|---|
| 2026-08-24 | CVE assigned and published to NVD |
| 2026-08-24 | exceljs-hardened v5.0.0 released with fix |