nexu-io/open-design · error · Error
javascript: URLs are not supported in live artifact previews
Error message
javascript: URLs are not supported in live artifact previews
What it means
Thrown by validateHtmlTemplateV1Security when an href, src, action, or formaction attribute is assigned a javascript: URL (e.g. href="javascript:..."). javascript: URLs execute their payload as script when the user activates the link/form, so they are blocked at template-validation time even though interpolation is otherwise permitted.
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('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''');
}
/**
* 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
- Replace javascript: URLs with real https:// (or other safe scheme) URLs.
- If you need behavior, it cannot live in the URL; live artifacts have no script execution at all.
Example fix
// before
<template><a href="javascript:doX()">{{data.label}}</a></template>
// after
<template><a href="{{data.url}}">{{data.label}}</a></template> Defensive patterns
Strategy: validation
Validate before calling
function assertNoJavascriptUrl(html: string): void {
if (/(?:href|src|action|formaction)\s*=\s*['"]?\s*javascript\s*:/i.test(html)) {
throw new Error('javascript: URLs not allowed');
}
} Type guard
function isJavascriptUrlFree(html: string): boolean {
return !/(?:href|src|action|formaction)\s*=\s*['"]?\s*javascript\s*:/i.test(html);
} Try / catch
try { validateHtmlTemplateV1Security(tpl); } catch (e) { throw e; } Prevention
- Use only http(s) URLs in href/src/action/formaction.
- Beware case variations — the filter is case-insensitive but authoring discipline still helps.
- Reject any URL string starting with 'javascript:' at the data layer too.
When it happens
Trigger: <a href="javascript:alert(1)">, <form action="javascript:...">, <button formaction="javascript:...">, <img src="javascript:...">; the scheme may be upper/mixed case and the value may be unquoted or single-quoted (regex tolerates both).
Common situations: Model writes an interactive link using the javascript: scheme (legacy pattern); developer copies a 1990s-style anchor; intent to test the security filter; case-variation attempt (JAVASCRIPT:) to evade naive matchers (this regex is case-insensitive so it still catches).
Related errors
- script elements are not supported in live artifact previews
- iframe elements are not supported in live artifact previews
- srcdoc attributes are not supported in live artifact preview
- event handler attributes are not supported in live artifact
- raw HTML insertion directives are not supported
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/d5e5dca9f30a89a9.
Report an issue: GitHub.