Text Editor - Security
How the Text Editor sanitizes untrusted HTML, URLs, and CSS, what guarantees it makes, and what remains the host application's responsibility.
#Overview
The TextEditor treats all incoming content — value / v-model, setValue, paste, drag-drop, and plugin replaceSelection(html) — as untrusted. Sanitization happens at the trust boundary (schema parse) and again on serialization, so a malicious value cannot execute script in the editor and cannot round-trip a dangerous attribute back into the emitted value.
The editor is built on ProseMirror. ProseMirror's schema drops unknown nodes (so <script>, <iframe>, <object>, <embed>, <svg> never enter the document), but it does not validate attribute values. The TextEditor adds that layer.
#What is sanitized
#Inert HTML parsing
Untrusted HTML is parsed inside an inert <template> fragment, never via element.innerHTML on a live element. In a <template>, the browser does not fetch resources or fire load/error events — so <img src=x onerror=...> cannot run during parsing, before the schema strips the handler.
#URL schemes (links and images)
href and src are validated against a scheme allowlist:
- Allowed:
http:,https:,mailto:,tel:,ftp:, relative URLs, and fragments. - Allowed for images only:
data:image/*exceptdata:image/svg+xml(SVG can carry script). - Rejected:
javascript:,vbscript:,data:text/html, and any other scheme — including obfuscated variants using control characters, tabs, newlines, or zero-width characters (e.g.java	script:).
An unsafe href drops the link mark but keeps the text. An unsafe image src drops the image node. The link target is restricted to _blank / _self / _parent / _top, and rel="noopener noreferrer" is always emitted.
#CSS values
Inline style values (color, background-color, font-family, font-size, table cell background) are validated on both parse and serialize. Rejected: url(...), expression(...), behavior, -moz-binding, @import, declaration separators (;, }), comments (/* */), and markup-breakout characters (<, >, backtick, backslash).
print() renders serialized (already-sanitized) HTML into a sandboxed iframe (sandbox="allow-modals allow-same-origin", without allow-scripts).
#Host responsibilities
The editor protects its own surfaces. The following remain the host application's responsibility:
getJSON()is data, not markup. It returns raw attribute values. When the host persists and later renders it directly, sanitize before inserting into the DOM, or render viagetHTML()which goes through the sanitizing serializer.mention.datais an opaque payload the host supplies. It round-trips as JSON; when read back into a host-owned DOM sink, treat it as untrusted.- Uploads. The client-side
allowedImageTypes/allowedDocumentTypescheck trusts the browser-reportedfile.type/extension, which is spoofable. The upload server must re-validate the file type and content, and must serve uploaded files with a safeContent-Type/Content-Disposition(a stored SVG served asimage/svg+xmlcan execute script in the viewer's origin). - Content Security Policy. A strict CSP is recommended as defense in depth.