or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcore-sanitization.mdhooks.mdindex.mdvalidation-utilities.md
tile.json

hooks.mddocs/

Hooks

DOMPurify's hook system provides an event-driven architecture for extending sanitization functionality with custom logic. Hooks allow you to intercept and modify the sanitization process at specific points, enabling custom validation, transformation, and logging.

Capabilities

Hook Management

Add, remove, and manage hook functions that execute at specific points during sanitization.

/**
 * Add a hook function to execute at a specific sanitization point
 * @param entryPoint - The sanitization event to hook into
 * @param hookFunction - Function to execute when the event occurs
 */
function addHook(entryPoint: HookName, hookFunction: HookFunction): void;

/**
 * Remove a specific hook function or the last added hook at an entry point
 * @param entryPoint - The sanitization event to remove hook from
 * @param hookFunction - Specific hook to remove (optional, removes last if not specified)
 * @returns The removed hook function
 */
function removeHook(entryPoint: HookName, hookFunction?: HookFunction): HookFunction | undefined;

/**
 * Remove all hooks at a specific entry point
 * @param entryPoint - The sanitization event to clear hooks from
 */
function removeHooks(entryPoint: HookName): void;

/**
 * Remove all hooks from all entry points
 */
function removeAllHooks(): void;

type HookName = 
  | 'beforeSanitizeElements'
  | 'afterSanitizeElements'
  | 'beforeSanitizeAttributes'
  | 'afterSanitizeAttributes'
  | 'uponSanitizeElement'
  | 'uponSanitizeAttribute'
  | 'beforeSanitizeShadowDOM'
  | 'afterSanitizeShadowDOM'
  | 'uponSanitizeShadowNode';

Usage Examples:

import DOMPurify from "dompurify";

// Add a logging hook
DOMPurify.addHook('beforeSanitizeElements', function(node, data, config) {
  console.log('Processing element:', node.nodeName);
});

// Add multiple hooks to same entry point
const validateHook = (node, data, config) => {
  // Custom validation logic
};
const logHook = (node, data, config) => {
  // Logging logic
};

DOMPurify.addHook('uponSanitizeElement', validateHook);
DOMPurify.addHook('uponSanitizeElement', logHook);

// Remove specific hook
DOMPurify.removeHook('uponSanitizeElement', validateHook);

// Remove all hooks at entry point
DOMPurify.removeHooks('beforeSanitizeElements');

// Clear all hooks
DOMPurify.removeAllHooks();

Element Processing Hooks

Hooks that execute during element processing phases of sanitization.

/**
 * Hook executed before each element is processed for sanitization
 */
type BeforeSanitizeElementsHook = (
  this: DOMPurify,
  currentNode: Node,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed after each element has been processed for sanitization
 */
type AfterSanitizeElementsHook = (
  this: DOMPurify,
  currentNode: Node,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed when an element is being evaluated for removal/keeping
 */
type UponSanitizeElementHook = (
  this: DOMPurify,
  currentNode: Node,
  hookEvent: {
    tagName: string;
    allowedTags: Record<string, boolean>;
  },
  config: Config
) => void;

Usage Examples:

// Log all elements being processed
DOMPurify.addHook('beforeSanitizeElements', function(node) {
  if (node.nodeType === 1) { // Element node
    console.log(`Processing: <${node.nodeName.toLowerCase()}>`);
  }
});

// Custom element validation
DOMPurify.addHook('uponSanitizeElement', function(node, data, config) {
  const tagName = data.tagName;
  
  // Allow custom elements with specific prefix
  if (tagName.startsWith('app-') && !data.allowedTags[tagName]) {
    data.allowedTags[tagName] = true;
  }
  
  // Block specific elements even if normally allowed
  if (tagName === 'iframe' && node.getAttribute('src')?.includes('untrusted.com')) {
    data.allowedTags[tagName] = false;
  }
});

// Post-processing cleanup
DOMPurify.addHook('afterSanitizeElements', function(node) {
  // Add custom attributes to elements after sanitization
  if (node.nodeType === 1 && node.nodeName === 'A') {
    node.setAttribute('rel', 'noopener');
  }
});

Attribute Processing Hooks

Hooks that execute during attribute processing phases of sanitization.

/**
 * Hook executed before attributes are processed on an element
 */
type BeforeSanitizeAttributesHook = (
  this: DOMPurify,
  currentNode: Element,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed after attributes have been processed on an element
 */
type AfterSanitizeAttributesHook = (
  this: DOMPurify,
  currentNode: Element,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed when an attribute is being evaluated for removal/keeping
 */
type UponSanitizeAttributeHook = (
  this: DOMPurify,
  currentNode: Element,
  hookEvent: {
    attrName: string;
    attrValue: string;
    keepAttr: boolean;
    allowedAttributes: Record<string, boolean>;
    forceKeepAttr?: boolean;
  },
  config: Config
) => void;

Usage Examples:

// Custom attribute validation
DOMPurify.addHook('uponSanitizeAttribute', function(node, data, config) {
  const { attrName, attrValue } = data;
  
  // Allow custom data attributes
  if (attrName.startsWith('data-app-')) {
    data.keepAttr = true;
    data.forceKeepAttr = true;
  }
  
  // Validate URL attributes
  if (attrName === 'href' && !attrValue.match(/^https?:\/\//)) {
    data.keepAttr = false;
  }
  
  // Transform attribute values
  if (attrName === 'target' && attrValue === '_blank') {
    data.attrValue = '_blank';
    // Also need to set rel="noopener" somewhere else
  }
});

// Attribute logging and analysis
DOMPurify.addHook('beforeSanitizeAttributes', function(node) {
  console.log(`Processing attributes for: <${node.nodeName.toLowerCase()}>`);
  for (let i = 0; i < node.attributes.length; i++) {
    const attr = node.attributes[i];
    console.log(`  ${attr.name}="${attr.value}"`);
  }
});

// Post-attribute processing
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
  // Add security attributes to links
  if (node.nodeName === 'A' && node.hasAttribute('href')) {
    node.setAttribute('rel', 'noopener noreferrer');
  }
});

Shadow DOM Hooks

Hooks that execute during Shadow DOM processing for Web Components.

/**
 * Hook executed before Shadow DOM content is processed
 */
type BeforeSanitizeShadowDOMHook = (
  this: DOMPurify,
  currentNode: DocumentFragment,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed after Shadow DOM content has been processed
 */
type AfterSanitizeShadowDOMHook = (
  this: DOMPurify,
  currentNode: DocumentFragment,
  hookEvent: null,
  config: Config
) => void;

/**
 * Hook executed for each node within Shadow DOM
 */
type UponSanitizeShadowNodeHook = (
  this: DOMPurify,
  currentNode: Node,
  hookEvent: null,
  config: Config
) => void;

Usage Examples:

// Shadow DOM processing
DOMPurify.addHook('beforeSanitizeShadowDOM', function(fragment) {
  console.log('Processing Shadow DOM fragment');
});

// Custom Shadow DOM node handling
DOMPurify.addHook('uponSanitizeShadowNode', function(node) {
  // Special handling for nodes inside Shadow DOM
  if (node.nodeType === 1) {
    console.log(`Shadow DOM element: ${node.nodeName}`);
  }
});

// Post-Shadow DOM processing
DOMPurify.addHook('afterSanitizeShadowDOM', function(fragment) {
  console.log('Shadow DOM processing complete');
  console.log(`Fragment contains ${fragment.childNodes.length} nodes`);
});

Hook Context and State

Understanding the execution context and state available in hook functions.

/**
 * Hook function execution context
 * - `this` refers to the DOMPurify instance
 * - Access to current sanitization config via `config` parameter
 * - Node being processed via `currentNode` parameter
 * - Hook-specific data via `hookEvent` parameter
 * - Can modify hookEvent properties to influence sanitization
 */

Usage Examples:

// Access DOMPurify instance and config in hooks
DOMPurify.addHook('uponSanitizeElement', function(node, data, config) {
  // `this` is the DOMPurify instance
  console.log('DOMPurify version:', this.version);
  
  // Access current configuration
  console.log('Return DOM mode:', config.RETURN_DOM);
  console.log('Allowed tags:', config.ALLOWED_TAGS);
  
  // Modify sanitization behavior through data object
  if (config.CUSTOM_VALIDATION_MODE) {
    // Custom logic based on configuration
  }
});

// State tracking across hook calls
let elementCount = 0;
DOMPurify.addHook('beforeSanitizeElements', function(node) {
  if (node.nodeType === 1) {
    elementCount++;
  }
});

DOMPurify.addHook('afterSanitizeElements', function(node) {
  console.log(`Processed ${elementCount} elements total`);
});

Advanced Hook Patterns

Common patterns and advanced techniques for using hooks effectively.

/**
 * Hook chaining: Multiple hooks on same entry point execute in order
 * Hook data modification: uponSanitize* hooks can modify behavior
 * Conditional hooks: Use config or node properties for conditional logic
 * Error handling: Hooks should handle errors to avoid breaking sanitization
 * Performance: Minimize work in hooks as they execute for every node/attribute
 */

Usage Examples:

// Hook chaining for multi-step processing
DOMPurify.addHook('uponSanitizeElement', function(node, data, config) {
  // Step 1: Basic validation
  console.log('Step 1: Basic validation');
});

DOMPurify.addHook('uponSanitizeElement', function(node, data, config) {
  // Step 2: Custom business logic
  console.log('Step 2: Custom validation');
});

// Conditional hook logic
DOMPurify.addHook('uponSanitizeAttribute', function(node, data, config) {
  const { attrName, attrValue } = data;
  
  // Different handling based on element type
  switch (node.nodeName) {
    case 'A':
      if (attrName === 'href') {
        // Link-specific validation
      }
      break;
    case 'IMG':
      if (attrName === 'src') {
        // Image-specific validation
      }
      break;
  }
});

// Error handling in hooks
DOMPurify.addHook('beforeSanitizeElements', function(node) {
  try {
    // Potentially dangerous operation
    someCustomValidation(node);
  } catch (error) {
    console.error('Hook error:', error);
    // Don't re-throw to avoid breaking sanitization
  }
});

// Performance-conscious hook
const allowedCustomElements = new Set(['app-header', 'app-footer']);
DOMPurify.addHook('uponSanitizeElement', function(node, data, config) {
  // Fast lookup instead of expensive operations
  if (allowedCustomElements.has(data.tagName)) {
    data.allowedTags[data.tagName] = true;
  }
});