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.

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/* except data: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&#9;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

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 via getHTML() which goes through the sanitizing serializer.
  • mention.data is 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 / allowedDocumentTypes check trusts the browser-reported file.type/extension, which is spoofable. The upload server must re-validate the file type and content, and must serve uploaded files with a safe Content-Type / Content-Disposition (a stored SVG served as image/svg+xml can execute script in the viewer's origin).
  • Content Security Policy. A strict CSP is recommended as defense in depth.