A Shared ChatGPT Link Could Run Code in Your Signed-In Session: Inside the HTML Preview Sandbox Escape
Parameter Labs chained a virtual keyboard, an innerHTML sink, an unsandboxed frame and a YouTube oEmbed reflection into code running in ChatGPT itself.
A shared ChatGPT link could, briefly, run code inside the signed-in session of anyone who opened it. On 31 October 2026, Simon Willison wrote up an attack chain that Parameter Labs found against ChatGPT’s implementation of the artifacts pattern — the feature OpenAI once called “Canvas” and which, Willison notes, now appears to ship as “HTML previews” — where interactive custom HTML apps are displayed in a sandboxed iframe. The chain escaped that sandbox and turned a share link into a request that executed with the session of whoever opened it. Willison reports that OpenAI closed the hole and paid a $5,000 bug bounty.
The framing from a Techmeme snapshot that afternoon is the same defect seen from the news side: Swati Khandelwal’s report for The Hacker News says researchers were paid $5K for a bug that would let attackers run code in the logged-in sessions of everyone who opened the hacker’s ChatGPT share link. The Hacker News forum thread listed alongside that item — a different outlet from the publication that carried her report — carries the title “Do the math: How we took over ChatGPT accounts.”

The sandbox was the entire security model
ChatGPT’s artifacts pattern lets the model emit a self-contained HTML app that the product renders in a sandboxed iframe on its own origin. Everything inside that frame is supposed to be untrusted: it must not reach the parent page, call OpenAI APIs or read anything private. If that boundary holds, a preview is inert no matter who wrote it, which is what makes “paste a shared artifact into your workflow” feel safe.
Parameter Labs broke that assumption in five hops, and not one hop is a novel technique. The chain reuses a virtual keyboard, an HTML parsing sink, a frame injection and one allowlisted third-party origin. The transferable lesson is structural: a sandbox is only as strong as the messages that cross it. Any product that renders third-party HTML next to a live session — a chat surface, a coding agent that previews generated pages, an internal tool that displays pasted HTML — repeats the same design.
The chain, hop by hop
| Hop | What happened | What it broke |
|---|---|---|
| 1 | The sandboxed preview frame sent a postMessage() that reached the MathLive virtual keyboard in the parent page |
The one legitimate channel became an attacker-controlled entry point |
| 2 | The parent page’s message handler inserted attacker-supplied markup with innerHTML |
A frame message became an HTML injection sink in the first-party document |
| 3 | innerHTML cannot run script tags, but it can create a new iframe, and that frame carried no sandbox restrictions |
The sandbox boundary did not transfer to the injected document |
| 4 | The Content Security Policy blocked inline scripts but allowed a short script-origin allowlist that included YouTube, whose oEmbed endpoint could reflect JavaScript that then ran | script-src was an allowlist, not a boundary |
| 5 | With script execution in the ChatGPT origin, private data APIs answered and the results were exfiltrated through an image reference | First-party session data left the page |
Two details deserve attention. Hop 3 is the bug in miniature: because innerHTML does not execute script elements, the payload looks harmless to a reviewer scanning for script tags, and yet it can still create a frame with none of the original constraints. Hop 4 shows what a permissive allowlist really buys you — blocking inline scripts is no defence when an allowed origin will hand your browser back executable code derived from your request.
What OpenAI closed, and what is still not public
Willison’s account says OpenAI closed the hole and paid a $5,000 bounty, and calls the payout cheap for a vulnerability this serious. OpenAI’s ChatGPT Help Center carries the two pieces of guidance that matter for readers today: a page on what to do if you think your account has been accessed, and the Lockdown Mode article, described as limiting access to the web and external services to reduce data exfiltration risk from prompt injection attacks. The release-note index we fetched does not itself carry an entry naming a fixed build or a date for this issue, so no version number is quoted here.
Two caveats for the record. Everything technical above comes from Willison’s summary of the researchers’ write-up, which he links from his post; that write-up was not part of the material reviewed for this article. And this disclosure covers one defect. Treat any claim that the share-link bug is fixed, or that a particular build carries the patch, as unverified until the vendor documents it. Willison’s own 31 October archive page timestamps the post at 11:15 pm and tags it iframes, javascript, sandboxing, security, xss, openai, chatgpt and content-security-policy.
If you opened one of these links
- Revoke the session first. OpenAI’s help center publishes a dedicated page for readers who think their account was accessed; follow it, and change your password if you are unsure what was shared.
- Treat a preview link as untrusted code, not as a document. A link from a stranger, a shared drive or a public thread deserves the suspicion you would give an emailed HTML attachment.
- Turn on Lockdown Mode for accounts that browse untrusted web content. It is the vendor’s own mitigation for the exfiltration step.
- If your product renders third-party HTML, audit it against the chain above. Our notes on sandbox escapes as a design failure, securing AI agents, the injection problem in agentic browsers and where your agents publish images cover the same shape on different surfaces.
The inverse checklist for your own renderers
For anyone embedding untrusted HTML beside a session, the audit is short, and it is the inverse of the chain: write frame messages into the DOM without an HTML parser, create every frame yourself with explicit sandbox attributes, validate the origin and shape of every message, and keep script-src free of origins that reflect request parameters into executable code.
// Treat every frame message as untrusted input.
window.addEventListener("message", function (event) {
// 1. Only accept messages from origins you actually host.
if (!ALLOWED_PREVIEW_ORIGINS.has(event.origin)) return;
// 2. Validate the shape of the payload, not just its presence.
var msg = event.data;
if (typeof msg !== "object" || msg.type !== "keyboard.layout") return;
// 3. Never feed a message into an HTML parser. Build nodes instead.
var host = document.getElementById("keyboard-host");
host.replaceChildren(buildKeyboardNodes(msg.layout));
});
The shape on display here — a preview frame, a message channel and a session cookie — is exactly what shows up in every agent product that displays HTML it did not write. That is why this disclosure matters even if you never open a shared artifact, and why “the HTML came from the model” was never a safety argument in the first place.
Sources
- How We Took Over ChatGPT Accounts
- Techmeme: Researchers are paid $5K for reporting a bug to OpenAI that would let attackers run code in logged-in sessions of all who opened the hacker's ChatGPT share link (Swati Khandelwal/The Hacker News)
- ChatGPT | OpenAI Help Center
- Archive for Saturday, 31st October 2026