Repository navigation
Document cross-origin tool exposure and two-sided opt-in in Section 6.3.4 #326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jpagnucco
wants to merge
3
commits into
webmachinelearning:main
Choose a base branch
from
jpagnucco:update-section-6-3-4
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+117
−4
Open
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
06526a4
Document cross-origin tool exposure and two-sided opt-in in Section 6…
jpagnucco b3b138b
Retitle Section 6.3.4 and reorder to lead with SOP considerations and…
jpagnucco 139b259
Address review feedback on Section 6.3.4 and wrap lines to 100 columns
jpagnucco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1883,11 +1883,65 @@ This creates a personalization-to-fingerprinting pipeline where sites can extrac | |
| <li><strong>Discrimination risk</strong>: Extracted attributes (age, pregnancy status, location) could be used for price discrimination or biased service</li> | ||
| </ul> | ||
|
|
||
| <h4 id="violation-same-origin-boundaries">Violation of Same-Origin Boundaries</h4> | ||
| <h4 id="interaction-with-same-origin-policy">Interaction with the Same-Origin Policy</h4> | ||
|
|
||
| <p class="XXX"> | ||
| TODO: Document risks and implications of [=agents=] carrying state from one origin to another. Detail how tools executed on one origin may carry state from another origin, potentially leading to data leakage or same-origin policy bypasses if not handled securely by the [=user agent=]. This section should probably talk about the WebMCP permissions policy and other cross-origin opt in mechanisms. | ||
| </p> | ||
| The Same-Origin Policy (SOP) is the foundational security boundary of the web platform, isolating documents of different [=origins=] so that one [=origin=] cannot inspect or manipulate the state of another without explicit consent. | ||
|
|
||
| In WebMCP, a registered tool's {{ModelContextTool/execute}} callback always runs within the registering {{Document}}'s execution context, with full access to that [=origin=]'s DOM, storage, and ambient credentials. Consequently, allowing a cross-origin {{Document}} in the frame tree to discover a tool ({{ModelContext/getTools()}}), invoke it ({{ModelContext/executeTool()}}), and receive its serialized return value crosses a privilege boundary. Without strict enforcement, a malicious embedding page or embedded subframe could silently invoke privileged operations or extract sensitive state from another [=origin=]. | ||
|
markafoltz marked this conversation as resolved.
Outdated
|
||
|
|
||
| <h5 id="in-page-cross-origin-exposure">Enforcing Same-Origin Isolation via Two-Sided Opt-In</h5> | ||
|
|
||
| By default, WebMCP enforces strict [=same origin=] isolation: a tool registered via <code><var ignore>document</var>.{{Document/modelContext}}.{{ModelContext/registerTool()}}</code> is only visible to and executable by {{Document}}s that are [=same origin=] with the registering {{Document}} (tool owner). Cross-origin {{Document}}s cannot discover, observe {{ModelContext/toolchange}} events for, or execute a tool unless three independent access-control gates are satisfied: | ||
|
|
||
| <ol> | ||
| <li> | ||
| <strong>Embedder Delegation via Permissions Policy (<code>allow="tools"</code>)</strong>: Access to all WebMCP APIs ({{ModelContext/registerTool()}}, {{ModelContext/getTools()}}, and {{ModelContext/executeTool()}}) is gated behind the "{{tools}}" [=policy-controlled feature=], whose [=policy-controlled feature/default allowlist=] is <code>[=default allowlist/'self'=]</code>. Consequently, a cross-origin <{iframe}> cannot register or expose tools—nor will an ancestor's {{ModelContext/getTools()}} traversal inspect that subframe—unless the embedding {{Document}} explicitly delegates the "{{tools}}" feature to the subframe via <code><iframe allow="tools"></code>. | ||
| </li> | ||
| <li> | ||
| <strong>Tool Provider Opt-In ({{ModelContextRegisterToolOptions/exposedTo}})</strong>: A {{Document}} registering a tool must explicitly opt in to cross-origin exposure by specifying the permitted caller [=origins=] in {{ModelContextRegisterToolOptions/exposedTo}}. Each listed [=origin=] must be a valid, [$is origin potentially trustworthy?|potentially trustworthy$] [=origin=]. During both discovery ({{ModelContext/getTools()}}) and invocation ({{ModelContext/executeTool()}}), the [=user agent=] evaluates the [=tool is exposed to an origin=] algorithm, ensuring that only callers whose [=Document/origin=] is [=same origin=] with the tool owner or explicitly listed in {{ModelContextRegisterToolOptions/exposedTo}} can access the tool. | ||
| </li> | ||
| <li> | ||
| <strong>Tool Consumer Opt-In ({{ModelContextGetToolOptions/fromOrigins}})</strong>: Even when an embedded cross-origin {{Document}} has been delegated <code>allow="tools"</code> and has exposed a tool to its parent [=origin=] via {{ModelContextRegisterToolOptions/exposedTo}}, {{ModelContext/getTools()}} only returns tools from [=same origin=] {{Document}}s by default. To discover tools from a cross-origin descendant [=navigable=], the calling {{Document}} must also explicitly list the target [=origin=] in {{ModelContextGetToolOptions/fromOrigins}}. | ||
| </li> | ||
| </ol> | ||
|
|
||
| **Why Two-Sided Opt-In Matters**: Requiring mutual consent from both the tool provider ({{ModelContextRegisterToolOptions/exposedTo}}) and the tool consumer ({{ModelContextGetToolOptions/fromOrigins}}) prevents both unauthorized cross-origin actuation and unsolicited tool-list pollution. Without provider opt-in ({{ModelContextRegisterToolOptions/exposedTo}}), a malicious parent page could invoke internal tools inside an embedded third-party widget that were intended only for [=same origin=] use. Conversely, without consumer opt-in ({{ModelContextGetToolOptions/fromOrigins}}), a compromised or untrusted third-party <{iframe}> could unilaterally expose tools with deceptive names or prompt-injected descriptions ([[#metadata-description-attacks]]) into the parent page's {{ModelContext/getTools()}} results. | ||
|
|
||
| ```js | ||
| // 1. Parent document (https://app.example.com) explicitly delegates the "tools" | ||
| // feature to the embedded widget (<iframe src="https://widget.example.org" allow="tools">) | ||
| // and explicitly requests tools from that origin via `fromOrigins`: | ||
| const tools = await document.modelContext.getTools({ | ||
| fromOrigins: ["https://widget.example.org"] | ||
| }); | ||
|
|
||
| // 2. Child iframe (https://widget.example.org) explicitly exposes the tool | ||
| // to the parent origin via `exposedTo`: | ||
| await document.modelContext.registerTool( | ||
| { | ||
| name: "generate-chart", | ||
| description: "Renders a chart inside the widget", | ||
| inputSchema: { type: "object", properties: { data: { type: "array" } } }, | ||
| execute: async ({ data }) => { /* ... */ } | ||
| }, | ||
| { | ||
| exposedTo: ["https://app.example.com"] | ||
| } | ||
| ); | ||
| ``` | ||
|
|
||
| <h5 id="same-origin-residual-risks">Residual Risks</h5> | ||
|
|
||
| Even when cross-origin access is governed by the mechanisms above, two categories of residual risk remain: | ||
|
|
||
| <ul> | ||
| <li> | ||
| <strong>Direct Cross-Origin Exposure</strong>: Even when two [=origins=] mutually opt in via {{ModelContextRegisterToolOptions/exposedTo}} and {{ModelContextGetToolOptions/fromOrigins}}, they remain separate trust domains: tool providers must still treat caller-supplied arguments as untrusted input, and callers must treat cross-origin tool descriptions and return values as untrusted data (see [[#prompt-injection]]). Additionally, because {{ModelContext/getTools()}} aggregates tools across all descendant frames at the [=origin=] level, callers should inspect {{RegisteredTool/origin}} and {{RegisteredTool/window}} on each {{RegisteredTool}} to disambiguate tools when multiple subframes or [=origins=] expose tools with the same {{RegisteredTool/name}}. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good callout. |
||
| </li> | ||
| <li> | ||
| <strong>Agent-Mediated Cross-Origin State Transfer</strong>: Where an external or browser-integrated [=agent=] ([=browser's agent=]) interacts with multiple [=origins=] over the course of a task and carries state derived from one [=origin=] into tool invocations on another [=origin=]. This is a general risk of agentic browsing across the web rather than a WebMCP-specific threat. | ||
|
markafoltz marked this conversation as resolved.
Outdated
|
||
| </li> | ||
| </ul> | ||
|
|
||
| <h4 id="interaction-with-private-browsing">Interaction with Private Browsing Modes</h4> | ||
|
|
||
|
|
||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.