While contributing to an open-source project built on top of Quill, I discovered and fixed a Cross-Site Scripting (XSS) vulnerability in its HTML export functionality.
Although this issue surfaced in a specific project, the root cause is relevant to anyone working with Quill or similar rich-text editors, especially when exporting user content as HTML.
This post covers:
• what went wrong,
• how the vulnerability could be exploited,
• and how it was fixed without breaking backward compatibility.
⸻
🚨 The Problem: Unsafe HTML Export
The project exposes a method to export editor content as semantic HTML, roughly like:
const html = quill.getSemanticHTML();
This is a common pattern in Quill-based systems — exporting editor content for:
• documentation
• previews
• dashboards
• emails
The issue was that user-controlled content was rendered into HTML without proper escaping in some export paths.
As a result, carefully crafted input could lead to arbitrary JavaScript execution when the exported HTML was rendered.
⸻
🧨 Attack Surface
The vulnerability affected two specific Quill embed formats.
1️⃣ Formula embeds
• Formula values were rendered directly into HTML
• No escaping was applied
• Malicious LaTeX-like input could inject raw HTML / JavaScript
2️⃣ Video embeds
• Video URLs were embedded into:
• href attributes
• visible text content
• URLs were not escaped
• Crafted input could break out of attributes and inject scripts
⸻
💥 Example Exploit
quill.insertEmbed(
0,
'formula',
'alert(document.cookie)'
);
const html = quill.getSemanticHTML();
// ❌ Script tag ends up unescaped in the HTML output
This creates a stored XSS scenario — particularly risky when exported HTML is later rendered in trusted contexts.
⸻
🛠️ The Fix
The fix was intentionally minimal and conservative, following Quill’s existing architecture:
• Escape all HTML special characters before rendering user-controlled content
• Apply escaping consistently to:
• formula values
• video URLs (both attributes and text nodes)
• Reuse the project’s existing escapeText() utility instead of introducing new sanitizers
Escaped characters include:
& < > ' "
This ensures user input can never be interpreted as executable markup during export.
⸻
🧪 Testing & Verification
To validate the fix, I added 14 focused security tests covering both embed types.
The tests verify prevention of:
• HTML tag injection
• Attribute injection
• Quote breaking
• Event handler injection
Results:
• ✅ All existing tests pass
• ✅ No runtime behavior changes
• ✅ No API changes
⸻
📦 Security Details
• CVE: CVE-2025-15056
• CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation)
• Severity: Low
• Affected versions: ≤ 2.0.3
• Patched versions: Provided by this fix
The fix is suitable for a security patch release and can be safely backported.
🤝 Final Thoughts
Open-source security work doesn’t always involve complex exploits or cryptography.
Often, it’s about carefully examining trust boundaries — especially where user content crosses from data into markup.
If you maintain or build on top of Quill and:
• export editor content as HTML,
• render exported HTML in user-facing systems,
• or build custom embeds,
…it’s worth auditing those paths carefully.
Thanks for reading 🙌
Happy to discuss similar issues or review HTML export pipelines.
Top comments (1)
Happy to discuss similar issues or review HTML export pipelines in rich-text editors. Feedback welcome.