nexu-io/open-design · error · Error

event handler attributes are not supported in live artifact

Error message

event handler attributes are not supported in live artifact previews

What it means

Thrown by validateHtmlTemplateV1Security when the template matches /\son[a-z][a-z0-9_-]*\s*=/i — any inline event-handler attribute such as onclick=, onload=, on-error=, on-input=. Inline handlers are the simplest XSS vector: they execute attacker-controlled strings as JavaScript on DOM events. Live artifact previews forbid them so that interpolated data cannot become executable code.

Source

Thrown at apps/daemon/src/live-artifacts/render.ts:34

const TEMPLATE_INTERPOLATION = /{{\s*([^{}]+?)\s*}}/g;
const RAW_TEMPLATE_INTERPOLATION = /{{{[^{}]*}}}|{{\s*&[^{}]*}}/;
const TEMPLATE_PATH = /^(?:data|[A-Za-z_][A-Za-z0-9_]*)(?:\.(?:[A-Za-z_][A-Za-z0-9_-]*|\d+))*$/;
// `data-od-repeat="item in data.items"` — one loop variable over one `data.*` array.
const REPEAT_DIRECTIVE = /\s*\bdata-od-repeat\s*=\s*"([^"]*)"/i;
const REPEAT_DIRECTIVE_SPEC = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(data(?:\.(?:[A-Za-z_][A-Za-z0-9_-]*|\d+))*)\s*$/;
const EXECUTABLE_TEMPLATE_PATTERNS: Array<{ pattern: RegExp; message: string }> = [
  { pattern: /<\s*script\b/i, message: 'script elements are not supported in live artifact previews' },
  { pattern: /<\s*iframe\b/i, message: 'iframe elements are not supported in live artifact previews' },
  { pattern: /\bsrcdoc\s*=/i, message: 'srcdoc attributes are not supported in live artifact previews' },
  { pattern: /\son[a-z][a-z0-9_-]*\s*=/i, message: 'event handler attributes are not supported in live artifact previews' },
  { pattern: /(?:href|src|action|formaction)\s*=\s*['"]?\s*javascript\s*:/i, message: 'javascript: URLs are not supported in live artifact previews' },
  { pattern: /\bdata-od-(?:html|raw|bind-html)\b/i, message: 'raw HTML insertion directives are not supported' },
];

export function validateHtmlTemplateV1Security(templateHtml: string): void {
  for (const { pattern, message } of EXECUTABLE_TEMPLATE_PATTERNS) {
    if (pattern.test(templateHtml)) throw new Error(message);
  }
}

export function escapeHtmlTemplateValue(value: unknown): string {
  return String(value)
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;');
}

/**
 * A binding resolver for one scope. Given a trimmed binding path (e.g.
 * `data.title` or a loop variable path like `item.label`) it returns the
 * already-escaped scalar string to substitute, or throws for an unsupported
 * path. Loop scopes delegate non-matching heads (including `data.*`) to their
 * parent so global bindings keep working inside a repeat.

View on GitHub (pinned to 5be4028344)

Solutions

  1. Remove the on* attribute; live artifacts do not run JavaScript handlers.
  2. Express desired behavior through the template's data bindings and repeat directives, not client-side event code.

Example fix

// before
<template><button onclick='doX()'>{{data.label}}</button></template>
// after
<template><button>{{data.label}}</button></template>
Defensive patterns

Strategy: validation

Validate before calling

function assertNoInlineHandlers(html: string): void {
  if (/\son[a-z][a-z0-9_-]*\s*=/i.test(html)) throw new Error('inline event handlers not allowed');
}

Type guard

function isEventHandlerFree(html: string): boolean {
  return !/\son[a-z][a-z0-9_-]*\s*=/i.test(html);
}

Try / catch

try { validateHtmlTemplateV1Security(tpl); } catch (e) { throw e; }

Prevention

When it happens

Trigger: Any element carries an on* attribute: <button onclick=...>, <body onload=...>, <img onerror=...>, <input onfocus=...>; matches case-insensitively and tolerates whitespace around the =.

Common situations: Model adds interactivity via onclick handlers copied from a tutorial; developer prototypes form validation with oninput; image fallback pattern <img onerror=...>; misunderstanding that live artifacts have no event layer.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/ca6405a3c12c1631. Report an issue: GitHub.