diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a363cf4da0..d10ebc829ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,7 @@ Docs: https://docs.openclaw.ai - TUI: replace the stale-response watchdog notice with plain user-facing copy so stalled replies no longer surface backend or streaming internals. (#77120) Thanks @davemorin. - Security/Windows: validate `SystemRoot`/`WINDIR` env values through the Windows install-root validator and add them to the dangerous-host-env policy when resolving `icacls.exe`/`whoami.exe` for `openclaw security audit`, so workspace `.env` overrides and bare command names cannot redirect Windows ACL helpers to attacker-controlled binaries. (#74458) Thanks @mmaps. - Security/Windows: pin Windows registry-probe `reg.exe` resolution to the canonical Windows install root in install-root probing, so `SystemRoot`/`WINDIR` env overrides cannot redirect registry queries during Windows host detection. (#74454) Thanks @mmaps. +- QQBot: preserve the framework command authorization decision when converting framework command contexts into engine slash command contexts, so downstream slash handlers see `commandAuthorized` matching the channel's resolved `isAuthorizedSender` instead of a hardcoded `true`. (#77453) Thanks @drobison00. ## 2026.5.3-1 diff --git a/extensions/qqbot/src/bridge/commands/framework-context-adapter.test.ts b/extensions/qqbot/src/bridge/commands/framework-context-adapter.test.ts new file mode 100644 index 00000000000..f362d4c9fd8 --- /dev/null +++ b/extensions/qqbot/src/bridge/commands/framework-context-adapter.test.ts @@ -0,0 +1,55 @@ +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types"; +import type { PluginCommandContext } from "openclaw/plugin-sdk/plugin-entry"; +import { describe, expect, it } from "vitest"; +import { buildFrameworkSlashContext } from "./framework-context-adapter.js"; + +function createCommandContext(isAuthorizedSender: boolean): PluginCommandContext { + return { + senderId: "SENDER_OPENID", + channel: "qqbot", + isAuthorizedSender, + args: "on", + commandBody: "/bot-streaming on", + config: {} as OpenClawConfig, + from: "qqbot:c2c:SENDER_OPENID", + requestConversationBinding: async () => undefined, + detachConversationBinding: async () => ({ removed: false }), + getCurrentConversationBinding: async () => null, + } as unknown as PluginCommandContext; +} + +describe("buildFrameworkSlashContext", () => { + it("preserves the framework authorization decision in the slash context", () => { + const authorized = buildFrameworkSlashContext({ + ctx: createCommandContext(true), + account: { + accountId: "default", + enabled: true, + appId: "app", + clientSecret: "secret", + secretSource: "config", + markdownSupport: true, + config: {}, + }, + from: { msgType: "c2c", targetType: "c2c", targetId: "SENDER_OPENID" }, + commandName: "bot-streaming", + }); + const unauthorized = buildFrameworkSlashContext({ + ctx: createCommandContext(false), + account: { + accountId: "default", + enabled: true, + appId: "app", + clientSecret: "secret", + secretSource: "config", + markdownSupport: true, + config: {}, + }, + from: { msgType: "c2c", targetType: "c2c", targetId: "SENDER_OPENID" }, + commandName: "bot-streaming", + }); + + expect(authorized.commandAuthorized).toBe(true); + expect(unauthorized.commandAuthorized).toBe(false); + }); +}); diff --git a/extensions/qqbot/src/bridge/commands/framework-context-adapter.ts b/extensions/qqbot/src/bridge/commands/framework-context-adapter.ts index 437c72cc3f4..fb6e7ccad0e 100644 --- a/extensions/qqbot/src/bridge/commands/framework-context-adapter.ts +++ b/extensions/qqbot/src/bridge/commands/framework-context-adapter.ts @@ -54,7 +54,7 @@ export function buildFrameworkSlashContext({ accountId: account.accountId, appId: account.appId, accountConfig: account.config as unknown as Record, - commandAuthorized: true, + commandAuthorized: ctx.isAuthorizedSender, queueSnapshot: { ...DEFAULT_QUEUE_SNAPSHOT }, }; }