jsPDF has PDF Object Injection via Unsanitized Input in addJS Method
Description
jsPDF is a library to generate PDFs in JavaScript. Prior to 4.2.0, user control of the argument of the addJS method allows an attacker to inject arbitrary PDF objects into the generated document. By crafting a payload that escapes the JavaScript string delimiter, an attacker can execute malicious actions or alter the document structure, impacting any user who opens the generated PDF. The vulnerability has been fixed in jspdf@4.2.0. As a workaround, escape parentheses in user-provided JavaScript code before passing them to the addJS method.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
jspdfnpm | < 4.2.0 | 4.2.0 |
Affected products
1Patches
13 files changed · +78 −3
src/modules/javascript.js+25 −3 modified@@ -41,11 +41,31 @@ import { jsPDF } from "../jspdf.js"; * @returns {jsPDF} */ jsPDFAPI.addJS = function(javascript) { - // FIX: Move variables inside function scope to prevent shared state - // between multiple jsPDF instances var jsNamesObj; var jsJsObj; - var text = javascript; + // Escape only unescaped parentheses, without double-escaping already escaped ones + function escapeParens(str) { + let out = ""; + for (let i = 0; i < str.length; i++) { + const ch = str[i]; + if (ch === "(" || ch === ")") { + // Count preceding backslashes to determine if the paren is already escaped + let bs = 0; + for (let j = i - 1; j >= 0 && str[j] === "\\"; j--) { + bs++; + } + if (bs % 2 === 0) { + out += "\\" + ch; + } else { + out += ch; + } + } else { + out += ch; + } + } + return out; + } + const text = escapeParens(javascript); this.internal.events.subscribe("postPutResources", function() { jsNamesObj = this.internal.newObject(); @@ -57,10 +77,12 @@ import { jsPDF } from "../jspdf.js"; jsJsObj = this.internal.newObject(); this.internal.out("<<"); this.internal.out("/S /JavaScript"); + // The sanitized 'text' is now safe to be enclosed in parentheses this.internal.out("/JS (" + text + ")"); this.internal.out(">>"); this.internal.out("endobj"); }); + this.internal.events.subscribe("putCatalog", function() { if (jsNamesObj !== undefined && jsJsObj !== undefined) { this.internal.out("/Names <</JavaScript " + jsNamesObj + " 0 R>>");
test/reference/autoprint-js.pdf+0 −0 modifiedtest/specs/javascript.spec.js+53 −0 added@@ -0,0 +1,53 @@ +/* global describe, it, jsPDF, expect, loadGlobals */ +/** + * jsPDF javascript plugin tests + */ + +describe("Module: JavaScript", () => { + beforeAll(loadGlobals); + + it("should correctly escape parentheses in addJS", () => { + const doc = new jsPDF(); + doc.addJS("alert('Hello (world)');"); + const output = doc.output(); + expect(output).toContain("/JS (alert\\('Hello \\(world\\)'\\);)"); + }); + + it("should correctly escape nested parentheses in addJS", () => { + const doc = new jsPDF(); + doc.addJS("function test() { alert('((nested))'); }"); + const output = doc.output(); + expect(output).toContain( + "/JS (function test\\(\\) { alert\\('\\(\\(nested\\)\\)'\\); })" + ); + }); + + it("should not double-escape parentheses in addJS", () => { + const doc = new jsPDF(); + doc.addJS("alert('Hello \\(world\\)');"); + const output = doc.output(); + expect(output).toContain("/JS (alert\\('Hello \\(world\\)'\\);)"); + }); + + it("should not double-escape parentheses at the start in addJS", () => { + const doc = new jsPDF(); + doc.addJS("\\("); + const output = doc.output(); + expect(output).toContain("/JS (\\()"); + }); + + it("should escape parentheses at the start in addJS", () => { + const doc = new jsPDF(); + doc.addJS("("); + const output = doc.output(); + expect(output).toContain("/JS (\\()"); + }); + + it("should escape parentheses after escaped backslash in addJS", () => { + const doc = new jsPDF(); + doc.addJS("\\\\(\\\\)"); + const output = doc.output(); + + expect(output).toContain("/JS (\\\\\\(\\\\\\))"); + }); +});
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-9vjf-qc39-jprpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-25755ghsaADVISORY
- github.com/ZeroXJacks/CVEs/blob/main/2026/CVE-2026-25755.mdghsax_refsource_MISCWEB
- github.com/parallax/jsPDF/commit/56b46d45b052346f5995b005a34af5dcdddd5437ghsax_refsource_MISCWEB
- github.com/parallax/jsPDF/releases/tag/v4.2.0ghsax_refsource_MISCWEB
- github.com/parallax/jsPDF/security/advisories/GHSA-9vjf-qc39-jprpghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.