Lookup for vulnerable packages by Package URL.

GET /api/packages/952430?format=api
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "url": "http://public2.vulnerablecode.io/api/packages/952430?format=api",
    "purl": "pkg:npm/%40pdfme/schemas@3.1.4",
    "type": "npm",
    "namespace": "@pdfme",
    "name": "schemas",
    "version": "3.1.4",
    "qualifiers": {},
    "subpath": "",
    "is_vulnerable": true,
    "next_non_vulnerable_version": "5.5.10",
    "latest_non_vulnerable_version": "5.5.10",
    "affected_by_vulnerabilities": [
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/21505?format=api",
            "vulnerability_id": "VCID-kxsk-fpuj-5uaw",
            "summary": "PDFME  has XSS via Unsanitized i18n Label Injection into innerHTML in multiVariableText propPanel\n## Summary\n\nThe multiVariableText property panel in `@pdfme/schemas` constructs HTML via string concatenation and assigns it to `innerHTML` using unsanitized i18n label values. An attacker who can control label overrides passed through `options.labels` can inject arbitrary JavaScript that executes in the context of any user who opens the Designer and selects a multiVariableText field with no `{variables}` in its text.\n\n## Details\n\nWhen a user selects a multiVariableText schema field that contains no `{variable}` placeholders, the property panel renders instructional text by concatenating i18n-translated strings directly into `innerHTML`.\n\n**Vulnerable sink** — `packages/schemas/src/multiVariableText/propPanel.ts:65-71`:\n\n```typescript\n// Use safe string concatenation for innerHTML\nconst typingInstructions = i18n('schemas.mvt.typingInstructions');\nconst sampleField = i18n('schemas.mvt.sampleField');\npara.innerHTML =\n  typingInstructions +\n  ` <code style=\"color:${safeColorValue}; font-weight:bold;\">{` +\n  sampleField +\n  '}</code>';\n```\n\nThe comment on line 64 claims \"safe string concatenation\" but the result is assigned to `innerHTML` with no HTML escaping applied to `typingInstructions` or `sampleField`.\n\n**i18n lookup has no escaping** — `packages/ui/src/i18n.ts:903`:\n\n```typescript\nexport const i18n = (key: keyof Dict, dict?: Dict) => (dict || getDict(DEFAULT_LANG))[key];\n```\n\nThis is a plain dictionary lookup — no HTML encoding or sanitization.\n\n**Label override via deep merge** — `packages/ui/src/components/AppContextProvider.tsx:57-63`:\n\n```typescript\nlet dict = getDict(lang);\nif (options.labels) {\n  dict = deepMerge(\n    dict as unknown as Record<string, unknown>,\n    options.labels as unknown as Record<string, unknown>,\n  ) as typeof dict;\n}\n```\n\nUser-supplied `options.labels` values are deep-merged into the i18n dictionary with no content sanitization. The Zod schema validates labels as `z.record(z.string(), z.string())` — enforcing type but not content safety.\n\n**Inconsistency:** The color value on lines 58-62 is explicitly validated with a regex allowlist, demonstrating security awareness. The i18n string values were simply overlooked.\n\n## PoC\n\n1. **Create a minimal app that passes attacker-controlled labels:**\n\n```html\n<html>\n<body>\n<div id=\"designer-container\" style=\"width:100%;height:700px;\"></div>\n<script type=\"module\">\nimport { Designer } from '@pdfme/ui';\nimport { multiVariableText } from '@pdfme/schemas';\n\nconst template = {\n  basePdf: { width: 210, height: 297, padding: [10, 10, 10, 10] },\n  schemas: [[{\n    type: 'multiVariableText',\n    name: 'field1',\n    text: 'plain text with no variables',\n    content: '{}',\n    variables: [],\n    position: { x: 20, y: 20 },\n    width: 100,\n    height: 20,\n    readOnly: true,\n  }]],\n};\n\nnew Designer({\n  domContainer: document.getElementById('designer-container'),\n  template,\n  plugins: { multiVariableText },\n  options: {\n    labels: {\n      'schemas.mvt.typingInstructions':\n        '<img src=x onerror=\"document.title=document.cookie\">Inject: ',\n      'schemas.mvt.sampleField': 'safe',\n    },\n  },\n});\n</script>\n</body>\n</html>\n```\n\n2. **Open the application in a browser.**\n\n3. **Click on the multiVariableText field** (`field1`) in the Designer canvas to select it.\n\n4. **Observe:** The property panel renders the injected HTML. The `onerror` handler executes, setting `document.title` to the page's cookies. In a real attack, this would exfiltrate session tokens to an attacker-controlled server.\n\n## Impact\n\n- **Session hijacking:** Attacker-injected JavaScript can steal authentication cookies and tokens from any user who opens the Designer.\n- **DOM manipulation:** The injected script runs in the application's origin, allowing phishing overlays, form hijacking, or data exfiltration.\n- **Stored XSS potential:** In multi-tenant applications where labels are stored in a database or fetched from an API, a single poisoned label entry affects all users who subsequently open the Designer.\n- **Scope change:** The XSS payload executes in the embedding application's browser context, escaping the pdfme component's security boundary.\n\n## Recommended Fix\n\nReplace `innerHTML` with safe DOM APIs in `packages/schemas/src/multiVariableText/propPanel.ts`:\n\n```typescript\n// BEFORE (vulnerable):\npara.innerHTML =\n  typingInstructions +\n  ` <code style=\"color:${safeColorValue}; font-weight:bold;\">{` +\n  sampleField +\n  '}</code>';\n\n// AFTER (safe):\npara.appendChild(document.createTextNode(typingInstructions + ' '));\nconst codeEl = document.createElement('code');\ncodeEl.style.color = safeColorValue;\ncodeEl.style.fontWeight = 'bold';\ncodeEl.textContent = `{${sampleField}}`;\npara.appendChild(codeEl);\n```\n\nThis ensures that i18n label values are always treated as text content, never parsed as HTML, regardless of their source.",
            "references": [
                {
                    "reference_url": "https://github.com/pdfme/pdfme",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "4.4",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme"
                },
                {
                    "reference_url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-xgx4-2wgv-4jhm",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "4.4",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-xgx4-2wgv-4jhm"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-xgx4-2wgv-4jhm",
                    "reference_id": "GHSA-xgx4-2wgv-4jhm",
                    "reference_type": "",
                    "scores": [],
                    "url": "https://github.com/advisories/GHSA-xgx4-2wgv-4jhm"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/56889?format=api",
                    "purl": "pkg:npm/%40pdfme/schemas@5.5.10",
                    "is_vulnerable": false,
                    "affected_by_vulnerabilities": [],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540pdfme/schemas@5.5.10"
                }
            ],
            "aliases": [
                "GHSA-xgx4-2wgv-4jhm"
            ],
            "risk_score": null,
            "exploitability": null,
            "weighted_severity": null,
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-kxsk-fpuj-5uaw"
        },
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/22765?format=api",
            "vulnerability_id": "VCID-p7h9-8dum-cybm",
            "summary": "Cross-Site Scripting (XSS) via Select Schema Option Value Injection in @pdfme/schemas\n## 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, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n```",
            "references": [
                {
                    "reference_url": "https://github.com/pdfme/pdfme",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "6.1",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme"
                },
                {
                    "reference_url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-qq9g-96v4-m3cj",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "6.1",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-qq9g-96v4-m3cj"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-qq9g-96v4-m3cj",
                    "reference_id": "GHSA-qq9g-96v4-m3cj",
                    "reference_type": "",
                    "scores": [],
                    "url": "https://github.com/advisories/GHSA-qq9g-96v4-m3cj"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/57060?format=api",
                    "purl": "pkg:npm/%40pdfme/schemas@5.5.9",
                    "is_vulnerable": true,
                    "affected_by_vulnerabilities": [
                        {
                            "vulnerability": "VCID-kxsk-fpuj-5uaw"
                        }
                    ],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540pdfme/schemas@5.5.9"
                }
            ],
            "aliases": [
                "GHSA-qq9g-96v4-m3cj"
            ],
            "risk_score": null,
            "exploitability": null,
            "weighted_severity": null,
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-p7h9-8dum-cybm"
        },
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/21614?format=api",
            "vulnerability_id": "VCID-r9pa-fc4s-p3a2",
            "summary": "Cross-Site Scripting (XSS) via SVG Schema innerHTML Injection in @pdfme/schemas\n## Summary\n\nThe SVG schema plugin in `@pdfme/schemas` renders user-supplied SVG content using `container.innerHTML = value` without any sanitization, enabling arbitrary JavaScript execution in the user's browser.\n\n## Details\n\nIn `packages/schemas/src/graphics/svg.ts`, line 87, the SVG schema's `ui` renderer assigns raw SVG markup directly to `innerHTML` when in viewer mode or form mode with `readOnly: true`:\n\n```typescript\n// svg.ts, line 81-94 (non-editable rendering path)\n} else {\n  if (!value) return;\n  if (!isValidSVG(value)) {\n    rootElement.appendChild(createErrorElm());\n    return;\n  }\n  container.innerHTML = value;  // <-- VULNERABLE: unsanitized SVG injected into DOM\n  const svgElement = container.childNodes[0];\n  if (svgElement instanceof SVGElement) {\n    svgElement.setAttribute('width', '100%');\n    svgElement.setAttribute('height', '100%');\n    rootElement.appendChild(container);\n  }\n}\n```\n\nThe `isValidSVG()` function (lines 11-37) only validates that the string contains `<svg` and `</svg>` tags and passes `DOMParser` well-formedness checks. It does NOT strip or block:\n- `<script>` tags embedded in SVG\n- Event handler attributes (`onload`, `onerror`, `onclick`, etc.)\n- `<foreignObject>` elements containing HTML with event handlers\n- `<animate>` / `<set>` elements with `onbegin` / `onend` handlers\n- SVG `<use>` elements referencing malicious external resources\n\nAll of these are valid SVG and pass `isValidSVG()`, but execute JavaScript when inserted via `innerHTML`.\n\n## Attack Vectors\n\n### 1. Malicious Template (readOnly SVG schema)\nAn attacker crafts a template JSON with a readOnly SVG schema containing a malicious `content` value. When loaded into the pdfme Form or Viewer component, the SVG executes JavaScript.\n\n### 2. Application-Supplied Inputs + Viewer\nIf an application uses the pdfme Viewer component and passes user-controlled data as inputs for a non-readOnly SVG schema, the attacker's SVG flows directly to `innerHTML`.\n\n## Proof of Concept\n\nLoading the following template into a pdfme Form or Viewer 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_svg\",\n      \"type\": \"svg\",\n      \"content\": \"<svg xmlns='http://www.w3.org/2000/svg' onload='alert(document.domain)'><rect width='100' height='100' fill='red'/></svg>\",\n      \"readOnly\": true,\n      \"position\": { \"x\": 20, \"y\": 20 },\n      \"width\": 80,\n      \"height\": 40\n    }\n  ]]\n}\n```\n\nAdditional payloads that bypass `isValidSVG()` and execute JavaScript:\n\n```svg\n<!-- Via foreignObject -->\n<svg xmlns=\"http://www.w3.org/2000/svg\"><foreignObject width=\"200\" height=\"60\"><body xmlns=\"http://www.w3.org/1999/xhtml\"><img src=\"x\" onerror=\"alert(1)\"/></body></foreignObject></svg>\n\n<!-- Via animate onbegin -->\n<svg xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100\" height=\"100\"><animate attributeName=\"x\" values=\"0\" dur=\"0.001s\" onbegin=\"alert(1)\"/></rect></svg>\n```\n\n## Impact\n\nAn attacker who can supply a malicious template (via file upload, shared template URL, multi-tenant template storage, or `updateTemplate()` API) can execute arbitrary JavaScript in the context of any user who views or fills the template. This enables:\n- Session hijacking via cookie/token theft\n- Keylogging of form inputs (including sensitive data being entered into PDF forms)\n- Phishing attacks by modifying the rendered page\n- Data exfiltration from the application\n\nThe attack is particularly concerning for multi-tenant SaaS applications using pdfme where templates may be user-supplied.\n\n## Suggested Fix\n\nSanitize SVG content before DOM insertion using DOMPurify or a similar library:\n\n```typescript\nimport DOMPurify from 'dompurify';\n\n// Replace line 87:\ncontainer.innerHTML = DOMPurify.sanitize(value, { USE_PROFILES: { svg: true } });\n```\n\nAlternatively, parse the SVG via `DOMParser`, strip all script elements and event handler attributes, then append the sanitized DOM nodes.",
            "references": [
                {
                    "reference_url": "https://github.com/pdfme/pdfme",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "6.1",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme"
                },
                {
                    "reference_url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-87v3-4cfp-cm76",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "6.1",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
                        },
                        {
                            "value": "MODERATE",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/pdfme/pdfme/security/advisories/GHSA-87v3-4cfp-cm76"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-87v3-4cfp-cm76",
                    "reference_id": "GHSA-87v3-4cfp-cm76",
                    "reference_type": "",
                    "scores": [],
                    "url": "https://github.com/advisories/GHSA-87v3-4cfp-cm76"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/57060?format=api",
                    "purl": "pkg:npm/%40pdfme/schemas@5.5.9",
                    "is_vulnerable": true,
                    "affected_by_vulnerabilities": [
                        {
                            "vulnerability": "VCID-kxsk-fpuj-5uaw"
                        }
                    ],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540pdfme/schemas@5.5.9"
                }
            ],
            "aliases": [
                "GHSA-87v3-4cfp-cm76"
            ],
            "risk_score": null,
            "exploitability": null,
            "weighted_severity": null,
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-r9pa-fc4s-p3a2"
        }
    ],
    "fixing_vulnerabilities": [],
    "risk_score": null,
    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540pdfme/schemas@3.1.4"
}