fix: allow agent JWTs to access plugin tool endpoints

Agents running with JWT authentication are blocked from discovering and
executing plugin-contributed tools because both GET /plugins/tools and
POST /plugins/tools/execute require board-level auth (assertBoard).
This means agents cannot call plugin tools like escalate_to_human
without workarounds.

Add assertBoardOrAgent() to authz.ts and use it on the two plugin tool
endpoints. Agents are still scoped to their own company via the existing
assertCompanyAccess check in the execute endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ChadandClaude Opus 4.6 authored and Devin Foley committed 2026-06-03 19:02:25 -07:00
1 parent c369d3d357
commit 2e34a77980
2 files changed
+11 -3

No files matched your search

+6
View File
@@ -31,6 +31,12 @@ export function assertBoardOrgAccess(req: Request) {
throw forbidden("Company membership or instance admin access required");
}
export function assertBoardOrAgent(req: Request) {
if (req.actor.type !== "board" && req.actor.type !== "agent") {
throw forbidden("Board or agent access required");
}
}
export function assertInstanceAdmin(req: Request) {
assertBoard(req);
if (req.actor.source === "local_implicit" || req.actor.isInstanceAdmin) {
+5 -3
View File
@@ -11,8 +11,9 @@
* - Retrieving UI slot contributions for frontend rendering
* - Discovering and executing plugin-contributed agent tools
*
* All routes require board-level authentication, and sensitive instance-wide
* Most routes require board-level authentication, and sensitive instance-wide
* mutations such as install/upgrade require instance-admin privileges.
* Plugin tool discovery and execution routes allow both board and agent access.
*
* @module server/routes/plugins
* @see doc/plugins/PLUGIN_SPEC.md for the full plugin specification
@@ -60,6 +61,7 @@ import { JsonRpcCallError, PLUGIN_RPC_ERROR_CODES } from "@paperclipai/plugin-sd
import {
assertAuthenticated,
assertBoard,
assertBoardOrAgent,
assertBoardOrgAccess,
assertCompanyAccess,
assertInstanceAdmin,
@@ -878,7 +880,7 @@ export function pluginRoutes(
* Errors: 501 if tool dispatcher is not configured
*/
router.get("/plugins/tools", async (req, res) => {
assertBoardOrgAccess(req);
assertBoardOrAgent(req);
if (!toolDeps) {
res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
@@ -912,7 +914,7 @@ export function pluginRoutes(
* - 502 if the plugin worker is unavailable or the RPC call fails
*/
router.post("/plugins/tools/execute", async (req, res) => {
assertBoardOrgAccess(req);
assertBoardOrAgent(req);
if (!toolDeps) {
res.status(501).json({ error: "Plugin tool dispatch is not enabled" });