node-xlsx is a convenience wrapper around the SheetJS xlsx engine, which means its security depends almost entirely on the version of SheetJS it pulls in. If you treat node-xlsx as a standalone parser and ignore what sits underneath it, you inherit every prototype pollution and ReDoS issue that has shipped in SheetJS Community Edition. This guide walks through what node-xlsx actually does, where the risk lives, and how to parse spreadsheets from untrusted sources without handing an attacker a foothold.
What node-xlsx Actually Is
The node-xlsx package is a small TypeScript wrapper that exposes a friendlier API for reading and writing Excel files in Node.js. Under the hood it calls into SheetJS xlsx to do the real parsing and serialization. The README is honest about this: node-xlsx relies on the SheetJS xlsx module to parse and build spreadsheets. The latest published version at the time of writing is 0.24.0, and there are hundreds of downstream projects depending on it.
Because it is a wrapper, node-xlsx inherits SheetJS's memory model (it loads the whole workbook into memory, so it is a poor fit for very large files) and, more importantly, it inherits SheetJS's parser behavior on malformed or hostile input. When people ask whether node xlsx is safe, the honest answer is "as safe as the SheetJS version resolved in your lockfile."
The Vulnerabilities You Inherit
Two SheetJS advisories matter most for anyone accepting uploaded spreadsheets.
The first is CVE-2023-30533, a prototype pollution issue. All versions of SheetJS Community Edition through 0.19.2 are affected when reading a specially crafted file, and the fix landed in 0.19.3. Prototype pollution lets an attacker inject properties onto Object.prototype by crafting a workbook whose internal structure abuses __proto__. Downstream, that can escalate into denial of service, data tampering, or in some application shapes, code execution. Critically, this only bites workflows that read attacker-controlled files. If you only ever export data to spreadsheets, this particular CVE does not apply.
The second is CVE-2024-22363, a regular expression denial of service (ReDoS). All versions through 0.20.1 are affected, and the fix is in 0.20.2. An attacker supplies input that forces a pathological regex backtrack, and a single request can pin a CPU core.
There is a distribution wrinkle worth flagging: SheetJS moved its fixed releases (0.20.2 and later) off the public npm registry and onto cdn.sheetjs.com. That means a naive npm update may not pull the patched version, because npm simply does not host it. You have to point your dependency at the CDN tarball explicitly. This is a real supply chain gotcha that trips up teams who assume npm audit fix will resolve everything.
Auditing Your node-xlsx Install
Start by figuring out which SheetJS version node-xlsx has actually resolved:
npm ls xlsx
npm why xlsx
If npm ls shows a version at or below 0.20.1, you are exposed to the ReDoS; at or below 0.19.2, you also carry the prototype pollution issue. Run an audit to see how your tooling reports it:
npm audit --omit=dev
Bear in mind that because the patched SheetJS builds live on the CDN rather than npm, npm audit may report an unfixable advisory even when a fix exists. Software composition analysis that understands the SheetJS CDN distribution will give you a truer picture than the registry alone; an SCA tool such as Safeguard can flag the transitive xlsx dependency that node-xlsx drags in and tell you whether the resolved version is behind the advisory line.
Hardening the Parse Path
Version pinning is necessary but not sufficient. Treat every uploaded spreadsheet as hostile and build defenses around the parse call.
Validate before you parse. Check the file's declared MIME type, cap the size, and reject anything that does not look like a real .xlsx or .xls container. A 2 MB spreadsheet that expands to a gigabyte of parsed cells is a classic decompression bomb.
Isolate the work. Run parsing in a worker thread or a short-lived child process with a wall-clock timeout, so a ReDoS stall or runaway allocation kills one worker instead of your event loop:
const xlsx = require('node-xlsx');
function parseWithTimeout(buffer, ms = 5000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('parse timeout')), ms);
try {
const sheets = xlsx.parse(buffer);
clearTimeout(timer);
resolve(sheets);
} catch (err) {
clearTimeout(timer);
reject(err);
}
});
}
Freeze the prototype in the process that handles uploads. Object.freeze(Object.prototype) shuts the door on prototype pollution entirely, at the cost of breaking any code that monkey-patches built-ins. Test carefully, but for a dedicated parsing microservice it is a strong control.
Sanitize what comes out. Cell values are strings you did not write. If a value flows into a shell command, a SQL query, or an HTML page, it needs the same escaping you would apply to any other user input.
Choosing node-xlsx Versus the Alternatives
node-xlsx is fine for straightforward read/write of small to medium files where you want a compact API. If you are parsing large files or streaming, ExcelJS handles streaming better. If you need the widest format support, plain SheetJS gives you more control and lets you pin the CDN-patched build directly rather than waiting for node-xlsx to bump its dependency. The security calculus is the same across all of them: know your parser version, sandbox the parse, and never trust cell contents. For a broader look at how transitive dependencies like this get flagged, our guide to catching risky npm dependencies covers the same auditing workflow for another common package.
FAQ
Is node-xlsx safe to use in production?
Yes, provided the SheetJS xlsx version it resolves is patched (0.20.2 or later for ReDoS, 0.19.3 or later for prototype pollution) and you sandbox the parse of untrusted files. The wrapper itself is thin; the risk lives in the engine underneath.
Why does npm audit still flag xlsx after I update?
Because SheetJS publishes patched releases (0.20.2+) on cdn.sheetjs.com, not the public npm registry. npm update cannot fetch a version npm does not host, so you must point the dependency at the CDN tarball. This is a known, deliberate distribution change.
Does the prototype pollution CVE affect me if I only export spreadsheets?
No. CVE-2023-30533 is triggered only when reading a specially crafted file. Workflows that build and export spreadsheets from your own data are not affected by that particular issue.
What is the difference between node-xlsx and xlsx?
xlsx (SheetJS) is the full parsing and serialization engine. node-xlsx is a lightweight wrapper that exposes a simpler API and depends on SheetJS to do the work, so it inherits SheetJS's capabilities and its vulnerabilities.