What's new
Security fixes
Four vulnerabilities identified and patched in this release:
Critical — DoS via infinite loop (concurrencyLimit)
Passing concurrencyLimit: 0 or any negative integer with processPagesInParallel: true caused an infinite loop that permanently blocked the event loop. concurrencyLimit is now validated as a positive integer (>= 1) before any I/O; invalid values throw immediately.
High — Out-of-memory via unbounded viewportScale
viewportScale had no upper bound. A value of 1e6 produced a canvas allocation of ~200 TB, crashing the process with ENOMEM. Two guards were added:
viewportScaleis now capped at1000(validated before the firstawait)- A canvas pixel-area cap of 100,000,000 px (≈ 400 MB at 4 bytes/px) is enforced per page before any allocation
Medium — TOCTOU race in write-containment guard
A symlink swap between the realpath() containment check and writeFile() could redirect output to an arbitrary path. A second realpath() re-verification is now performed immediately before writeFile(), narrowing the race window to its minimum. The residual limitation (inherent to userspace POSIX I/O) is documented in JSDoc and PdfToPngOptions.
Low — Path traversal via outputFileMaskFunc
A custom outputFileMaskFunc returning ../secret.png or an absolute path could write outside the declared outputFolder. A segment-aware containment check using path.relative() + realpath() now rejects any filename that escapes the output directory.
New feature — Uint8Array and Buffer accepted as direct input
The pdfFile parameter type is widened from string | ArrayBufferLike to string | ArrayBufferLike | Uint8Array. Node.js Buffer instances are also accepted directly without wrapping. Internal buffer normalisation avoids the double-copy that previously occurred for ArrayBufferLike inputs.
// All of these now work:
await pdfToPng(fs.readFileSync('doc.pdf')); // Buffer
await pdfToPng(new Uint8Array(rawBytes)); // Uint8Array
await pdfToPng(arrayBuffer); // ArrayBuffer (unchanged)
await pdfToPng('/path/to/doc.pdf'); // string (unchanged)Package improvements
- Added
"type": "commonjs"for explicit module format declaration - Added
"require"condition to theexportsfield for better CJS tooling compatibility - Removed dead
build:dockerscript andpredocker:testlifecycle hook - Updated package description to accurately reflect pre-built native binaries
- Corrected
.dockerignoreto exclude.env,.claude/, and other local-only files from the Docker build context
Breaking changes
These changes throw in cases that previously caused silent wrong behavior:
| Option | Old behavior | New behavior |
|---|---|---|
viewportScale > 1000
| Process crash (OOM) | Throws Error immediately
|
viewportScale <= 0 or non-finite
| Throws (unchanged) | Throws (unchanged) |
concurrencyLimit < 1 with processPagesInParallel: true
| Infinite loop / hang | Throws Error immediately
|
| Canvas > 100,000,000 px | Process crash (OOM) | Throws Error per page
|
outputFileMaskFunc returning ../ path
| Writes outside outputFolder
| Throws Error
|
Upgrade guide
No API changes are required for standard usage. If your code explicitly relies on any of the behaviors listed in the breaking changes table above, update accordingly:
- Keep
viewportScalein the range(0, 1000]— values above 12× are rarely useful in practice (600 dpi A0 paper ≈ scale 12) - Keep
concurrencyLimitas a positive integer (>= 1) whenprocessPagesInParallel: true