Cross-Site Scripting (XSS) via Select Schema Option Value Injection in @pdfme/schemas
Summary
The Select schema plugin in @pdfme/schemas constructs HTML from template-defined option values using unsanitized string interpolation and sets it via innerHTML, enabling arbitrary JavaScript execution.
Details
In packages/schemas/src/select/index.ts, lines 159-164, the Select schema's ui renderer builds <option> elements by directly interpolating option values from the template into an HTML string:
const options = Array.isArray(schema.options) ? schema.options : [];
selectElement.innerHTML = options
.map(
(option) =>
`<option value="${option}" ${option === value ? 'selected' : ''}>${option}</option>`,
)
.join('');
The option values come from schema.options, which is an array of strings defined in the template JSON. These values are interpolated directly into the HTML string without any escaping of <, >, ", &, or other HTML-special characters. An option value containing "> breaks out of the value attribute and allows injection of arbitrary HTML elements and event handlers.
Proof of Concept
Loading the following template into a pdfme Form or Designer component triggers JavaScript execution:
{
"basePdf": { "width": 210, "height": 297, "padding": [20, 20, 20, 20] },
"schemas": [[
{
"name": "malicious_select",
"type": "select",
"content": "Normal",
"options": [
"Normal",
"\"></option><img src=x onerror=\"alert(document.domain)\">"
],
"position": { "x": 20, "y": 20 },
"width": 80,
"height": 10
}
]]
}
The injected <img onerror> element executes JavaScript because it is parsed as HTML when assigned to selectElement.innerHTML.
Attack Vectors
The options array is defined in the template (not by form-filling end users). The attack requires a malicious template to be loaded, which can happen via:
- File upload (e.g., "Load Template" functionality in applications)
- Shared/imported templates in multi-tenant applications
- Templates stored in databases without content sanitization
- The
updateTemplate()API being called with untrusted data
This vulnerability is triggered in Form mode (for non-readOnly select fields) and Designer mode when the select element is rendered.
Impact
An attacker who can supply a malicious template can execute arbitrary JavaScript in the browser of any user who views or interacts with the template. This enables:
- Session hijacking via cookie/token theft
- Keylogging of form input data
- Phishing and page modification
- Data exfiltration
Suggested Fix
Use DOM APIs to create option elements safely instead of string interpolation:
options.forEach((option) => {
const optionEl = document.createElement('option');
optionEl.value = option;
optionEl.textContent = option;
if (option === value) optionEl.selected = true;
selectElement.appendChild(optionEl);
});
Alternatively, HTML-encode option values before interpolation:
const escape = (s) => s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
Affected software
Click a technology to see all vulnerabilities affecting it.
| Platform | Package | Vulnerable versions | Fixed in |
|---|---|---|---|
| npm package | @pdfme/schemas | < 5.5.9 | 5.5.9 |
References
- CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N- Weakness (CWE)
CWE-79- Platform
- npm
- Package
@pdfme/schemas- Disclosed
- 18.03.2026
- Updated
- 11.10.2026 10:22
Sources and licenses
OSV / GitHub Advisory Database - GHSA-qq9g-96v4-m3cj - CC BY 4.0
Vulnerability history: @pdfme/schemas
All published vulnerabilities of this package (npm package), newest first. Shows whether this is a one-off slip or a package that needs watching.
- Total entries
- 3
- First → latest
- 03.2026 03.2026
- Without a fix
- 0
- Exploited in attacks
- 0
Severity breakdown
- Medium 3
Disclosures per year
- 2026: 3
-
Cross-Site Scripting (XSS) via Select Schema Option Value Injection in @pdfme/schemas this entry
Know which vulnerabilities affect your application
With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.