An AI answer that looks like a harmless Markdown document can still produce a dangerous page. Raw HTML may execute if a viewer trusts it, a misleading link may send a reader to the wrong destination, and a remote image may transmit viewing information without the reader clicking anything. Safe agent rendering keeps generated and retrieved text as untrusted data, converts only approved structures into browser elements, checks URLs separately, and treats external media loading as a deliberate privacy decision.
This article explains how to build that boundary, how to measure useful formatting without confusing it with security, and how to diagnose renderer failures. The worked 120-item helpdesk and 48-report classroom laboratories are fictional, with arithmetic shown so their denominators can be checked. These are engineering methods for AI-agent systems, not claims that artificial superintelligence currently exists.
Reading routes and contents
For developers: begin with the syntax, DOM, URL and media boundaries. For educators and reviewers: start with the fictional laboratory and changed-context transfer. For security teams: read the threat cases, acceptance criteria and limitations. Each linked section below can be reached directly.
- The output is untrusted even when the agent is helpful
- Define the reader’s real safety outcome
- A map of the five boundaries
- Why Markdown itself is not a browser sandbox
- Text nodes versus active markup
- The URL is more important than the coloured link label
- Remote images are privacy decisions
- Raw HTML needs an explicit policy
- The browser insertion point completes the trust boundary
- Code highlighting, math rendering and diagram plugins
- Citations must be visible and verifiable
- Incremental streaming must not produce provisional active nodes
- Accessible presentation is a correctness constraint
- A fictional 120-item preview laboratory
- Read the denominators before claiming a win
- Threat cases the test suite should actually cover
- Content security policy is a supporting layer
- Link previews are a separate fetch product
- Images and attachments owned by the application
- A safe link is not an approved transaction
- Changed-context transfer: a classroom report viewer
- A realistic failure diagnosis
- What a complete acceptance test proves
- Constraints that rendering cannot solve
- Frequently asked questions
- Evidence and practical companion routes
The output is untrusted even when the agent is helpful
Markdown is a useful way for an AI agent to present an answer, but its appearance as ordinary text must not be confused with permission to execute browser behaviour. A generated answer can include headings, lists, code fences, links, images and raw HTML. Some of those structures are visually harmless; others can activate a network request, navigate a person to a deceptive destination, or produce executable content if inserted into the page without a deliberate trust boundary. The safe design treats agent text and retrieved documents as content, not as an application developer’s instructions.
The practical answer is to keep generation, parsing, policy and rendering separate. The model produces a string. A Markdown parser recognises its structure. A renderer maps approved structures to safe text or DOM nodes, and an independent URL policy decides whether links and media can be activated. An HTML sanitizer or safe DOM interface handles any deliberately permitted HTML. The application, rather than the model, owns the decision that a particular operation is safe. This remains true if the model is unusually capable; no currently demonstrated artificial superintelligence is assumed here.
Define the reader’s real safety outcome
The goal is neither to remove all formatting nor to trust anything that resembles a nicely formatted report. The useful outcome is a readable answer that cannot acquire the authority of the surrounding product simply by supplying markup. The user should be able to read the meaning, copy code as data, inspect link destinations, and decide whether remote resources may load. The application must resist executable attributes and ambiguous URLs without secretly changing the substance of quoted evidence.
Write two acceptance criteria. First, untrusted text must not cause script execution, privileged navigation or an undisclosed external request through a renderer path. Second, ordinary headings, lists, emphasis, tables and code must remain understandable. A system that displays a blank page is not a successful assistant; a system that retains every active HTML feature is not a safe viewer. Security and fidelity are separate measurements, so the team should report both. For sensitive work, prefer text-only mode when the rich viewer cannot satisfy its policy.
A map of the five boundaries
Boundary one is origin: the model may combine instructions, retrieved pages and an uploaded file, each with different reliability. Boundary two is syntax: Markdown creates a structured representation, but syntax recognition is not authorization. Boundary three is HTML: several Markdown dialects permit raw HTML or pass it through. Boundary four is URL resolution: link text and link destination are different strings, and an image reference may automatically fetch its target. Boundary five is browser execution: the browser, extensions, site configuration and enabled APIs determine what happens after rendered content enters a live document.
Track each boundary with one responsible component and one failure signal. An input classifier alone cannot replace a correct renderer. Sanitizing after converting to HTML does not excuse a link policy that allows unsupported schemes. Blocking scripts does not settle privacy if an image tag fetches a unique tracking URL. Conversely, blocking all network references does not stop confusing evidence where a plausible citation points to the wrong site. A trustworthy preview needs both mechanical controls and understandable presentation.
Why Markdown itself is not a browser sandbox
Markdown’s simple characters make it feel safer than HTML. A line beginning with a hash looks like a heading, and backticks appear to mean literal code. But real Markdown implementations vary in their treatment of raw HTML, embedded links, autolinks, tables, images and extensions. Plugins can add diagrams or mathematical notation, sometimes by calling another renderer. The safety properties of one Markdown library cannot automatically be transferred to every plugin or to a different version of the same library.
Choose a documented flavour, pin and review parser versions, and disable extensions not needed by the reader task. Parse to an abstract syntax tree where possible; decide which node types can enter the document; build DOM nodes with explicit APIs. If you first build an HTML string, make sanitation the last transformation before a trusted insertion API, not the first stage before several more string rewrites. A dangerous pattern is sanitize, decorate, concatenate external snippets, and then place the new string into innerHTML. The later operations can undo the original protection.
Text nodes versus active markup
Putting a value into an ordinary text node is fundamentally different from asking a browser to interpret the value as markup. A user-supplied sequence that looks like an HTML tag becomes visibly printed text when inserted using a text-only DOM API. The same bytes can become active elements, events or network requests if fed into an HTML-parsing sink. The objective is not to identify a finite blacklist of bad words; it is to prevent data from being interpreted as code or privileged structure in the wrong context.
For a general answer, render paragraphs and inline code as text nodes. Create headings, lists and links as application-owned elements using known element names, not arbitrary element names taken from a generated response. Preserve the original raw text for copying or auditing when this is appropriate and permitted. For code examples, show the bytes inside a code block; never run, preview or import them simply because the agent labeled them ‘example’. A code fence is a display format, not consent to execution.
The URL is more important than the coloured link label
A rendered link can say ‘official guide’ while its actual destination is a different host. Displayed text is content and has no authority to attest to the target. Parse candidate URLs with a standards-compatible URL implementation, resolve relative references only against a known permitted base, and decide which schemes and destinations the application will allow. For a reading-focused answer, HTTPS links are usually a sensible default; schemes such as javascript and data are not equivalent to normal website navigation and should not be accepted merely because a parser made them clickable.
URL policy also needs context. An internal application link may be allowed only for a verified origin and known route. An email address can be displayed as plain text or use a separate explicitly approved mailto path. A local file link should not become an arbitrary browser file access. A hostname that resembles a trusted brand is not necessarily that brand. Show the destination domain where it materially helps readers, and avoid disguising external links as internal buttons. If the action moves money, shares a file or changes an account, the destination should be confirmed through the application’s transactional workflow, not inferred from Markdown.
Remote images are privacy decisions
Markdown image syntax can cause a browser to request a remote asset as soon as the preview is displayed. That request may reveal an IP address, a user agent, timing information, a referring page where policy allows it, or a one-time identifier embedded in the URL. An agent might accidentally include a web page’s tracking pixel in a summary, while a malicious source might include a URL intended to learn when a privileged person viewed the report. Neither case requires JavaScript execution for a privacy failure.
Make remote media a separate feature, not a side effect of Markdown parsing. Options include replacing the image with its alt text, requiring an explicit click, using a carefully designed fetch-and-proxy service subject to network and content policies, or serving approved attachments from controlled storage. A proxy must itself guard against internal-network requests, redirects and hostile content; it does not make every URL harmless. For many school, medical and business reports, a textual note saying that an external image was omitted is the least surprising behaviour.
Raw HTML needs an explicit policy
Some Markdown documents legitimately include superscripts, tables or custom inline elements; other documents include browser-active constructs. If the product does not need raw HTML, turn it off in the Markdown pipeline and show unsupported HTML literally. That policy is simpler to reason about than permitting arbitrary markup and trying to enumerate every dangerous element. If the product needs limited HTML, define an allowlist of elements and attributes suited to the reader job, use a maintained sanitizer and test it with the actual browser and parser integration.
OWASP’s XSS guidance distinguishes context-specific output encoding from HTML sanitization and notes that modifying sanitized output later can undermine the protection. A sanitizer is not a licence to pass the cleaned string into new libraries that reconstruct unsafe elements. Reject event-handler attributes and unsafe link schemes, and consider whether style attributes, embeds, forms or SVG are necessary at all. They are rarely needed to read a technical explanation. Keep rich trusted site components separate from the lower-trust material they display.
The browser insertion point completes the trust boundary
Every renderer eventually inserts nodes into a browser document or creates some other output representation. Where a platform offers a safe HTML insertion API that sanitizes the input, use it according to its documented compatibility and constraints. MDN documents Element.setHTML as a sanitizing interface, while also noting its limited browser availability. A cross-browser application should therefore check support and have a maintained safe fallback rather than assuming a particular method exists on every user’s device.
Do not treat a browser-only mitigation as the sole line of defence. A server-side HTML email renderer, PDF converter, mobile WebView or downloadable report may use different engines and different unsafe behaviours. Define the output contract for each surface separately. Also avoid serializing previously sanitized DOM to a string and reinserting it into a new context without re-sanitization: context may change what is safe. This is especially relevant when the same AI answer appears in a dashboard, a public post preview and an exported PDF.
Code highlighting, math rendering and diagram plugins
Code highlighters usually transform text into styled token spans, which can help readers inspect an example. They can also become unsafe if the implementation treats highlighted token content as trusted HTML. Mathematical renderers and diagram plugins add another transformation stage: a supposedly passive formula may be converted into rich DOM or SVG. The right question is not ‘does it have a good reputation?’ but ‘what exact output nodes does this version produce from arbitrary input, and which active features remain?’
For code, begin with a preformatted text node and apply only a reviewed highlighting pipeline. For formulas, choose a rendering mode with a clear security contract, disable unnecessary hyperlink or macro features, and test escape boundaries. For diagrams, prefer static image output from a controlled service or an approved structured graph representation rather than executing a general scripting language embedded in a document. Keep resource limits in place so a gigantic formula or recursive construct cannot freeze a viewer even if script injection is blocked.
Citations must be visible and verifiable
A safe renderer can still mislead a reader if an AI-generated citation is visually convincing but points nowhere useful. Distinguish a citation marker, a claimed source title and an actually resolved source document. If a source cannot be verified, display the limitation; do not decorate invented references with a trusted badge. A document that quotes other material should mark the boundaries of that quotation so that instructions or claims inside it do not silently inherit the assistant’s authority.
When linking to a source, verify the exact URL and its relevant scope before presenting it as support for a factual claim. The displayed link label should make a mismatch easier to notice, not harder. This connects preview security with evidence quality: malicious Markdown can exploit a renderer, while confident unsourced prose can exploit a reader’s trust. They require distinct controls. A model’s own statement that a URL is safe is not a substitute for application-level checking.
Incremental streaming must not produce provisional active nodes
Streaming answers arrive in fragments, and a Markdown parser may initially see half a link, half a code fence or an unfinished HTML construct. If the viewer converts every fragment immediately and attaches the resulting HTML to a privileged document, earlier text can be reinterpreted when the rest of the syntax arrives. A partially displayed link might change its destination after another chunk. A half-open code fence might alter whether later text is treated as code or markup.
Separate the provisional display from the committed rendering state. A plain-text streaming view can show fragments safely while a parser buffers enough information to produce stable, authorized nodes. When a segment is finalized, re-parse it under the same policy and replace the provisional view with its committed representation. If a reconnect repeats bytes or revises a message, the renderer must avoid duplicate links, stale click targets and mixed versions. See the companion on agent partial-answer streaming for commit boundaries and recovery.
Accessible presentation is a correctness constraint
Security controls sometimes remove a picture or a hyperlink, but the reader still needs to understand the answer. Image alt text can convey the intended information even when the remote image is blocked. Link text should describe the destination or action, rather than say ‘click here’ in a long sequence. A security notice should be announced accessibly, and keyboard focus must not jump unexpectedly when streamed content is replaced.
Screen-reader users deserve the same warning about external destinations as visual users. Do not use colour alone to distinguish a blocked link from a working link. Ensure code examples remain selectable and preserve line breaks without creating focus traps. When readers need a plain-text option, do not force it to lose essential headings or reading order. The neighbouring guide on accessible agent interfaces explains keyboard, screen-reader and consent requirements more widely; here the lesson is that safety must survive the transformation into the specific reader’s medium.
A fictional 120-item preview laboratory
Consider a fictional helpdesk that previews 120 independently submitted AI answer segments. The laboratory is a teaching model, not a report of measured performance. It contains 60 plain paragraphs, 22 link segments, 14 code samples, 12 segments containing limited HTML fragments and 12 segments with remote-image references. These five mutually exclusive categories sum to 60 + 22 + 14 + 12 + 12 = 120 segments. The exercise asks two different questions: how many can be displayed with their intended useful richness, and how many can be placed on the screen safely under the selected conservative policy?
The renderer accepts all 60 plain paragraphs and all 14 code samples as text. Of the 22 link segments, 18 have an approved HTTPS destination and 4 have destinations that policy holds for review or displays without an active link. The 12 limited HTML fragments are represented through approved plain text or sanitized elements. All 12 remote-image references become neutral placeholders with their alt descriptions. Thus 60 + 18 + 14 + 12 = 104 segments retain their intended useful display; 4 + 12 = 16 use a placeholder or non-clickable fallback. The sums reconcile to 120. The 104/120 useful-richness rate is 86.67% rounded to two decimals; 16/120 is 13.33%. Neither number proves that the renderer resisted every attack.
Read the denominators before claiming a win
It would be tempting to announce that the pipeline made 120 out of 120 segments ‘safe’. The example demonstrates a policy decision, not a penetration test. The safe-render outcome still needs adversarial test cases and independent verification. The 104 numerator measures preserved rich display; it is not the number of segments without a security incident. The 16 numerator measures constrained fallbacks; it is not the number of attacks. Both rates use the same 120-segment intake denominator because every segment is in exactly one class.
Suppose a later test discovers a risky media behaviour in one ostensibly approved branch. The original 104 number remains a description of user-visible richness, but any security acceptance claim must be reevaluated. A useful quality dashboard could report rich-display rate, blocked-URL count, sanitized-node count, remote-fetch count, readability checks and confirmed security failures separately. Do not merge them into a single vague percentage. Arithmetic can check a report’s internal consistency, while independent browser tests determine whether its technical conclusions are justified.
Threat cases the test suite should actually cover
Build tests from the output sinks outward. For ordinary paragraphs, include angle brackets, entity references, nested quotation marks and unusual Unicode. For links, include mixed casing, relative references, percent-encoding, credentials in the URL, visually similar hostnames, redirects and forbidden schemes. For images, include remote tracking paths, query identifiers, overlarge resources and unavailable hosts. For raw HTML, include event-handler attributes, embedded frames, forms and nested markup. For code fences, include copied HTML and JavaScript as text to prove the viewer does not execute a quoted example.
Run the cases on the actual target surfaces, not only against one sanitizer function in isolation. Browser differences, plugins, Markdown dialect options and content-security headers can change the result. A passing unit test that counts banned strings is weaker than an integration test that confirms no active element, navigation or prohibited network request occurred. Use a controlled test origin and make prohibited effects observable. Record renderer, parser, sanitizer and browser versions so a later regression has enough context to reproduce the failure.
Content security policy is a supporting layer
Content Security Policy can restrict script sources, image sources and other browser capabilities. It is valuable defence in depth, but it does not excuse injecting untrusted HTML through an unsafe API. A policy that allows remote images may still permit privacy leaks through image loads; a policy that restricts scripts does not prevent a reader from following a misleading link. The precise protection depends on deployed directives and browser behaviour. Treat CSP as one independently testable control, not a declaration that the viewer is safe.
Trusted Types can help centralise risky HTML-insertion paths in compatible environments, but it should not become a permissive default policy that rubber-stamps whatever the model generated. The safe default remains to create known DOM nodes and text nodes. Review every escape hatch deliberately, including third-party widgets and extensions. If another library requires raw HTML, document why the feature is necessary, sanitize for that destination and keep the insertion pathway under versioned tests.
Link previews are a separate fetch product
Many chat products generate a little card for a URL: title, picture, excerpt and icon. That convenience introduces a second subsystem. A preview fetcher may follow redirects, contact private network addresses or download unexpectedly large resources; fetched titles or descriptions can contain hostile text of their own. The preview is not just a visual rendering step. It is a network client handling untrusted destinations and then returning lower-trust metadata for another renderer to display.
Separate the approval to show a link from the authority to fetch it. A browser-facing preview should not ask a privileged backend to resolve arbitrary URLs without tight controls. Apply destination validation, redirect rechecking, request budgets and content-type checks. Keep the metadata fields as untrusted text when they come back. If a link card cannot be fetched safely, a plain URL with a domain label is still useful. Our separate agent safe-URL-fetching guide develops the network boundary beyond what this display article can cover.
Images and attachments owned by the application
Not every image is remote content. A user-uploaded illustration, a diagram generated by an approved workflow, and an external tracking pixel have different owners and permission states. For controlled attachments, verify the source record, storage identifier, access grants, declared media type and actual decodable content before generating a preview. A CDN URL alone is not a proof that the correct user is entitled to view it.
A privacy-respecting viewer can offer an attachment download without inline rendering for formats with a risky preview pipeline. It can show filename, size and type as verified metadata and allow the user to request access. Where a preview is necessary, use an isolation policy appropriate to the type and strip or constrain active features. The basic rule remains: content created by an agent does not get privileged browser abilities simply because a legitimate user asked the agent to create it.
A safe link is not an approved transaction
Suppose a Markdown report contains a button-like link labeled ‘approve payment’. A display layer might conclude that the URL scheme is permitted. That does not mean the assistant may approve a payment or that the document may prompt the person through a spoofed account interface. The browser renderer’s job is to prevent untrusted content from acquiring executable power. The transactional application’s job is to authenticate the actual request, validate amount and recipient, obtain the required approval and verify the final state.
Keep these responsibilities visibly separate. A report can describe a proposed action in text. A real action should use an application-owned control with a clearly identified target and an appropriate human decision. Do not render user-supplied HTML form elements as if they belonged to the host product. A well-designed viewer reduces accidental clicks, but business logic must still reject unauthorized state changes. This is why ‘the link passed the Markdown policy’ is a narrow and insufficient statement.
Changed-context transfer: a classroom report viewer
Transfer the mechanism to a fictional school portal that receives 48 submitted learning reports. The categories differ from the earlier helpdesk: 24 text-only reports, 10 containing one external link, 6 containing code snippets, 4 with limited HTML formatting and 4 with remote-image references. Their sum is 24 + 10 + 6 + 4 + 4 = 48. The policy keeps text and code inert, allows 8 of the links after destination checks, presents the other 2 without navigation, safely displays the four HTML fragments, and substitutes descriptions for all four remote images.
The full-presentation numerator is 24 + 8 + 6 + 4 = 42, or 42/48 = 87.5%. The constrained-fallback numerator is 2 + 4 = 6, or 6/48 = 12.5%. Both fractions refer to fictional report previews, not students, malicious submissions or confirmed incidents. What must transfer is the architecture: untrusted report text remains data, the portal owns controls, remote requests are explicit, and the uploaded content is never accepted as instructions to alter grades or reveal classmates’ private work. A school may select a stricter no-external-links policy even if it lowers the richness metric.
A realistic failure diagnosis
If an exported answer unexpectedly runs a browser event handler, inspect the insertion sink, raw-HTML allowance and sanitizer placement before blaming the model alone. If the main page never executes script but an external observer learns when it was opened, inspect image-loading and preview-fetch decisions. If clicking a citation goes to the wrong organisation, inspect URL parsing and link display. If a streamed answer first showed safe text and later acquired an active link, inspect the provisional-to-committed parsing boundary.
If ordinary reports become unreadable, examine whether policy blocks more than the task needs. The repair might be a safe code-node renderer or a clearer placeholder rather than relaxing the entire HTML allowlist. If a sanitizer test passes but a production plugin changes the DOM afterward, inspect the complete chain and pin the dependency. Treat each symptom as evidence about a boundary. The repair should change the smallest responsible layer and then retest both security and reader comprehension.
What a complete acceptance test proves
An acceptance record should identify the input set, parser version, sanitizer or safe DOM strategy, URL policy, remote media policy, test browser and observed effects. For one case, the expected result may be ‘literal code displayed, no execution’. For another, it may be ‘link visible but not clickable’. For a remote image, it may be ‘alt text displayed with zero network requests to the external image host’. The test result should state how that effect was measured, not merely that a test runner returned green.
Add manual accessibility verification and a reader comprehension check for important reports. When a vulnerability is discovered, preserve a safe reproducible fixture and patch both the vulnerable pipeline and its regression tests. A policy is not an achievement until the actual delivered viewer enforces it under realistic inputs. Review the system when parser settings, browser support, rendering plugins or product requirements change.
Constraints that rendering cannot solve
A safe renderer cannot determine whether a scientific claim is true, whether a source is authentic, whether a user was authorized to request the original data, or whether a person should act on the final recommendation. It can make malicious text less capable of hijacking the browser. It can also label uncertainty and keep citations traceable, but it is not the system’s entire trust model. Permission checks, evidence validation, disclosure controls and transaction approval live at other boundaries.
Likewise, no sanitizer can promise perfect security for every future browser and every combination of extensions. Maintain patching discipline, monitor browser compatibility, run representative tests, and have a safe text-only fallback. Do not treat artificial superintelligence as an existing deployed capability: the engineering principles here describe current and foreseeable AI-assisted interfaces. The safety property is about the actual software components and how the human can inspect the result.
Frequently asked questions
Is Markdown safer than HTML? It is often easier to restrict because its common constructs are simpler, but some Markdown engines permit raw HTML or plugins that generate active output. Safety depends on the complete parsing and rendering path.
Can the model be instructed never to produce dangerous HTML? Such an instruction may reduce undesirable output, but it cannot replace deterministic software controls, especially when material comes from retrieved websites and uploads.
Should every external image be banned? A product can choose opt-in remote loading or a validated proxy when images are genuinely useful. A text-only default is reasonable when privacy or confidentiality outweighs convenience.
Does rel=noopener make a link completely safe? No. It mitigates a particular opener relationship, but it does not establish that the target page is truthful, authorized or harmless.
Should I use a sanitizer if I already escape all text? If the product truly creates only safe known elements and uses text nodes for external values, it may not need an HTML sanitizer for that path. If any path accepts actual rich HTML, it needs an appropriate sanitization strategy and tests.
How can a student use this? Compare the visible label and the true destination before following a link, avoid treating copied code as something to run, and ask for a text explanation when an embedded item has unclear origin.
Evidence and practical companion routes
Security and implementation references include the OWASP Cross Site Scripting Prevention Cheat Sheet and its distinction between output encoding and HTML sanitization; MDN’s Element.setHTML documentation for a safe insertion approach and its compatibility limitations; and the OWASP JavaScript and TypeScript Security Cheat Sheet for DOM insertion practices. These sources establish principles and interface behaviour, not a claim that a particular eduKate product was tested against them.
Related reading should follow the failure boundary. If the problem arises while fetching a website, use the agent safe URL fetching guide. If the problem arises when answers arrive in fragments, use the partial-answer streaming guide. If the main difficulty is keyboard and screen-reader behaviour, use the accessible agent interfaces guide. For a general systems map, return to the How Super Intelligence Works hub. The student and work examples are illustrative exercises designed to transfer the distinctions beyond agent-chat software.
Primary technical references: OWASP XSS Prevention Cheat Sheet; OWASP JavaScript and TypeScript Security; MDN Element.setHTML.
Continue with safe URL fetching and SSRF defence, partial-answer streaming, accessible agent interfaces, and the How Super Intelligence Works hub.