diff --git a/src/backend/operations/approvals.ts b/src/backend/operations/approvals.ts index 8e9c10fcd..2ad8ad9d8 100644 --- a/src/backend/operations/approvals.ts +++ b/src/backend/operations/approvals.ts @@ -69,7 +69,8 @@ export function clearApprovalPolicies(): void { } const RANK = { allow: 0, approve: 1, deny: 2 } as const; -const TRUSTED_VIA = new Set(["automation", "rollout", "fleet"]); +// approved: a person approved this exact request in the plugin's own UI. +const TRUSTED_VIA = new Set(["automation", "rollout", "fleet", "approved"]); const SELF_APPROVE_VIA = new Set(["assistant", "mcp", "automation"]); /** A person is the check when the request came from something else. */ diff --git a/src/backend/tests/operations/gate.test.ts b/src/backend/tests/operations/gate.test.ts index cb862cfdc..47accad2e 100644 --- a/src/backend/tests/operations/gate.test.ts +++ b/src/backend/tests/operations/gate.test.ts @@ -451,6 +451,15 @@ describe("policy", () => { expect(outcome.status).toBe("success"); }); + it("lets a request a person approved in a plugin go by RBAC", async () => { + register(); + const outcome = await runOperation({ + ...base, + origin: { kind: "plugin", pluginId: "x", via: "approved" }, + }); + expect(outcome.status).toBe("success"); + }); + it("treats a community read as a change", async () => { state.plugin.source = "user"; state.plugin.signedTier = "community";