VYPR
High severity8.8NVD Advisory· Published May 6, 2026· Updated May 7, 2026

CVE-2026-44110

CVE-2026-44110

Description

OpenClaw before 2026.4.15 contains an authorization bypass vulnerability in Matrix room control-command authorization that trusts DM pairing-store entries. Attackers with DM-paired sender IDs can execute room control commands without being in configured allowlists by posting in bot rooms, potentially enabling privileged OpenClaw behavior.

Affected products

2
  • OpenClaw/Openclawinferred2 versions
    <2026.4.15+ 1 more
    • (no CPE)range: <2026.4.15
    • cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*range: <2026.4.15

Patches

2
2bfd808a8311

fix(matrix): skip pairing-store reads for room auth (#67325)

https://github.com/openclaw/openclawGustavo Madeira SantanaApr 15, 2026via nvd-ref
3 files changed · +5 2
  • CHANGELOG.md+1 0 modified
    @@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
     - Plugins/bundled channels: partition bundled channel lazy caches by active bundled root so `OPENCLAW_BUNDLED_PLUGINS_DIR` flips stop reusing stale plugin, setup, secrets, and runtime state. (#67200) Thanks @gumadeiras.
     - Packaging/plugins: prune common test/spec cargo from bundled plugin runtime dependencies and fail npm release validation if packaged test cargo reappears, keeping published tarballs leaner without plugin-specific special cases. (#67275) thanks @gumadeiras.
     - Agents/context + Memory: trim default startup/skills prompt budgets, cap `memory_get` excerpts by default with explicit continuation metadata, and keep QMD reads aligned with the same bounded excerpt contract so long sessions pull less context by default without losing deterministic follow-up reads.
    +- Matrix/commands: skip DM pairing-store reads on room traffic now that room control-command authorization ignores pairing-store entries, keeping the room path narrower without changing room auth behavior. (#67325) Thanks @gumadeiras.
     
     ## 2026.4.15-beta.1
     
    
  • extensions/matrix/src/matrix/monitor/handler.test.ts+3 1 modified
    @@ -446,10 +446,11 @@ describe("matrix monitor handler pairing account scope", () => {
       });
     
       it("blocks room control commands from DM-only paired senders", async () => {
    +    const readAllowFromStore = vi.fn(async () => ["@user:example.org"]);
         const { handler, finalizeInboundContext, recordInboundSession } =
           createMatrixHandlerTestHarness({
             isDirectMessage: false,
    -        readAllowFromStore: vi.fn(async () => ["@user:example.org"]),
    +        readAllowFromStore,
             roomsConfig: {
               "!room:example.org": { requireMention: false },
             },
    @@ -473,6 +474,7 @@ describe("matrix monitor handler pairing account scope", () => {
     
         expect(recordInboundSession).not.toHaveBeenCalled();
         expect(finalizeInboundContext).not.toHaveBeenCalled();
    +    expect(readAllowFromStore).not.toHaveBeenCalled();
       });
     
       it("processes room messages mentioned via displayName in formatted_body", async () => {
    
  • extensions/matrix/src/matrix/monitor/handler.ts+1 1 modified
    @@ -586,7 +586,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
               senderNamePromise ??= getMemberDisplayName(roomId, senderId).catch(() => senderId);
               return await senderNamePromise;
             };
    -        const storeAllowFrom = await readStoreAllowFrom();
    +        const storeAllowFrom = isDirectMessage ? await readStoreAllowFrom() : [];
             const roomUsers = roomConfig?.users ?? [];
             const accessState = resolveMatrixMonitorAccessState({
               allowFrom,
    
f8705f512b09

fix(matrix): block DM pairing-store entries from authorizing room control commands [AI-assisted] (#67294)

https://github.com/openclaw/openclawPavan Kumar GondhiApr 15, 2026via nvd-ref
4 files changed · +71 7
  • CHANGELOG.md+1 0 modified
    @@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
     
     ### Fixes
     
    +- fix(matrix): block DM pairing-store entries from authorizing room control commands [AI-assisted]. (#67294) Thanks @pgondhi987.
     - Docker/build: verify `@matrix-org/matrix-sdk-crypto-nodejs` native bindings with `find` under `node_modules` instead of a hardcoded `.pnpm/...` path so pnpm v10+ virtual-store layouts no longer fail the image build. (#67143) thanks @ly85206559.
     - Matrix/E2EE: keep startup bootstrap conservative for passwordless token-auth bots, still attempt the guarded repair pass without requiring `channels.matrix.password`, and document the remaining password-UIA limitation. (#66228) Thanks @SARAMALI15792.
     - Cron/announce delivery: suppress mixed-content isolated cron announce replies that end with `NO_REPLY` so trailing silent sentinels no longer leak summary text to the target channel. (#65004) thanks @neo1027144-creator.
    
  • extensions/matrix/src/matrix/monitor/access-state.test.ts+19 0 modified
    @@ -28,6 +28,25 @@ describe("resolveMatrixMonitorAccessState", () => {
         ]);
       });
     
    +  it("does not let DM pairing-store entries authorize room control commands", () => {
    +    const state = resolveMatrixMonitorAccessState({
    +      allowFrom: [],
    +      storeAllowFrom: ["@attacker:example.org"],
    +      groupAllowFrom: [],
    +      roomUsers: [],
    +      senderId: "@attacker:example.org",
    +      isRoom: true,
    +    });
    +
    +    expect(state.effectiveAllowFrom).toEqual(["@attacker:example.org"]);
    +    expect(state.directAllowMatch.allowed).toBe(true);
    +    expect(state.commandAuthorizers).toEqual([
    +      { configured: false, allowed: false },
    +      { configured: false, allowed: false },
    +      { configured: false, allowed: false },
    +    ]);
    +  });
    +
       it("keeps room-user matching disabled for dm traffic", () => {
         const state = resolveMatrixMonitorAccessState({
           allowFrom: [],
    
  • extensions/matrix/src/matrix/monitor/access-state.ts+21 7 modified
    @@ -1,19 +1,24 @@
     import { normalizeMatrixAllowList, resolveMatrixAllowListMatch } from "./allowlist.js";
    -import type { MatrixAllowListMatch } from "./allowlist.js";
     
     type MatrixCommandAuthorizer = {
       configured: boolean;
       allowed: boolean;
     };
     
    +type MatrixMonitorAllowListMatch = {
    +  allowed: boolean;
    +  matchKey?: string;
    +  matchSource?: "wildcard" | "id" | "prefixed-id" | "prefixed-user";
    +};
    +
     export type MatrixMonitorAccessState = {
       effectiveAllowFrom: string[];
       effectiveGroupAllowFrom: string[];
       effectiveRoomUsers: string[];
       groupAllowConfigured: boolean;
    -  directAllowMatch: MatrixAllowListMatch;
    -  roomUserMatch: MatrixAllowListMatch | null;
    -  groupAllowMatch: MatrixAllowListMatch | null;
    +  directAllowMatch: MatrixMonitorAllowListMatch;
    +  roomUserMatch: MatrixMonitorAllowListMatch | null;
    +  groupAllowMatch: MatrixMonitorAllowListMatch | null;
       commandAuthorizers: [MatrixCommandAuthorizer, MatrixCommandAuthorizer, MatrixCommandAuthorizer];
     };
     
    @@ -25,12 +30,14 @@ export function resolveMatrixMonitorAccessState(params: {
       senderId: string;
       isRoom: boolean;
     }): MatrixMonitorAccessState {
    +  const configuredAllowFrom = normalizeMatrixAllowList(params.allowFrom);
       const effectiveAllowFrom = normalizeMatrixAllowList([
    -    ...params.allowFrom,
    +    ...configuredAllowFrom,
         ...params.storeAllowFrom,
       ]);
       const effectiveGroupAllowFrom = normalizeMatrixAllowList(params.groupAllowFrom);
       const effectiveRoomUsers = normalizeMatrixAllowList(params.roomUsers);
    +  const commandAllowFrom = params.isRoom ? configuredAllowFrom : effectiveAllowFrom;
     
       const directAllowMatch = resolveMatrixAllowListMatch({
         allowList: effectiveAllowFrom,
    @@ -50,6 +57,13 @@ export function resolveMatrixMonitorAccessState(params: {
               userId: params.senderId,
             })
           : null;
    +  const commandAllowMatch =
    +    commandAllowFrom.length > 0
    +      ? resolveMatrixAllowListMatch({
    +          allowList: commandAllowFrom,
    +          userId: params.senderId,
    +        })
    +      : null;
     
       return {
         effectiveAllowFrom,
    @@ -61,8 +75,8 @@ export function resolveMatrixMonitorAccessState(params: {
         groupAllowMatch,
         commandAuthorizers: [
           {
    -        configured: effectiveAllowFrom.length > 0,
    -        allowed: directAllowMatch.allowed,
    +        configured: commandAllowFrom.length > 0,
    +        allowed: commandAllowMatch?.allowed ?? false,
           },
           {
             configured: effectiveRoomUsers.length > 0,
    
  • extensions/matrix/src/matrix/monitor/handler.test.ts+30 0 modified
    @@ -445,6 +445,36 @@ describe("matrix monitor handler pairing account scope", () => {
         expect(recordInboundSession).not.toHaveBeenCalled();
       });
     
    +  it("blocks room control commands from DM-only paired senders", async () => {
    +    const { handler, finalizeInboundContext, recordInboundSession } =
    +      createMatrixHandlerTestHarness({
    +        isDirectMessage: false,
    +        readAllowFromStore: vi.fn(async () => ["@user:example.org"]),
    +        roomsConfig: {
    +          "!room:example.org": { requireMention: false },
    +        },
    +        shouldHandleTextCommands: () => true,
    +        hasControlCommand: () => true,
    +        cfg: {
    +          commands: {
    +            useAccessGroups: true,
    +          },
    +        },
    +        getMemberDisplayName: async () => "sender",
    +      });
    +
    +    await handler(
    +      "!room:example.org",
    +      createMatrixTextMessageEvent({
    +        eventId: "$dm-only-room-command",
    +        body: "/config",
    +      }),
    +    );
    +
    +    expect(recordInboundSession).not.toHaveBeenCalled();
    +    expect(finalizeInboundContext).not.toHaveBeenCalled();
    +  });
    +
       it("processes room messages mentioned via displayName in formatted_body", async () => {
         const recordInboundSession = vi.fn(async () => {});
         const { handler } = createMatrixHandlerTestHarness({
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

8

News mentions

9