Portal Community

The 6-Layer Pipeline

1
DOMPurify (primary sanitiser)

Industry-standard HTML sanitiser. Parses the input into a DOM tree and strips disallowed nodes and attributes. Configured with ALLOWED_TAGS and ALLOWED_ATTR whitelists.

2
Custom sanitiser hooks

DOMPurify BEFORE_SANITIZE_ELEMENT and AFTER_SANITIZE_ATTRIBUTES hooks apply additional rules not covered by the standard whitelist. Strips javascript: in href values, data URIs in img src, and mXSS patterns.

3
Content Security Policy (CSP)

HTTP header Content-Security-Policy set by the Atlas Forms server prevents execution of inline scripts even if a script tag somehow reaches the DOM. script-src 'self' blocks all inline script execution.

4
Tag whitelist enforcement

After DOMPurify runs, a secondary tag-list check validates every element in the sanitised output against the 17-tag allowed list. Any element not in the list is replaced with its text content.

5
Event-handler stripping

All on* attribute names (onclick, onload, onerror, etc.) are stripped by a regex pass over the sanitised HTML string before it is inserted into the DOM. This is a final backstop for browser-specific DOMPurify bypasses.

6
Style injection blocking

All style attributes and <style> elements are removed. CSS injection can be used to exfiltrate data through background-image URLs or create phishing overlays. Blocking all inline styles is the safest approach.

DOMPurify Configuration

import DOMPurify from 'dompurify';

const ALLOWED_TAGS = [
  'p', 'br', 'hr', 'div', 'span',
  'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  'strong', 'em', 'u', 'code', 'pre',
  'ul', 'ol', 'li',
  'a', 'blockquote', 'img'
];

const ALLOWED_ATTR = [
  'href', 'target', 'rel',   // a
  'src', 'alt', 'width', 'height',  // img
  'class', 'id'
];

const config = {
  ALLOWED_TAGS,
  ALLOWED_ATTR,
  ALLOW_DATA_ATTR: false,
  ALLOW_UNKNOWN_PROTOCOLS: false,
  FORCE_BODY: true,
};

export function sanitizeHtml(dirty: string): string {
  return DOMPurify.sanitize(dirty, config);
}

Custom Hook: javascript: href Blocking

DOMPurify.addHook('afterSanitizeAttributes', (node) => {
  // Block javascript: protocol in href
  if (node.hasAttribute('href')) {
    const href = node.getAttribute('href') ?? '';
    if (/^javascript:/i.test(href.trim())) {
      node.removeAttribute('href');
    }
  }

  // Block data: URIs in img src (potential XSS vector)
  if (node.tagName === 'IMG' && node.hasAttribute('src')) {
    const src = node.getAttribute('src') ?? '';
    if (/^data:/i.test(src.trim())) {
      node.removeAttribute('src');
    }
  }

  // Force noopener on external links
  if (node.tagName === 'A' && node.getAttribute('target') === '_blank') {
    node.setAttribute('rel', 'noopener noreferrer');
  }
});

Blocked Attack Vectors (28+)

CategoryExample VectorBlocked By
Script injection<script>alert(1)</script>Layer 1 (DOMPurify tag whitelist)
Event handler<img onload="alert(1)">Layer 1 + Layer 5
JavaScript href<a href="javascript:alert(1)">Layer 2 (custom hook)
SVG script<svg><script>...</script></svg>Layer 1
Data URI<img src="data:text/html,<script>...">Layer 2 (custom hook)
Style injection<div style="background:url()">Layer 6
CSS import<style>@import url(evil.css)</style>Layer 6
mXSSMalformed HTML parsed differently by browsersLayer 1 + Layer 2
Object/embed<object data="evil.swf">Layer 1
Form hijack<form action="evil.com">Layer 1
Meta redirect<meta http-equiv="refresh">Layer 1
Base tag<base href="evil.com">Layer 1
Inline script CSP bypass<p></p><script>...</script>Layer 1 + Layer 3 (CSP)

Security Guarantees

Security Applies to Form-Author Content Only The html control is designed for content written by trusted form authors in Form Studio. If your application allows end-users to provide HTML content that is then stored in a form schema and rendered by the html control, you are bypassing the intended security boundary. Always validate and sanitise user-provided content on the server before it enters a form schema.