You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Indirect prompt injections hide in content invisible to a human but visible to the reader: zero-width unicode, white-on-white text, HTML comments (<!-- ignore previous instructions -->), off-screen / display:none nodes. Content Disarm & Reconstruction (CDR) strips these; data-tainting wraps what's left in structural provenance markers so the model treats page content as data, not instructions.
The seam (a correction to the original design sketch)
The web actor reads through deterministic serializers, NOT an arbitrary-JS REPL — page_exec (CDP Runtime.evaluate) is deliberately excluded from the actor's toolset ("the exclusion IS the boundary"), and the Playwright-style page_code API runs in the sealed worker with its reads funneling back through the same fixed tools. So page bytes reach the actor only through a countable set of readers, not an open REPL — CDR polices a finite surface.
That surface today: snapshot (→ serializeAxTree), read_page, read_state, read_pdf, view, query_dom — each with its own injected reader. So CDR lands either:
per-reader (scrub in each), or
at a shared post-read chokepoint — the wrapUntrusted seam every read result passes through before entering the actor heap (one place, all readers).
Leaning: the shared chokepoint, which also de-risks by consolidating — see the supporting note below.
Supporting: consolidate the page readers
snapshot/read_page/read_state/read_pdf/view shipping five separate injected readers is likely too many tools and five separate CDR chokepoints. Collapsing toward a shared reader (or fewer tools) means scrubbing + tainting live in one place instead of five — directly de-risking this item. (Owner flagged the reader count as worth simplifying.) Could be split into its own issue or folded here.
Where it lands
A pure CDR module (peerd-runtime/dom/cdr.js or similar): raw text → scrubbed text, stripping zero-width/invisible unicode, HTML comments, and (where structure survives) off-screen/hidden nodes. Fast, deterministic, no model.
Taint wrappers applied at the shared read chokepoint, feeding the same wrapUntrusted provenance the reply path uses.
Off-screen/hidden-node stripping needs the render tree — feasible in the serializeAxTree path (a11y tree already knows visibility), harder for read_page's markdown extraction. May be reader-specific after all.
False positives — legitimately hidden-then-revealed content (accordions, tabs); scrub what's hidden at read time, accept the rest.
Whether to split the reader-consolidation into its own issue.
Security-boundary arc, item 4 of 4. Sibling issues: #241 (deterministic-schema return), #242 (UGC downscaling), #243 (egress firewall).
Problem
Indirect prompt injections hide in content invisible to a human but visible to the reader: zero-width unicode, white-on-white text, HTML comments (
<!-- ignore previous instructions -->), off-screen /display:nonenodes. Content Disarm & Reconstruction (CDR) strips these; data-tainting wraps what's left in structural provenance markers so the model treats page content as data, not instructions.The seam (a correction to the original design sketch)
The web actor reads through deterministic serializers, NOT an arbitrary-JS REPL —
page_exec(CDPRuntime.evaluate) is deliberately excluded from the actor's toolset ("the exclusion IS the boundary"), and the Playwright-stylepage_codeAPI runs in the sealed worker with its reads funneling back through the same fixed tools. So page bytes reach the actor only through a countable set of readers, not an open REPL — CDR polices a finite surface.That surface today:
snapshot(→serializeAxTree),read_page,read_state,read_pdf,view,query_dom— each with its own injected reader. So CDR lands either:wrapUntrustedseam every read result passes through before entering the actor heap (one place, all readers).Leaning: the shared chokepoint, which also de-risks by consolidating — see the supporting note below.
Supporting: consolidate the page readers
snapshot/read_page/read_state/read_pdf/viewshipping five separate injected readers is likely too many tools and five separate CDR chokepoints. Collapsing toward a shared reader (or fewer tools) means scrubbing + tainting live in one place instead of five — directly de-risking this item. (Owner flagged the reader count as worth simplifying.) Could be split into its own issue or folded here.Where it lands
peerd-runtime/dom/cdr.jsor similar): raw text → scrubbed text, stripping zero-width/invisible unicode, HTML comments, and (where structure survives) off-screen/hidden nodes. Fast, deterministic, no model.wrapUntrustedprovenance the reply path uses.Open questions
serializeAxTreepath (a11y tree already knows visibility), harder forread_page's markdown extraction. May be reader-specific after all.