GHSA-qq9g-96v4-m3cj: @pdfme/schemas Cross-Site Scripting Vulnerability
MODERATEGHSA-qq9g-96v4-m3cj is a MODERATE (CVSS 6.1) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting @pdfme/schemas libraries in the npm ecosystem. The issue involves cross-site scripting (xss) via select schema option value injection in @pdfme/schemas. Remediation is available by upgrading affected packages to version 5.5.9.
GHSA-qq9g-96v4-m3cj at a Glance
Security Impact
CVSS: 6.1
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).
How to Fix GHSA-qq9g-96v4-m3cj
To remediate this vulnerability, upgrade all affected @pdfme/schemas dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @pdfme/schemas@5.5.9
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @pdfme/schemas
How to Check if You Are Affected
To determine whether your Node.js application is affected by GHSA-qq9g-96v4-m3cj, inspect your project dependencies using npm:
npm ls @pdfme/schemas
Analyze the terminal output to locate any version of @pdfme/schemas in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.5.9), your application is affected.
Note: The npm ls @pdfme/schemas command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In @pdfme/schemas, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
GHSA-qq9g-96v4-m3cj Affected @pdfme/schemas Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@pdfme/schemas
|
npm |
< 5.5.9
|
5.5.9
|
References
Frequently Asked Questions
What is GHSA-qq9g-96v4-m3cj?
Which @pdfme/schemas versions are affected?
What version fixes GHSA-qq9g-96v4-m3cj?
Is @pdfme/schemas affected?
What is the CVSS score of GHSA-qq9g-96v4-m3cj?
What CWE is associated with GHSA-qq9g-96v4-m3cj?
How can I check npm dependencies for GHSA-qq9g-96v4-m3cj?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-qq9g-96v4-m3cj",
"modified" : "2026-03-18T16:10:16Z",
"published" : "2026-03-18T16:10:16Z",
"aliases" : [ ],
"summary" : "Cross-Site Scripting (XSS) via Select Schema Option Value Injection in @pdfme/schemas",
"details" : "## Summary\n\nThe 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.\n\n## Details\n\nIn `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:\n\n```typescript\nconst options = Array.isArray(schema.options) ? schema.options : [];\nselectElement.innerHTML = options\n .map(\n (option) =>\n `<option value=\"${option}\" ${option === value ? 'selected' : ''}>${option}</option>`,\n )\n .join('');\n```\n\nThe `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.\n\n## Proof of Concept\n\nLoading the following template into a pdfme Form or Designer component triggers JavaScript execution:\n\n```json\n{\n \"basePdf\": { \"width\": 210, \"height\": 297, \"padding\": [20, 20, 20, 20] },\n \"schemas\": [[\n {\n \"name\": \"malicious_select\",\n \"type\": \"select\",\n \"content\": \"Normal\",\n \"options\": [\n \"Normal\",\n \"\\\"></option><img src=x onerror=\\\"alert(document.domain)\\\">\"\n ],\n \"position\": { \"x\": 20, \"y\": 20 },\n \"width\": 80,\n \"height\": 10\n }\n ]]\n}\n```\n\nThe injected `<img onerror>` element executes JavaScript because it is parsed as HTML when assigned to `selectElement.innerHTML`.\n\n## Attack Vectors\n\nThe `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:\n1. File upload (e.g., \"Load Template\" functionality in applications)\n2. Shared/imported templates in multi-tenant applications\n3. Templates stored in databases without content sanitization\n4. The `updateTemplate()` API being called with untrusted data\n\nThis vulnerability is triggered in Form mode (for non-readOnly select fields) and Designer mode when the select element is rendered.\n\n## Impact\n\nAn 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:\n- Session hijacking via cookie/token theft\n- Keylogging of form input data\n- Phishing and page modification\n- Data exfiltration\n\n## Suggested Fix\n\nUse DOM APIs to create option elements safely instead of string interpolation:\n\n```typescript\noptions.forEach((option) => {\n const optionEl = document.createElement('option');\n optionEl.value = option;\n optionEl.textContent = option;\n if (option === value) optionEl.selected = true;\n selectElement.appendChild(optionEl);\n});\n```\n\nAlternatively, HTML-encode option values before interpolation:\n```typescript\nconst escape = (s) => s.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n```",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@pdfme/schemas"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "5.5.9"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 5.5.8"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/pdfme/pdfme/security/advisories/GHSA-qq9g-96v4-m3cj"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/pdfme/pdfme"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-79" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-18T16:10:16Z",
"nvd_published_at" : null
}
}