Security model
inspectable sits on a boundary: it reads values produced by code you may not trust and draws them on a page you do.
What inspectable guarantees
Section titled “What inspectable guarantees”- Rendering never interprets text as markup. Every label, key, string, function source and error stack is set with
textContent; nothing is assigned throughinnerHTMLor an equivalent sink, and no URL from a preview is ever loaded. A preview built from hostile data renders as inert text (covered by the “hostile strings render as text” test). - Previews are plain data. The output contains only strings, finite numbers, booleans, arrays and plain objects, so
it is safe to
structuredClone,JSON.stringifyandpostMessage. - Own getters are not invoked. Accessor properties are reported as
getterwithout calling them (example 02). - A throwing value can’t sink the preview. Any exception while reading a value (a revoked Proxy, a throwing trap)
becomes an
unreadablenode. - Output is bounded by
depth,maxEntries,maxStringandmaxSource.
What is still yours
Section titled “What is still yours”- Serializing runs the value’s realm’s own code in a few places. Proxy traps (
ownKeys,getOwnPropertyDescriptor,getPrototypeOf) run when a Proxy is previewed, a prototype’sconstructorgetter (if someone defined one) runs when the class name is read, andFunction.prototype.toStringruns on functions. Serialize inside the realm that already runs the untrusted code (the Worker or iframe), never by pulling the value into your page first. - Handles keep values alive until
release(). A long-lived inspector that never releases is a memory leak by design; scope handles per cell or per result. textis a description, not a parser-grade representation. Don’tevalor re-parse it.
What that means in practice
Section titled “What that means in practice”- The boundary is the realm, not inspectable. inspectable doesn’t sandbox anything. Put the untrusted code and the
serializer in a Worker or a sandboxed iframe (for example with andbox), and let only previews cross. The
page renders them with
<value-inspector>, which can’t be made to run anything. - Bounded output is not bounded time. The limits cap what is returned, not how long a hostile Proxy trap takes to run. A trap that loops forever hangs the realm doing the serializing, which is one more reason that realm should be one you can terminate.
- A preview can still lie. It shows what the value reported about itself: a Proxy can present any keys it likes, and
namecomes from the value’s own constructor. Treat what it says as untrusted text, which is exactly how it is rendered.