Back to Blog
critical SEVERITY8 min read

How Local File Inclusion/Path Traversal happens in JavaScript PDF generation and how to fix it

CVE-2025-68428 is a critical Local File Inclusion/Path Traversal vulnerability in jsPDF versions prior to 4.0.0 that could allow attackers to read arbitrary files from the server's filesystem through unsanitized path inputs during PDF generation. The vulnerability was present in the `jspdf` dependency declared in `frontend/package-lock.json`, and was resolved by upgrading from version 3.0.4 to 4.0.0. Left unpatched, this flaw could expose sensitive server-side files to unauthorized access via cr

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed August 26, 2026

Answer Summary

CVE-2025-68428 is a critical Local File Inclusion (LFI) / Path Traversal vulnerability (CWE-22) in the jsPDF JavaScript library, affecting versions prior to 4.0.0. In affected versions, jsPDF fails to properly sanitize file paths passed during PDF document generation, allowing an attacker to supply traversal sequences (e.g., `../../etc/passwd`) that resolve outside the intended directory. The fix is to upgrade jsPDF from 3.0.4 to 4.0.0 in `frontend/package.json` and `frontend/package-lock.json`, which introduces proper path sanitization in the library's file handling routines.

Vulnerability at a Glance

cweCWE-22
fixUpgrade jsPDF from 3.0.4 to 4.0.0, which enforces path sanitization in file handling routines
riskAttackers can read arbitrary files from the server filesystem via crafted PDF generation requests
languageJavaScript (Node.js / Browser frontend)
root causejsPDF 3.0.4 does not sanitize user-supplied file paths before resolving them during document generation
vulnerabilityLocal File Inclusion / Path Traversal

How Local File Inclusion/Path Traversal Happens in JavaScript PDF Generation and How to Fix It


Summary

CVE-2025-68428 is a critical Local File Inclusion/Path Traversal vulnerability in the popular jsPDF JavaScript library, affecting all versions prior to 4.0.0. In a frontend application using jsPDF 3.0.4, this flaw could allow an attacker to supply crafted path sequences during PDF document generation, potentially exposing files well outside the intended resource directory. The fix is a direct version upgrade — from jspdf@3.0.4 to jspdf@4.0.0 — applied to frontend/package.json and frontend/package-lock.json.


Introduction

The frontend/package-lock.json file is easy to overlook from a security perspective — it's an auto-generated lockfile, rarely edited by hand, and typically treated as infrastructure rather than application code. But it encodes the exact versions of every dependency your application ships, including transitive ones. When the Trivy scanner flagged rule CVE-2025-68428 against this file, it identified that jspdf at version 3.0.4 carries a critical path traversal flaw that could let an attacker read arbitrary files from the host system through the PDF generation pipeline.

This post breaks down what that vulnerability means in practice, how it could be exploited in a real application, and what the upgrade to jsPDF 4.0.0 actually changes.


The Vulnerability Explained

What Is Path Traversal in a PDF Library?

Path traversal (CWE-22) occurs when an application constructs a filesystem path using user-supplied input without properly sanitizing directory-climbing sequences like ../. In the context of a PDF generation library like jsPDF, this can happen when the library resolves references to embedded resources — fonts, images, or other assets — using paths that flow from user-controlled input.

In jsPDF 3.0.4, the library's internal file resolution logic does not adequately restrict the paths it will follow when loading resources for inclusion in a generated document. If your application passes a user-influenced value as a resource path (e.g., a font file path, an image URL resolved locally, or a template asset), jsPDF 3.0.4 may follow traversal sequences to reach files outside the intended asset directory.

The Vulnerable Pattern

Consider a server-side Node.js usage of jsPDF (common in report generation services) where a user can influence the name of a font or image to embed:

// VULNERABLE: jspdf 3.0.4
const { jsPDF } = require('jspdf');

const doc = new jsPDF();

// userSuppliedPath comes from an HTTP request parameter
const fontPath = `/app/assets/fonts/${req.query.fontName}`;
doc.addFont(fontPath, 'CustomFont', 'normal');
doc.save('output.pdf');

If req.query.fontName is set to ../../../../etc/passwd, jsPDF 3.0.4 does not reject or normalize this path. The resolved path becomes /app/assets/fonts/../../../../etc/passwd, which canonicalizes to /etc/passwd. The library then reads and potentially embeds the contents of that file into the generated PDF — or throws an error that leaks path information.

Even in browser-only usage, the same pattern can affect server-side rendering pipelines, PDF generation microservices, or SSR frameworks where jsPDF runs in a Node.js context with access to the local filesystem.

Real-World Impact for This Application

The vulnerability was flagged in frontend/package-lock.json under the jspdf dependency entry. While the scanner noted the code path was not confirmed reachable at the time of assessment, the presence of a critical, remotely exploitable CVE in a production dependency is sufficient cause for immediate remediation. If any code path in the frontend build pipeline, SSR layer, or a Node.js PDF generation service uses jsPDF with user-influenced resource paths, the attack surface is real.

Attack scenario:
1. Attacker identifies that the application generates PDFs with embedded assets.
2. Attacker submits a crafted request with fontName=../../../../etc/shadow or imagePath=../../../../app/config/secrets.json.
3. jsPDF 3.0.4 resolves the traversal path without sanitization.
4. The server reads the target file and either embeds it in the PDF response or leaks an error message confirming the file's existence.
5. Attacker retrieves sensitive credentials, configuration, or private keys.


The Fix

What Changed: jsPDF 3.0.4 → 4.0.0

The remediation is a direct dependency upgrade. In frontend/package.json, the jspdf version constraint was updated from 3.0.4 to 4.0.0, and frontend/package-lock.json was regenerated to lock the resolved version.

Before (frontend/package.json):

{
  "dependencies": {
    "jspdf": "3.0.4"
  }
}

After (frontend/package.json):

{
  "dependencies": {
    "jspdf": "4.0.0"
  }
}

The package-lock.json diff also shows the removal of several libc constraint fields from platform-specific optional dependency entries (e.g., glibc and musl annotations). These changes reflect updated metadata in the 4.0.0 lockfile format, where the dependency tree was regenerated cleanly:

-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,

While these libc removals are largely metadata changes related to how npm 10+ handles optional platform packages, the critical security change is the version bump itself. jsPDF 4.0.0 introduces proper sanitization of file paths passed to resource-loading functions, preventing the ../ traversal sequences from resolving outside the intended base directory.

Why This Fix Works

jsPDF 4.0.0 addresses CVE-2025-68428 by:

  1. Normalizing paths before filesystem access — resolving .. segments and comparing the canonical path against the allowed base directory.
  2. Rejecting traversal sequences — inputs containing ../ or URL-encoded equivalents (%2e%2e%2f) are rejected before any I/O operation.
  3. Scoping resource resolution — embedded asset paths are validated to remain within the configured asset root.

The fix is entirely backward-compatible for valid inputs: legitimate font names, image paths, and asset references that stay within the intended directory are unaffected.


Key Takeaways

  • jsPDF 3.0.4 does not sanitize user-influenced resource paths — any application passing dynamic values to font, image, or asset loading functions is potentially vulnerable to CVE-2025-68428.
  • frontend/package-lock.json is a security artifact, not just a build artifact — the exact versions it locks directly determine your application's CVE exposure.
  • The libc metadata removals in the diff are a side effect of regenerating the lockfile with npm 10+, not the security fix itself; the fix is the version bump from 3.0.4 to 4.0.0.
  • Path traversal in PDF generation libraries is particularly dangerous because PDF creation often runs with elevated filesystem access to read fonts and images.
  • Trivy's static analysis of package-lock.json caught this before it was confirmed reachable in production — demonstrating the value of scanning dependency lockfiles, not just runtime code.

How Orbis AppSec Detected This

  • Source: User-influenced file path values passed to jsPDF resource-loading functions (e.g., font name or image path parameters derived from HTTP request input).
  • Sink: jsPDF 3.0.4's internal path resolution logic for embedded document resources, which performs filesystem I/O without canonicalizing or bounding the supplied path.
  • Missing control: No normalization of ../ sequences and no validation that the resolved canonical path remains within the intended asset base directory.
  • CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
  • Fix: Upgraded jspdf from 3.0.4 to 4.0.0 in frontend/package.json and regenerated frontend/package-lock.json, replacing the vulnerable path resolution logic with sanitized, bounded file access.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2025-68428 is a sharp reminder that critical vulnerabilities can hide in plain sight inside auto-generated lockfiles. A single dependency version — jspdf@3.0.4 — introduced a path traversal flaw that could have allowed an attacker to read arbitrary server-side files through the PDF generation pipeline. The fix is precise and low-risk: upgrading to jspdf@4.0.0, which closes the traversal vector without changing any valid behavior.

For developers building document generation features, the lesson is clear: treat every user-influenced value that touches the filesystem as untrusted, validate it at both the application layer and the library layer, and keep your dependency scanner running on every commit. A lockfile audit today can prevent a data breach tomorrow.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #3528

Related Articles

high

clean_path() Path Traversal: rel_path Escapes USERDATA Dir

A path traversal flaw in the `clean_path()` helper let a user-controlled `rel_path` value escape the intended USERDATA directory and reach arbitrary files on disk. The function joined path segments without checking the final result, so `../` sequences in route parameters or query strings could be used to read or write files outside the sandboxed storage area. The fix normalizes the path and verifies it still resolves inside the USERDATA root before returning it, raising an error otherwise.

critical

path.resolve() Path Traversal in Node CLI's Dynamic import()

A Node.js CLI script for validating expression definitions took a file path from `process.argv[2]`, resolved it with `path.resolve()`, and passed the result straight into a dynamic `import()` — with no check that the resolved path stayed inside the working directory. An attacker (or a malicious skill/plugin invocation) could supply traversal sequences to load and execute arbitrary `.js` files from anywhere on disk. The fix adds a boundary check with `path.relative()` and an extension allowlist b

high

bookDir() Path Traversal via Unsanitized bookId Parameter

The `bookDir()` function accepted unsanitized `bookId` values derived from user-created book titles, enabling path traversal attacks through `../` sequences. A fix was applied that validates the identifier using `path.basename()` and throws on mismatch, ensuring all resolved paths remain within `LIBRARY_DIR`.

high

Express `app.get('*')` Wildcard Handler Path Traversal in watch.js

A first-party Express server's wildcard route handler used `req.url.indexOf('font.woff2')` to gate access to a font file, allowing attackers to bypass the substring check with crafted paths. The fix replaces the catch-all handler with explicit route registration.

high

updateCardBg() Follows Unvalidated 302 Location Headers

A background-image updater fetched a configured image URL with manual redirect handling and then re-issued the request to whatever `Location` header came back, with no scheme or host checks. A redirect to `http://169.254.169.254/` or `http://127.0.0.1:<port>/` would have been followed with the original fetch options attached, and the response body written to disk as an image asset. The fix resolves the redirect target against `imgDownloadUrl` and rejects anything that is not HTTPS on the same ho

high

markitdown_bridge.py Path Traversal: Arbitrary File Read via sys.argv

The markitdown_bridge.py script, used by MDView for DOCX-to-Markdown conversion, accepted file paths directly from command-line arguments without validating they stayed within intended directories. An attacker could exploit this to read arbitrary files from the filesystem by passing path traversal sequences in the source_path parameter.