From 3bedce151ebe9bd3ec300209ff07d3dfff7605e8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 15 May 2026 11:23:26 +0100 Subject: [PATCH] fix(gateway): keep exec approvals policy admin scoped --- src/gateway/method-scopes.test.ts | 19 +++++++++++++++++++ src/gateway/methods/core-descriptors.ts | 8 ++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/gateway/method-scopes.test.ts b/src/gateway/method-scopes.test.ts index 33d65fb8ade..c40bf20f8b5 100644 --- a/src/gateway/method-scopes.test.ts +++ b/src/gateway/method-scopes.test.ts @@ -69,6 +69,10 @@ describe("method scope resolution", () => { ["nativeHook.invoke", ["operator.admin"]], ["wizard.start", ["operator.admin"]], ["update.run", ["operator.admin"]], + ["exec.approvals.get", ["operator.admin"]], + ["exec.approvals.set", ["operator.admin"]], + ["exec.approvals.node.get", ["operator.admin"]], + ["exec.approvals.node.set", ["operator.admin"]], ])("resolves least-privilege scopes for %s", (method, expected) => { expect(resolveLeastPrivilegeOperatorScopesForMethod(method)).toEqual(expected); }); @@ -293,6 +297,21 @@ describe("operator scope authorization", () => { }, ); + it.each([ + "exec.approvals.get", + "exec.approvals.set", + "exec.approvals.node.get", + "exec.approvals.node.set", + ])("requires admin scope for exec approval policy method %s", (method) => { + expect(authorizeOperatorScopesForMethod(method, ["operator.approvals"])).toEqual({ + allowed: false, + missingScope: "operator.admin", + }); + expect(authorizeOperatorScopesForMethod(method, ["operator.admin"])).toEqual({ + allowed: true, + }); + }); + it.each([ "plugin.approval.list", "plugin.approval.request", diff --git a/src/gateway/methods/core-descriptors.ts b/src/gateway/methods/core-descriptors.ts index 9c1e9f450d6..c5cca9df27a 100644 --- a/src/gateway/methods/core-descriptors.ts +++ b/src/gateway/methods/core-descriptors.ts @@ -48,10 +48,10 @@ export const CORE_GATEWAY_METHOD_SPECS: readonly CoreGatewayMethodSpec[] = [ { name: "config.patch", scope: "operator.admin", controlPlaneWrite: true }, { name: "config.schema", scope: "operator.read" }, { name: "config.schema.lookup", scope: "operator.read" }, - { name: "exec.approvals.get", scope: "operator.approvals" }, - { name: "exec.approvals.set", scope: "operator.approvals" }, - { name: "exec.approvals.node.get", scope: "operator.approvals" }, - { name: "exec.approvals.node.set", scope: "operator.approvals" }, + { name: "exec.approvals.get", scope: "operator.admin" }, + { name: "exec.approvals.set", scope: "operator.admin" }, + { name: "exec.approvals.node.get", scope: "operator.admin" }, + { name: "exec.approvals.node.set", scope: "operator.admin" }, { name: "exec.approval.get", scope: "operator.approvals" }, { name: "exec.approval.list", scope: "operator.approvals" }, { name: "exec.approval.request", scope: "operator.approvals" },