// Shared HTML-escaping helpers. All Sentry / MCP / agent-derived values are
// interpolated into HTML string templates, so every dynamic text or attribute
// value MUST pass through here before it reaches the webview.
const HTML_ENTITIES = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
}
/** Escape a value for use as HTML text or a double/single-quoted attribute. */
export function escapeHtml(value) {
if (value == null) return ''
return String(value).replace(/[&<>"']/g, (ch) => HTML_ENTITIES[ch])
}
/**
* Return a safe href: only http(s) URLs are allowed through, everything else
* (javascript:, data:, malformed, etc.) collapses to '#'. The result is still
* attribute-escaped for embedding.
*/
export function safeHref(value) {
if (value == null) return '#'
const raw = String(value).trim()
try {
const url = new URL(raw)
if (url.protocol === 'http:' || url.protocol === 'https:') {
return escapeHtml(url.href)
}
} catch {
// not an absolute URL — fall through
}
return '#'
}
/**
* Serialize a value as JSON safe to embed inside an inline