fix(commands): preserve owner allowlists for native auth

This commit is contained in:
Ayaan Zaidi
2026-04-28 22:03:40 +05:30
parent 195f704c74
commit 8af50b5b4c
3 changed files with 73 additions and 4 deletions

View File

@@ -99,6 +99,31 @@ describe("senderIsOwner only reflects explicit owner authorization", () => {
expect(auth.senderIsOwner).toBe(false);
});
it("does not let native command authorization bypass explicit owner allowlists", () => {
const cfg = {
channels: { telegram: {} },
commands: { ownerAllowFrom: ["456"] },
} as OpenClawConfig;
const ctx = {
Provider: "telegram",
Surface: "telegram",
ChatType: "group",
From: "telegram:group:-100123",
SenderId: "200482621",
CommandSource: "native",
} as MsgContext;
const auth = resolveCommandAuthorization({
ctx,
cfg,
commandAuthorized: true,
});
expect(auth.senderIsOwner).toBe(false);
expect(auth.isAuthorizedSender).toBe(false);
});
it("senderIsOwner is true when ownerAllowFrom matches sender", () => {
const cfg = {
channels: { discord: {} },

View File

@@ -429,6 +429,7 @@ function resolveOwnerAuthorizationState(params: {
function resolveCommandSenderAuthorization(params: {
commandAuthorized: boolean;
nativeCommandAuthorized: boolean;
isOwnerForCommands: boolean;
senderCandidates: string[];
commandsAllowFromList: string[] | null;
@@ -450,10 +451,7 @@ function resolveCommandSenderAuthorization(params: {
!params.providerResolutionError && (commandsAllowAll || Boolean(matchedCommandsAllowFrom))
);
}
if (params.commandAuthorized) {
return true;
}
return params.isOwnerForCommands;
return params.commandAuthorized && (params.isOwnerForCommands || params.nativeCommandAuthorized);
}
function isConversationLikeIdentity(value: string): boolean {
@@ -707,8 +705,11 @@ export function resolveCommandAuthorization(params: {
: ownerAllowlistConfigured
? senderIsOwner
: senderIsOwnerByScope || Boolean(matchedCommandOwner);
const nativeCommandAuthorized =
commandAuthorized && ctx.CommandSource === "native" && !ownerAllowlistConfigured;
const isAuthorizedSender = resolveCommandSenderAuthorization({
commandAuthorized,
nativeCommandAuthorized,
isOwnerForCommands,
senderCandidates,
commandsAllowFromList,

View File

@@ -202,6 +202,49 @@ describe("resolveCommandAuthorization", () => {
expect(auth.isAuthorizedSender).toBe(false);
});
it("allows channel-validated native commands when plugin owner enforcement has no owner allowlist", () => {
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "discord",
plugin: {
...createOutboundTestPlugin({
id: "discord",
outbound: { deliveryMode: "direct" },
}),
commands: { enforceOwnerForCommands: true },
config: {
listAccountIds: () => ["default"],
resolveAccount: () => ({}),
resolveAllowFrom: () => ["*"],
formatAllowFrom,
},
},
source: "test",
},
]),
);
const cfg = {
channels: { discord: { allowFrom: ["*"] } },
} as OpenClawConfig;
const auth = resolveCommandAuthorization({
ctx: {
Provider: "discord",
Surface: "discord",
ChatType: "direct",
From: "discord:123",
SenderId: "123",
CommandSource: "native",
} as MsgContext,
cfg,
commandAuthorized: true,
});
expect(auth.senderIsOwner).toBe(false);
expect(auth.isAuthorizedSender).toBe(true);
});
it("uses explicit owner allowlist when allowFrom is empty", () => {
const cfg = {
commands: { ownerAllowFrom: ["whatsapp:+15551234567"] },