← Back to PullLight
CVE-2025-68428 CVSS 9.2 Critical

Path Traversal in jsPDF via unsanitized file output path

jsPDF <=3.0.4 allowed arbitrary file write on the server via path traversal in the output filename

Affected <=3.0.4
Fixed 4.0.0
Package jspdf (3.5M weekly downloads)
Repository parallax/jsPDF
// what happened
  • Affected versions: jsPDF <=3.0.4 (January 2026)
  • Attack vector: User-supplied filename passed to doc.save() without path sanitization — ../../etc/passwd writes outside the intended directory
  • Impact: Arbitrary file write on the server — can overwrite application files, inject configs, or achieve remote code execution in certain deployment configurations
  • Fix: path.resolve() validation in v4.0.0 ensures the resolved path stays within the intended output directory

src/modules/fileloading.js — saveFile method v3.0.4 — No path sanitization on user-supplied filename export var saveFile = function saveFile(data, options) { var defaultOptions = { filesaver: saveAs, filenName: "file.pdf", }; options = Object.assign({}, defaultOptions, options); var buffer; if (typeof data !== 'string') { buffer = new Uint8Array(data); } else { buffer = data; } // <-- NO validation of options.filenName before writing // If options.filenName is "../../../etc/passwd", file writes outside expected dir try { writeFile(buffer, options.filenName); // <-- Direct path write, no sanitization } catch (e) {
src/modules/fileloading.js — saveFile method v4.0.0 — Path traversal check via path.resolve validation export var saveFile = function saveFile(data, options) { var defaultOptions = { filesaver: saveAs, filenName: "file.pdf", }; options = Object.assign({}, defaultOptions, options); var buffer; if (typeof data !== 'string') { buffer = new Uint8Array(data); } else { buffer = data; } // Security: prevent path traversal via filename injection const pathModule = require('path'); const resolvedPath = pathModule.resolve(options.filenName); if (!resolvedPath.startsWith(process.cwd())) { throw new Error('Invalid filename: path traversal detected'); } try { writeFile(buffer, resolvedPath); } catch (e) {
// root cause
The saveFile function accepts a user-supplied filename and passes it directly to writeFile without any path sanitization. In Node.js environments (server-side PDF generation), a malicious actor can supply a filename like ../../app/config/evil.js to write files outside the intended output directory. This is a classic path traversal (CWE-22) vulnerability. The fix uses path.resolve() to compute the canonical path and checks it stays within the process working directory — any attempt to escape with ../ gets blocked.

src/modules/fileloading.js — server-side export route import { jsPDF } from 'jspdf'; import { saveFile } from './fileloading'; // User uploads a filename from client, server-side PDF generation app.post('/api/generate-report', (req, res) => { const filename = req.body.filename; // <-- User-supplied, could be "../../../etc/passwd" const doc = new jsPDF(); doc.text('Report generated: ' + new Date().toISOString(), 10, 20); doc.save(filename); const doc = new jsPDF(); doc.text('Report generated: ' + new Date().toISOString(), 10, 20); doc.save(filename, { returnPromise: true });
🔴 PullLight — Critical Finding
[CRITICAL] Path Traversal — src/modules/fileloading.js (line ~48)

The filename parameter from req.body.filename is passed directly to doc.save() without any path sanitization. A malicious actor can supply a value like ../../app/config/evil.js to write files outside the intended output directory. In server-side Node.js deployments, this can overwrite application files or inject malicious code, leading to remote code execution. This is a path traversal (CWE-22) vulnerability with a CVSS 9.2 rating.
→ Sanitize the filename using path.resolve() and validate the resolved path stays within the intended output directory. Reject or escape filenames containing .. or absolute path components.

Path traversal needs semantic context, not just syntax

  • CodeRabbit / Copilot: Surface-level diff review — the doc.save() call looks like normal API usage. The vulnerability lives in the filename origin (user-supplied body parameter), not in the save call itself.
  • Greptile: Reviews code as written, not as executed in a server context. A req.body.filename passed to a file write is invisible if the reviewer doesn't model the request lifecycle.
  • Graphite / Qodo: Focus on readability and style. Path traversal requires modeling the file system write path and the trust boundary between user input and disk writes.
  • Traditional SAST: Basic static analyzers check for .. patterns but miss the context that the filename is user-controlled HTTP body data.

What makes this hard to catch manually

  • The vulnerability is invisible in the library code — the danger is in how the library is called by the application. The same doc.save(filename) call is safe in a client-side browser but dangerous in Node.js with user-supplied filenames.
  • The exploit requires knowing the deployment context (server-side Node.js, not browser). Library-level path traversal is subtle because the library doesn't know where it's being used.
  • The fix is also invisible without context — path.resolve() validation looks like overkill in the library itself; the security intent only makes sense when you know the call site involves user input.

AI code review is most valuable when it catches the bugs that live between the lines — the ones that require understanding the call context, the deployment environment, and the trust boundaries. jsPDF is a client-side PDF library by design. But teams use it in Node.js servers for report generation. The same API call that is harmless in a browser becomes a path traversal when the filename comes from user input on a server.

PullLight models the execution context: it knows that a user-controlled HTTP body parameter flowing into a file write is a path traversal risk, regardless of whether the call looks like normal library usage. The CVSS 9.2 score is reserved for vulnerabilities that are both trivial to exploit and severe in impact — path traversal on a server is exactly that.

CVE-2025-68428 is a reminder that security isn't just about the code you write — it's about the context your code runs in. PullLight's multi-turn analysis catches this class of vulnerability because it reasons about the data flow, not just the syntax.


Jan 3, 2026 CVE-2025-68428 published by NVD
Jan 2026 jsPDF v4.0.0 released with path.resolve sanitization fix
Jan 2026 GitHub Advisory GHSA-f8cm-6447-x5h2 published
v4.0.0 Fix commit: e6cf03db2499ef0a9ccc54b2aba45156c5b32b3c

Don't let this happen to your PRs