Trusted HTML and expression security
The simplest safe default is to display user content as text and keep Brio templates under your application’s control.
Treat expressions as application code
Section titled “Treat expressions as application code”Directive expressions are ordinary strict-mode JavaScript, compiled and cached
at runtime with new Function, without with.
Expressions are not a sandbox. Never render untrusted content as a Brio template or directive expression. Runtime compilation is incompatible with a strict CSP that disallows
unsafe-eval.
Choose text or markup deliberately
Section titled “Choose text or markup deliberately”b-text evaluates its source expression and writes the result as literal text.
Tags and ${...} inside returned values are never interpreted. Authored HTML
interpolation still evaluates once; its returned values also remain literal.
b-html parses markup without activating Brio directives, interpolation, or
contextual scripts. Both sources remain reactive:
<p b-text="$data.userText"></p><p>${$data.userText}</p><div b-html="$data.staticMarkup"></div>Use b-text for untrusted/user-provided text. Inert HTML is not sanitized:
HTML attributes and other browser behavior can still be dangerous.
| Content | Brio behavior |
|---|---|
b-text or the value returned by authored ${...} interpolation | Literal text; tags and ${...} in the returned value are not interpreted. |
b-html | Parses HTML, but leaves Brio directives, interpolation, and contextual scripts inactive. Not sanitization. |
b-html.hydrate | Activates a Brio template only when explicitly marked with trustedHTML(). |
Activate only trusted dynamic templates
Section titled “Activate only trusted dynamic templates”To activate a dynamic Brio template, explicitly use b-html.hydrate with a
trustedHTML() value. Ordinary strings, null, and undefined are rejected in this
mode. Use trustedHTML('') to clear an active template. Each replacement
disposes previous effects, listeners, refs, and owned resources before hydrating
the new content. A value without the trust marker is rejected before replacing
existing content. Authored b-template and managed insertion are unchanged.
import { createApp, trustedHTML } from '@jnreis/briojs';
createApp({ state: () => ({ content: '<em>Trusted</em>' }) }).mount('#content');<div id="content" b-html.hydrate="trustedHTML($data.content)"></div>Only call trustedHTML() with templates you trust as application code. It is a
trust assertion, not sanitization or a security boundary. Sanitizing ordinary
HTML does not necessarily make it safe to execute as a Brio template.