Medium severity6.5GHSA Advisory· Published May 5, 2026· Updated May 7, 2026
CVE-2026-43568
CVE-2026-43568
Description
OpenClaw versions 2026.4.5 before 2026.4.10 contain a privilege escalation vulnerability allowing write-scoped operators to modify persistent memory dreaming settings. Attackers with write-scoped gateway access can toggle admin-class configuration mutations through the /dreaming endpoint to escalate privileges.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | >= 2026.4.5, < 2026.4.10 | 2026.4.10 |
Affected products
2Patches
16af17b39e11ffix(dreaming): require admin for persistent gateway toggle (#63872)
5 files changed · +76 −3
CHANGELOG.md+1 −0 modified@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai - Gateway/startup: keep WebSocket RPC available while channels and plugin sidecars start, hold `chat.history` unavailable until startup sidecars finish so synchronous history reads cannot stall startup (reported in #63450), refresh advertised gateway methods after deferred plugin reloads, and enforce the pre-auth WebSocket upgrade budget before the no-handler 503 path so upgrade floods cannot bypass connection limits during that window. (#63480) Thanks @neeravmakwana. - Gateway/tailscale: start Tailscale exposure and the gateway update check before awaiting channel and plugin sidecar startup so remote operators are not locked out when startup sidecars stall. - QQBot/streaming: make block streaming configurable per QQ bot account via `streaming.mode` (`"partial"` | `"off"`, default `"partial"`) instead of hardcoding it off, so responses can be delivered incrementally. (#63746) +- Dreaming/gateway: require `operator.admin` for persistent `/dreaming on|off` changes and treat missing gateway client scopes as unprivileged instead of silently allowing config writes. (#63872) Thanks @mbelinky. ## 2026.4.9
extensions/memory-core/src/dreaming-command.test.ts+47 −1 modified@@ -52,13 +52,17 @@ function createHarness(initialConfig: OpenClawConfig = {}) { }; } -function createCommandContext(args?: string): PluginCommandContext { +function createCommandContext( + args?: string, + overrides?: Partial<Pick<PluginCommandContext, "gatewayClientScopes">>, +): PluginCommandContext { return { channel: "webchat", isAuthorizedSender: true, commandBody: args ? `/dreaming ${args}` : "/dreaming", args, config: {}, + gatewayClientScopes: overrides?.gatewayClientScopes, requestConversationBinding: async () => ({ status: "error", message: "unsupported" }), detachConversationBinding: async () => ({ removed: false }), getCurrentConversationBinding: async () => null, @@ -115,6 +119,48 @@ describe("memory-core /dreaming command", () => { expect(result.text).toContain("Dreaming disabled."); }); + it("blocks unscoped gateway callers from persisting dreaming config", async () => { + const { command, runtime } = createHarness(); + + const result = await command.handler( + createCommandContext("off", { + gatewayClientScopes: [], + }), + ); + + expect(result.text).toContain("requires operator.admin"); + expect(runtime.config.writeConfigFile).not.toHaveBeenCalled(); + }); + + it("blocks write-scoped gateway callers from persisting dreaming config", async () => { + const { command, runtime } = createHarness(); + + const result = await command.handler( + createCommandContext("off", { + gatewayClientScopes: ["operator.write"], + }), + ); + + expect(result.text).toContain("requires operator.admin"); + expect(runtime.config.writeConfigFile).not.toHaveBeenCalled(); + }); + + it("allows admin-scoped gateway callers to persist dreaming config", async () => { + const { command, runtime, getRuntimeConfig } = createHarness(); + + const result = await command.handler( + createCommandContext("on", { + gatewayClientScopes: ["operator.admin"], + }), + ); + + expect(runtime.config.writeConfigFile).toHaveBeenCalledTimes(1); + expect(resolveStoredDreaming(getRuntimeConfig())).toMatchObject({ + enabled: true, + }); + expect(result.text).toContain("Dreaming enabled."); + }); + it("returns status without mutating config", async () => { const { command, runtime } = createHarness({ plugins: {
extensions/memory-core/src/dreaming-command.ts+7 −0 modified@@ -75,6 +75,10 @@ function formatUsage(includeStatus: string): string { ].join("\n"); } +function requiresAdminToMutateDreaming(gatewayClientScopes?: readonly string[]): boolean { + return Array.isArray(gatewayClientScopes) && !gatewayClientScopes.includes("operator.admin"); +} + export function registerDreamingCommand(api: OpenClawPluginApi): void { api.registerCommand({ name: "dreaming", @@ -102,6 +106,9 @@ export function registerDreamingCommand(api: OpenClawPluginApi): void { } if (firstToken === "on" || firstToken === "off") { + if (requiresAdminToMutateDreaming(ctx.gatewayClientScopes)) { + return { text: "⚠️ /dreaming on|off requires operator.admin for gateway clients." }; + } const enabled = firstToken === "on"; const nextConfig = updateDreamingEnabledInConfig(currentConfig, enabled); await api.runtime.config.writeConfigFile(nextConfig);
src/gateway/server-methods/chat.directive-tags.test.ts+20 −1 modified@@ -207,7 +207,7 @@ function extractFirstTextBlock(payload: unknown): string | undefined { } function createScopedCliClient( - scopes: string[], + scopes?: string[], client: Partial<{ id: string; mode: string; @@ -1414,6 +1414,25 @@ describe("chat directive tag stripping for non-streaming final payloads", () => expect(mockState.lastDispatchCtx?.CommandBody).toBe("/scopecheck"); }); + it("normalizes missing gateway caller scopes to an empty array before dispatch", async () => { + createTranscriptFixture("openclaw-chat-send-missing-gateway-client-scopes-"); + mockState.finalText = "ok"; + const respond = vi.fn(); + const context = createChatContext(); + + await runNonStreamingChatSend({ + context, + respond, + idempotencyKey: "idem-gateway-client-scopes-missing", + message: "/scopecheck", + client: createScopedCliClient(), + expectBroadcast: false, + }); + + expect(mockState.lastDispatchCtx?.GatewayClientScopes).toEqual([]); + expect(mockState.lastDispatchCtx?.CommandBody).toBe("/scopecheck"); + }); + it("injects ACP system provenance into the agent-visible body", async () => { createTranscriptFixture("openclaw-chat-send-system-provenance-acp-"); mockState.finalText = "ok";
src/gateway/server-methods/chat.ts+1 −1 modified@@ -1671,7 +1671,7 @@ export const chatHandlers: GatewayRequestHandlers = { SenderId: clientInfo?.id, SenderName: clientInfo?.displayName, SenderUsername: clientInfo?.displayName, - GatewayClientScopes: client?.connect?.scopes, + GatewayClientScopes: client?.connect?.scopes ?? [], }; const agentId = resolveSessionAgentId({
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/openclaw/openclaw/commit/6af17b39e11f5f35e23b7e5a5f71a7d0aa3c7310nvdPatchWEB
- github.com/advisories/GHSA-5gjc-grvm-m88jghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-5gjc-grvm-m88jnvdVendor AdvisoryWEB
- www.vulncheck.com/advisories/openclaw-privilege-escalation-via-memory-dreaming-configuration-in-dreaming-endpointnvdThird Party Advisory
- github.com/openclaw/openclaw/pull/63872ghsaWEB
News mentions
0No linked articles in our index yet.