mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-05 21:54:37 +00:00
refactor(browser): defer permission enforcement
This commit is contained in:
parent
4d54418f14
commit
5eefeee0ce
3 changed files with 9 additions and 30 deletions
|
|
@ -28,6 +28,9 @@ Control events use OpenCode's existing authenticated, server-wide event feed.
|
|||
Consumers filter by `connectionID`; this identifier is correlation, not private
|
||||
event delivery. State and results use RPC calls rather than broadcast events.
|
||||
|
||||
The plugin requests normal agent permissions before acting on a URL. Browser
|
||||
content is untrusted. Pages use the desktop's network, with no server-side tunnel.
|
||||
The desktop owns Chromium, page isolation, and native controls.
|
||||
Per-URL permission checks are deferred to the final permission layer (#46530).
|
||||
Until that layer lands, browser actions do not enforce URL-specific ask or deny
|
||||
rules. Attachment ownership, page validation, and cancellation remain enforced.
|
||||
|
||||
Browser content is untrusted. Pages use the desktop's network, with no server-side
|
||||
tunnel. The desktop owns Chromium, page isolation, and native controls.
|
||||
|
|
|
|||
|
|
@ -95,20 +95,8 @@ export default Plugin.define({
|
|||
Effect.gen(function* () {
|
||||
const browser = browsers.get(tool.sessionID)
|
||||
if (!browser) return yield* new Tool.Error({ message: "No desktop browser is connected." })
|
||||
if (action.type !== "open") {
|
||||
if (!browser.state) return yield* new Tool.Error({ message: "Open the browser first." })
|
||||
const url = action.type === "navigate" ? action.url : browser.state.url
|
||||
yield* ctx.permission
|
||||
.assert({
|
||||
action: "browser",
|
||||
resources: [url],
|
||||
metadata: { type: action.type, url },
|
||||
sessionID: tool.sessionID,
|
||||
agent: tool.agent,
|
||||
source: { type: "tool", messageID: tool.messageID, id: tool.id },
|
||||
})
|
||||
.pipe(Effect.mapError((error) => new Tool.Error({ message: "Browser action failed", error })))
|
||||
}
|
||||
if (action.type !== "open" && !browser.state)
|
||||
return yield* new Tool.Error({ message: "Open the browser first." })
|
||||
const requestID = crypto.randomUUID()
|
||||
const pending = yield* Deferred.make<Browser.Result, Tool.Error>()
|
||||
browser.pending.set(requestID, pending)
|
||||
|
|
|
|||
|
|
@ -29,14 +29,12 @@ const fixture = Effect.gen(function* () {
|
|||
project: false,
|
||||
content: JSON.stringify({
|
||||
plugins: ["-opencode.browser"],
|
||||
permissions: [{ action: "browser", resource: "*", effect: "allow" }],
|
||||
}),
|
||||
},
|
||||
models: { fetch: false },
|
||||
fs: { filewatcher: false, fff: false },
|
||||
})
|
||||
const captured = Promise.withResolvers<Tool.Info>()
|
||||
const permissions: Array<{ action: string; resources: readonly string[] }> = []
|
||||
yield* opencode.plugin({ ...plugin, id: "browser-test" })
|
||||
yield* opencode.plugin({
|
||||
id: "browser-test-observer",
|
||||
|
|
@ -47,9 +45,6 @@ const fixture = Effect.gen(function* () {
|
|||
const tool = draft.get("browser")
|
||||
if (tool && ctx.location.directory === location.directory) captured.resolve(tool)
|
||||
})
|
||||
yield* ctx.permission.hook("evaluate", (event) =>
|
||||
Effect.sync(() => permissions.push({ action: event.action, resources: event.resources })),
|
||||
)
|
||||
}).pipe(Effect.orDie),
|
||||
})
|
||||
yield* opencode.plugin.list({ location })
|
||||
|
|
@ -80,7 +75,6 @@ const fixture = Effect.gen(function* () {
|
|||
opencode,
|
||||
location,
|
||||
rpc,
|
||||
permissions,
|
||||
execute,
|
||||
next,
|
||||
attach: Effect.fn(function* (connectionID: string) {
|
||||
|
|
@ -196,7 +190,7 @@ test(
|
|||
)
|
||||
|
||||
test(
|
||||
"commands use published state and permissions, and RPC results render text and screenshot bytes",
|
||||
"commands use published state, and RPC results render text and screenshot bytes",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const host = yield* fixture
|
||||
|
|
@ -212,7 +206,6 @@ test(
|
|||
options,
|
||||
)
|
||||
expect((yield* Fiber.join(open.pending)).metadata).toEqual({ url: state.url })
|
||||
expect(host.permissions).toEqual([])
|
||||
yield* host.rpc.state({ ...attached.input, state }, options)
|
||||
|
||||
const navigate = yield* host.command({ type: "navigate", url: "https://example.org/next" })
|
||||
|
|
@ -263,11 +256,6 @@ test(
|
|||
],
|
||||
metadata: { url: updated.url },
|
||||
})
|
||||
expect(host.permissions).toEqual([
|
||||
{ action: "browser", resources: [updated.url] },
|
||||
{ action: "browser", resources: [updated.url] },
|
||||
{ action: "browser", resources: [updated.url] },
|
||||
])
|
||||
const failure = yield* host.command({ type: "snapshot" })
|
||||
yield* host.rpc.result(
|
||||
{ ...attached.input, requestID: failure.requestID, outcome: { type: "failure", message: "Stale document" } },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue