harden phone-control command scope checks

This commit is contained in:
Robin Waslander
2026-03-30 15:52:52 +02:00
parent 3b9dab0ece
commit 847912f3e2
2 changed files with 27 additions and 2 deletions

View File

@@ -167,6 +167,25 @@ describe("phone-control plugin", () => {
});
});
it("blocks non-webchat gateway callers with operator.write from mutating phone control", async () => {
await withRegisteredPhoneControl(async ({ command, writeConfigFile }) => {
const armRes = await command.handler({
...createCommandContext("arm writes 30s"),
channel: "telegram",
gatewayClientScopes: ["operator.write"],
});
expect(String(armRes?.text ?? "")).toContain("requires operator.admin");
expect(writeConfigFile).not.toHaveBeenCalled();
const disarmRes = await command.handler({
...createCommandContext("disarm"),
channel: "telegram",
gatewayClientScopes: ["operator.write"],
});
expect(String(disarmRes?.text ?? "")).toContain("requires operator.admin");
});
});
it("allows internal operator.admin callers to mutate phone control", async () => {
await withRegisteredPhoneControl(async ({ command, writeConfigFile }) => {
const res = await command.handler({

View File

@@ -358,7 +358,10 @@ export default definePluginEntry({
}
if (action === "disarm") {
if (ctx.channel === "webchat" && !ctx.gatewayClientScopes?.includes("operator.admin")) {
if (
(ctx.channel === "webchat" || Array.isArray(ctx.gatewayClientScopes)) &&
!ctx.gatewayClientScopes?.includes("operator.admin")
) {
return {
text: "⚠️ /phone disarm requires operator.admin.",
};
@@ -380,7 +383,10 @@ export default definePluginEntry({
}
if (action === "arm") {
if (ctx.channel === "webchat" && !ctx.gatewayClientScopes?.includes("operator.admin")) {
if (
(ctx.channel === "webchat" || Array.isArray(ctx.gatewayClientScopes)) &&
!ctx.gatewayClientScopes?.includes("operator.admin")
) {
return {
text: "⚠️ /phone arm requires operator.admin.",
};