Repository navigation
Mark browser page text as untrusted and fence all outside tool text with a nonce - #1195
Merged
Merged
Conversation
…with a nonce Browser pages reached the model with no untrusted framing, unlike the same page fetched with os.web.fetch. Now the page in ### world and the summaries of browser.navigate, browser.read_aria and browser.tabs get the same framing line, and every untrusted tool result (web, search, HTTP, MCP, browser) sits inside a nonce-fenced block shared with the risk reviewer. Copies of the nonce are removed and fake fences are defused, so a page cannot close the block and go on as instructions. A page too long for ### world is cut inside the fence, so the closing marker stays. ATO-1378
This was referenced Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A page opened in the browser reached the model with no "this is outside text" framing, while the same page fetched with
os.web.fetchhad it. This closes that gap and makes the framing harder to fake.### worldand the summaries ofbrowser.navigate,browser.read_ariaandbrowser.tabs(page titles) are now framed as external content, like a fetched page.os.http.request, MCP tools, resources and prompts, browser) now sits inside a nonce-fenced block after the framing line:src/tools/untrusted-text.ts;risk-review.tsre-exportswrapUntrusted). Copies of the nonce are removed from the content and fence-shaped text the content brings (<<<END UNTRUSTED ...>>>, any case or spacing, half-open too) is defused, so a page cannot close the block early.### worldis now cut inside the fence, so the closing marker always stays (fitWorldSnapshotSection).Same approach as OpenClaw
src/security/external-content.tsand Hermes PR #32269. The framing lowers the odds that a page steers the model; the approval cards are still what stops an action.Tests
src/tools/untrusted-text.test.ts(new): fence shape, nonce removal, forged and half-open fences defused, ordinary text untouched, forged blocks with text outside refused, stable vs random nonce.src/tools/untrusted-tool-output.test.ts: fenced framing, same bytes twice, forged framing line plus fence framed again, browser tools declareuntrustedOutput.src/prompt/build-prompt.test.ts: the browser page in### worldis inside the fence with its fake end defused; a long page is cut inside the fence.src/tools/tool-output-secrets.test.ts: updated to the new framing.The live check (marker in the model request after
browser.read_aria) comes with the L18 scenario (ATO-1385).Merge order
Merge before or together with #1239 (ATO-1376/1385). Its L18 scenario checks this framing in the live smoke and stays red without this change.
Verification
npm run build && npx vitest run src/tools/untrusted-text.test.ts src/tools/untrusted-tool-output.test.ts src/tools/tool-output-secrets.test.ts src/prompt/build-prompt.test.ts, then L18 from Live tests: real headless browser in the smoke, and a hostile page scenario (L18) #1239.Refs ATO-1378.
Checked on the second Mac on 12.11.2026 in the combined run with the other open PRs: build, lint, test types, vitest, desktop unit tests, desktop smoke and live smoke all green.