Audit finding from #300 (commit 679f9fe)
Severity: low
Category: auth / authorization
File: crates/agent/src/server.rs:161
Obvious fix: yes
Description
list_resources filters by scope.allows_resource, but read_resource skips the scope check entirely and dispatches to resources::read_resource directly. Today allows_resource always returns true so this is dormant, but as soon as any scope returns false (e.g. to hide willow://server/join-links from a Messaging-scoped token), a client that knows the URI can still read it because list_resources is the only gate.
Impact / Threat
A future scope-tightening change is silently bypassed by clients that probe URIs directly; non-listed but readable resources are exfiltrable.
Suggested fix
In call_tool-style fashion, gate read_resource on self.scope.allows_resource(&request.uri) before delegating, returning INVALID_REQUEST otherwise.
Verify
rg -n "allows_resource" crates/agent/src
Audit finding from #300 (commit 679f9fe)
Severity: low
Category: auth / authorization
File: crates/agent/src/server.rs:161
Obvious fix: yes
Description
list_resourcesfilters byscope.allows_resource, butread_resourceskips the scope check entirely and dispatches toresources::read_resourcedirectly. Todayallows_resourcealways returns true so this is dormant, but as soon as any scope returns false (e.g. to hidewillow://server/join-linksfrom a Messaging-scoped token), a client that knows the URI can still read it becauselist_resourcesis the only gate.Impact / Threat
A future scope-tightening change is silently bypassed by clients that probe URIs directly; non-listed but readable resources are exfiltrable.
Suggested fix
In
call_tool-style fashion, gateread_resourceonself.scope.allows_resource(&request.uri)before delegating, returningINVALID_REQUESTotherwise.Verify
rg -n "allows_resource" crates/agent/src