high · 8.1Sep 10, 2026

@eigenpal/docx-editor-core: CSS Injection and Print-Time XSS via Unescaped Font Name

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A crafted .docx file with a malicious embedded font name injects attacker-controlled CSS into any page that opens the file, and escalates to full script execution the moment a user clicks Print.

Package@eigenpal/docx-editor-core
Ecosystemnpm
Affected<= 1.8.2
Fixed in1.8.3
@eigenpal/docx-editor-core: CSS Injection and Print-Time XSS via Unescaped Font Name

The problem

The core parser reads font-family names from word/fontTable.xml and drops them verbatim into an injected @font-face <style> block. No sanitisation or escaping is applied, so any string that is legal XML is also legal as the injected value.

On open, a crafted name applies page-wide CSS with zero user interaction: overlay phishing, attribute-selector credential exfiltration, or tracking beacons are all viable. Clicking Print escalates the impact: the same font name is interpolated into document.write() inside the print window, where closing the <style> tag with </style> lets arbitrary HTML, including <script>, execute in the embedder's origin.

Proof of concept

A working proof-of-concept for this issue in @eigenpal/docx-editor-core, with the exact payload below.

javascript
<!-- word/fontTable.xml inside the crafted .docx -->
<w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:font w:name="Evil}</style><img src=x onerror=alert(document.domain)><style>/*">
    <w:charset w:val="00"/>
  </w:font>
</w:fonts>

<!-- What the library injected on open (CSS injection only): -->
<style>
@font-face {
  font-family: 'Evil}</style><img src=x onerror=alert(document.domain)><style>/*';
  src: local('Evil...</style>

<!-- What the library injected on Print (document.write, full XSS): -->
document.write(
  '<style>@font-face { font-family: "Evil}</style><img src=x onerror=alert(document.domain)><style>/*"; }</style>'
);

The root cause is CWE-79: user-controlled data (the XML font name) flows directly into two HTML sinks with no escaping. In the <style> injection path, the string }</style> closes the CSS block and the <style> tag simultaneously, promoting the payload from CSS context into HTML context.

In the document.write path the same trick works because document.write parses its argument as a full HTML stream, so a closing </style> inside the string terminates the tag and allows inline event handlers to execute.

The fix in 1.8.3 CSS-escapes font names before interpolation, quoting or backslash-escaping characters including <, >, ", ', and CSS newlines. The print window was also rewritten to use DOM APIs (createElement, appendChild) instead of document.write, eliminating the string-parsing sink entirely.

The fix

Upgrade @eigenpal/docx-editor-core (and the React/Vue adapter that depends on it) to version 1.8.3. In 1.8.3 font names are CSS-escaped before interpolation and the print window is built with DOM APIs, closing both injection paths. No workaround exists in 1.8.2 or earlier.

Reporter not attributed.

References: [1][2]

Related research