nexu-io/open-design · error · Error

raw HTML insertion directives are not supported

Error message

raw HTML insertion directives are not supported

What it means

Thrown by validateHtmlTemplateV1Security when the template contains a data-od-html, data-od-raw, or data-od-bind-html attribute (regex /\bdata-od-(?:html|raw|bind-html)\b/i). These would be raw-HTML insertion directives that bypass HTML escaping; the render pipeline supports only escaped interpolation ({{...}}) and explicit safe directives, so any raw-insertion directive is rejected up front to keep the model from injecting unescaped markup.

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. Use plain {{data.field}} interpolation; values are HTML-escaped automatically.
  2. If the data genuinely contains HTML that must be rendered as markup, that is not supported by html_template_v1 — pre-render or strip tags before feeding the data.

Example fix

// before
<template><div data-od-html="{{data.body}}"></div></template>
// after
<template><div>{{data.body}}</div></template>
Defensive patterns

Strategy: validation

Validate before calling

function assertNoRawHtmlDirective(html: string): void {
  if (/\bdata-od-(?:html|raw|bind-html)\b/i.test(html)) {
    throw new Error('raw-HTML directives are not supported');
  }
}

Type guard

function isRawDirectiveFree(html: string): boolean {
  return !/\bdata-od-(?:html|raw|bind-html)\b/i.test(html);
}

Try / catch

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

Prevention

When it happens

Trigger: <div data-od-html="{{data.body}}">, <span data-od-raw="{{data.html}}">, <p data-od-bind-html="...">; appears in any case or with extra whitespace before the =.

Common situations: Model assumes a Vue/Angular-style v-html / ng-bind-html directive exists; developer expects to render markdown HTML output; misunderstanding that all {{...}} interpolation is already escaped and there is no opt-out.

Related errors


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