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
Published August 26, 2026Reviewed 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.


Prevention & Best Practices

1. Validate Paths at the Application Layer Too

Even with a patched library, never pass raw user input directly as a file path. Validate and allowlist resource identifiers before they reach any file-loading API:

const ALLOWED_FONTS = ['Helvetica', 'Courier', 'Arial'];

if (!ALLOWED_FONTS.includes(req.query.fontName)) {
  return res.status(400).json({ error: 'Invalid font name' });
}

2. Canonicalize and Scope Paths Explicitly

If dynamic paths are genuinely required, resolve them to a canonical absolute path and verify they remain under the expected base directory:

const path = require('path');

const BASE_DIR = '/app/assets/fonts';
const requestedFont = req.query.fontName;
const resolvedPath = path.resolve(BASE_DIR, requestedFont);

if (!resolvedPath.startsWith(BASE_DIR + path.sep)) {
  throw new Error('Path traversal attempt detected');
}

3. Keep Dependencies Audited and Locked

  • Run npm audit in CI on every pull request.
  • Use tools like Trivy, Snyk, or Socket.dev to scan package-lock.json for known CVEs.
  • Pin exact versions in package-lock.json and regenerate it when upgrading.

4. Apply the Principle of Least Privilege

If your PDF generation service runs in Node.js, restrict its filesystem access using OS-level controls (e.g., chroot, Docker volume mounts scoped to /app/assets) so that even a successful traversal cannot reach sensitive files.

5. Reference Security Standards


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.


References

Frequently Asked Questions

What is a Local File Inclusion / Path Traversal vulnerability?

It's a flaw where an application uses unsanitized user input to construct file paths, allowing attackers to use sequences like `../` to escape the intended directory and access arbitrary files on the server.

How do you prevent path traversal in JavaScript applications?

Always validate and canonicalize file paths against a known safe base directory, reject inputs containing `..` sequences, and use library versions that enforce these controls internally.

What CWE is Local File Inclusion / Path Traversal?

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

Is input validation alone enough to prevent path traversal in jsPDF?

Input validation at the application layer helps but is insufficient if the underlying library also fails to sanitize paths. The safest approach is to upgrade to jsPDF 4.0.0, which fixes the issue at the library level.

Can static analysis detect path traversal vulnerabilities like CVE-2025-68428?

Yes. Tools like Trivy (which flagged this CVE), Semgrep, and npm audit can identify vulnerable dependency versions and unsafe path handling patterns automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #3528

Related Articles

high

How Path Traversal happens in Node.js PostCSS and how to fix it

A high-severity path traversal vulnerability in PostCSS versions before 8.5.18 allowed attackers to exploit the `sourceMappingURL` auto-loading mechanism to read arbitrary `.map` files from the filesystem. The fix upgrades PostCSS from 8.5.8 to 8.5.18 and pins the dependency via an npm `overrides` entry, closing the attack surface entirely. Any project using PostCSS as a direct or transitive dependency should apply this upgrade immediately.

critical

How Path Traversal happens in JavaScript i18n loaders and how to fix it

A path traversal vulnerability in `beta/js/i18n-chatrd.js` allowed attackers to manipulate the `lang` URL query parameter to load arbitrary JSON files from the web server by injecting payloads like `../../sensitive-file`. The fix adds input validation to ensure only safe, expected language codes are accepted before they are interpolated into the fetch URL. This type of vulnerability is especially dangerous in internationalization loaders because they are often publicly accessible and designed to

high

How Path Traversal happens in Python FastAPI and how to fix it

A critical path traversal vulnerability was discovered in `SovitsTest/GSVI.py`, a FastAPI-based TTS inference server, where the `/upload` endpoint accepted user-supplied filenames without sanitization. An unauthenticated remote attacker could exploit this to write arbitrary files anywhere on the filesystem — including sensitive system directories like `/etc/cron.d`. The fix adds path validation to prevent filenames from escaping the intended upload directory.

high

How Path Traversal happens in Python Flask routes and how to fix it

A high-severity path traversal vulnerability was discovered in `xkeen-ui/routes/cores_status.py` at line 221, where user-controlled input was passed directly to Python's `open()` function without sanitization. An attacker could exploit this to read arbitrary files on the server by supplying crafted path strings like `../../etc/passwd`. The fix introduces strict path validation using a trusted root directory, ensuring only files within the intended directory can be accessed.

critical

How Path Traversal happens in Vitest UI Server and how to fix it

CVE-2026-47429 is a critical path traversal vulnerability in Vitest's UI server that allows unauthenticated attackers to read and execute arbitrary files on the host system when the UI server is active. The vulnerability was fixed by upgrading Vitest from the vulnerable `^4.0.0` range to the pinned safe release `4.1.0`. Any project running Vitest's UI mode during development or CI is potentially exposed until this upgrade is applied.

critical

How eval() Code Injection happens in JavaScript and how to fix it

A critical code injection vulnerability was discovered in `js/lib/jsencrypt.js` at line 195, where a direct `eval()` call executed a JavaScript string shim for the `process` object in browser environments. If an attacker could influence the string passed to `eval()`—through a compromised dependency, a man-in-the-middle attack, or supply chain tampering—they could achieve arbitrary JavaScript execution in any user's browser. The fix replaces the `eval()` call with the equivalent inline JavaScript