Skip to content

[Bug] Examples can render user/model text as HTML #833

Description

@AK-Khan02

Several examples currently use DOM HTML sinks for strings that come from users, model output, or page context. That means HTML-looking text can be parsed as markup instead of being displayed as plain text.

This is in examples rather than core library code, but examples are copied into real integrations, so the examples should model a safe trust boundary: generated text, user prompts, and page-derived context should be treated as plain text unless an example explicitly opts into sanitized HTML rendering.

Issues Found

  1. User/model text rendered through HTML sinks

    The simple chat and Chrome extension examples used patterns like innerHTML = ... and insertAdjacentHTML(...) for strings that can include user prompts, model output, or page-derived context. This can turn HTML-looking text into markup instead of displaying it as text.

  2. The bug had already reappeared once

    This is a class issue, not just a one-off cleanup:

  3. The original prevention guard needed to cover React examples too

    The examples include React/TSX files. A robust lint rule should cover *.js, *.jsx, *.ts, and *.tsx, and should also reject React's equivalent HTML sink, dangerouslySetInnerHTML.

  4. The WebGPU Chrome extension passed raw page HTML as context

    examples/chrome-extension-webgpu-service-worker/src/content.js used document.body.innerHTML when passing active-tab context. The sibling Chrome extension uses document.body.innerText. For this example, visible page text is the safer default and better matches the plain-text trust boundary.

  5. Copy button event listeners were registered repeatedly during streaming

    In both Chrome extension popup examples, the copy-answer click listener was registered from inside updateAnswer(). Since updateAnswer() runs for streamed chunks, this could add many duplicate listeners during a single answer.

  6. Adjacent example correctness issues were found while fixing this

    • Several examples displayed [System Initalize] instead of [System Initialize].
    • The Next simple chat component rendered the literal string ${value.text} instead of the message text.

Expected Fix

A robust fix should:

  1. Render model/user/page-controlled strings with textContent, JSX text, or direct DOM node construction, not HTML parsing.
  2. Preserve line breaks with CSS such as white-space: pre-wrap rather than converting newlines to <br> and assigning innerHTML.
  3. Add examples-only lint enforcement so future PRs cannot reintroduce innerHTML / outerHTML assignments, insertAdjacentHTML() calls, or React dangerouslySetInnerHTML.
  4. Register copy-answer event listeners once, outside streamed answer updates.
  5. Keep page context extraction in examples on visible text unless a future example explicitly demonstrates sanitized HTML handling.

This does not require an HTML sanitizer because these examples do not need to support model-rendered HTML. The safer default is to treat generated and user-provided strings as plain text.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions