fix: honor allowlist default account

This commit is contained in:
Tak Hoffman 2026-04-03 17:04:07 -05:00
parent 42ffdf882f
commit cc0987f7b1
No known key found for this signature in database
2 changed files with 114 additions and 2 deletions

View File

@ -58,6 +58,22 @@ type AllowlistCommand =
const ACTIONS = new Set(["list", "add", "remove"]);
const SCOPES = new Set<AllowlistScope>(["dm", "group", "all"]);
function resolveAllowlistAccountId(params: {
cfg: OpenClawConfig;
channelId: ChannelId;
parsedAccount?: string;
ctxAccountId?: string;
}): string {
const explicitAccountId = normalizeOptionalAccountId(params.parsedAccount);
if (explicitAccountId) {
return explicitAccountId;
}
const plugin = getChannelPlugin(params.channelId);
const configuredDefaultAccountId = plugin?.config.defaultAccountId?.(params.cfg)?.trim();
const ctxAccountId = normalizeOptionalAccountId(params.ctxAccountId);
return configuredDefaultAccountId || ctxAccountId || DEFAULT_ACCOUNT_ID;
}
function parseAllowlistCommand(raw: string): AllowlistCommand | null {
const trimmed = raw.trim();
if (!trimmed.toLowerCase().startsWith("/allowlist")) {
@ -276,7 +292,12 @@ export const handleAllowlistCommand: CommandHandler = async (params, allowTextCo
},
};
}
const accountId = normalizeAccountId(parsed.account ?? params.ctx.AccountId);
const accountId = resolveAllowlistAccountId({
cfg: params.cfg,
channelId,
parsedAccount: parsed.account,
ctxAccountId: params.ctx.AccountId,
});
const plugin = getChannelPlugin(channelId);
if (parsed.action === "list") {
@ -464,7 +485,7 @@ export const handleAllowlistCommand: CommandHandler = async (params, allowTextCo
cfg: params.cfg,
channel: params.command.channel,
channelId,
accountId: params.ctx.AccountId,
accountId,
gatewayClientScopes: params.ctx.GatewayClientScopes,
target: editResult.writeTarget,
});

View File

@ -2169,6 +2169,48 @@ describe("handleCommands /allowlist", () => {
}
});
it("uses the configured default account for omitted-account /allowlist list", async () => {
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "telegram",
source: "test",
plugin: {
...telegramCommandTestPlugin,
config: {
...telegramCommandTestPlugin.config,
defaultAccountId: (cfg) =>
((cfg.channels?.telegram as { defaultAccount?: string } | undefined)?.defaultAccount ??
DEFAULT_ACCOUNT_ID),
},
},
},
]),
);
const cfg = {
commands: { text: true, config: true },
channels: {
telegram: {
defaultAccount: "work",
accounts: { work: { allowFrom: ["123"] } },
},
},
} as OpenClawConfig;
readChannelAllowFromStoreMock.mockResolvedValueOnce([]);
const params = buildPolicyParams("/allowlist list dm", cfg, {
Provider: "telegram",
Surface: "telegram",
AccountId: undefined,
});
const result = await handleCommands(params);
expect(result.shouldContinue).toBe(false);
expect(result.reply?.text).toContain("Channel: telegram (account work)");
expect(result.reply?.text).toContain("DM allowFrom (config): 123");
});
it("blocks config-targeted /allowlist edits when the target account disables writes", async () => {
const previousWriteCount = writeConfigFileMock.mock.calls.length;
const cfg = {
@ -2199,6 +2241,55 @@ describe("handleCommands /allowlist", () => {
expect(writeConfigFileMock.mock.calls.length).toBe(previousWriteCount);
});
it("honors the configured default account when gating omitted-account /allowlist config edits", async () => {
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "telegram",
source: "test",
plugin: {
...telegramCommandTestPlugin,
config: {
...telegramCommandTestPlugin.config,
defaultAccountId: (cfg) =>
((cfg.channels?.telegram as { defaultAccount?: string } | undefined)?.defaultAccount ??
DEFAULT_ACCOUNT_ID),
},
},
},
]),
);
const previousWriteCount = writeConfigFileMock.mock.calls.length;
const cfg = {
commands: { text: true, config: true },
channels: {
telegram: {
defaultAccount: "work",
configWrites: true,
accounts: {
work: { configWrites: false, allowFrom: ["123"] },
},
},
},
} as OpenClawConfig;
readConfigFileSnapshotMock.mockResolvedValueOnce({
valid: true,
parsed: structuredClone(cfg),
});
const params = buildPolicyParams("/allowlist add dm --config 789", cfg, {
Provider: "telegram",
Surface: "telegram",
AccountId: undefined,
});
params.command.senderIsOwner = true;
const result = await handleCommands(params);
expect(result.shouldContinue).toBe(false);
expect(result.reply?.text).toContain("channels.telegram.accounts.work.configWrites=true");
expect(writeConfigFileMock.mock.calls.length).toBe(previousWriteCount);
});
it("blocks allowlist writes from authorized non-owner senders, including cross-channel targets", async () => {
const cfg = {
commands: {