The exceljs npm package is an actively maintained, widely used library for reading and writing Excel workbooks in Node.js, and it is a reasonable dependency to adopt, but the moment you use it to parse spreadsheets uploaded by users, you are parsing untrusted, attacker-controllable input and need to treat it accordingly. ExcelJS pulls roughly two million downloads a week and is commonly recommended as a maintained alternative to the older xlsx (SheetJS) package. The library being healthy does not make spreadsheet parsing safe by default; that part is on you.
What exceljs is and where it stands
ExcelJS reads, writes, and manipulates .xlsx workbooks: styling, formulas, images, streaming for large files. It is the go-to when you need real write support, not just parsing, and it is maintained on GitHub under exceljs/exceljs. Installation is the usual:
npm install exceljs
You will see it referenced as both exceljs npm and npm exceljs; same package. Compared with xlsx, ExcelJS is frequently chosen because it stays maintained and because a well-known prototype-pollution issue affected SheetJS. That comparison is worth understanding before you assume a swap makes you safe.
The xlsx comparison that drives people here
A lot of teams land on ExcelJS after hitting CVE-2023-30533, a prototype-pollution vulnerability in SheetJS Community Edition before 0.19.3, where a crafted spreadsheet could inject properties into Object.prototype because the parser did not validate assigned values. The complication is that for a period the fixed xlsx version was not published to the public npm registry, pushing people to alternatives. ExcelJS is the most common landing spot.
The important nuance: switching libraries does not eliminate the risk class. Prototype pollution, denial of service through pathological files, and memory exhaustion are properties of parsing complex file formats, not of one specific library. A different parser can have its own bugs. So the right mindset is not "ExcelJS is safe, xlsx was not," but "any spreadsheet parser handling untrusted input needs guardrails."
The real risks when parsing untrusted spreadsheets
An .xlsx file is a ZIP archive of XML. That structure creates several attack avenues that apply to spreadsheet parsing in general:
- Decompression bombs. A tiny file that expands to gigabytes on parse, exhausting memory. Cap the accepted file size before you open it, and be wary of streaming enormous sheets.
- Malformed or hostile XML. Parsers can be pushed into excessive resource use by deeply nested or pathological structures, a denial-of-service vector.
- Prototype pollution. As the SheetJS CVE showed, a parser that copies keys from file content into objects without guarding
__proto__andconstructorcan corrupt application state. Validate and sanitize any object you build from parsed cell data. - Formula and content trust. Cell values, including anything that looks like a formula, are attacker-controlled data. Never evaluate parsed content, and encode it before rendering it anywhere.
The through-line: data that came out of a spreadsheet is untrusted input, exactly like a form field or a JSON body. Validate it against a schema before it reaches your database or your UI.
Mind the dependency tree
ExcelJS itself being maintained does not mean its dependencies are all current. Reports have flagged vulnerabilities in packages down the ExcelJS dependency chain, including older lodash versions carried transitively. This is the ordinary reality of any real-world Node dependency: the package you install is the tip of a tree, and advisories can land in packages you never chose directly.
The practical response is to scan the resolved tree, not just the top-level package.json. Run npm audit and an SCA scan in CI, and pin everything with a committed package-lock.json. An SCA tool such as Safeguard can flag a vulnerable transitive dependency of ExcelJS even when it sits several levels deep, which a top-level review misses. Our SCA product page explains how transitive resolution works.
Using exceljs safely
A defensive pattern for accepting an uploaded workbook:
const ExcelJS = require('exceljs')
async function parseUpload(buffer) {
if (buffer.length > 5 * 1024 * 1024) { // cap size before parsing
throw new Error('file too large')
}
const workbook = new ExcelJS.Workbook()
await workbook.xlsx.load(buffer)
const rows = []
workbook.worksheets[0].eachRow((row) => {
// treat every cell as untrusted; validate against a schema
rows.push(validateRow(row.values))
})
return rows
}
The guardrails that matter: a size cap enforced before parsing, a per-row validation step so malformed data never flows onward, and resource limits (timeouts, worker isolation) if you process large or many files. Run parsing off the main request path for big files so a slow or hostile upload cannot stall your service.
The maintenance loop
Keeping npm exceljs safe over time is the same discipline as any dependency:
- Pin with a committed lockfile including integrity hashes.
- Scan the full tree in CI, failing builds on high-severity findings in transitive packages.
- Update promptly when an advisory lands, especially in sub-dependencies.
- Never trust spreadsheet content: validate, bound, and encode it.
FAQ
Is exceljs safe to use?
Yes, as a dependency it is actively maintained and widely adopted. The risk is not the library but what you do with it: parsing spreadsheets uploaded by users means handling untrusted input, which requires size caps, validation, and dependency scanning regardless of which parser you choose.
Is exceljs safer than xlsx?
ExcelJS is commonly chosen after the SheetJS prototype-pollution CVE and because it stays maintained, but switching libraries does not remove the underlying risk class. Any complex-format parser handling untrusted files can have decompression-bomb, denial-of-service, or pollution issues. Guardrails matter more than the library name.
What are the main risks of parsing spreadsheets?
Decompression bombs that exhaust memory, denial of service from pathological XML, prototype pollution from unsafe key copying, and treating cell values as trusted when they are attacker-controlled. Cap file size, validate parsed data, and never evaluate spreadsheet content.
Do I need to worry about exceljs dependencies?
Yes. Advisories have appeared in packages within the ExcelJS dependency chain. Scan the full resolved tree with SCA in CI and pin versions with a committed lockfile so a transitive vulnerability is caught rather than shipped.