VYPR
Medium severity5.3NVD Advisory· Published May 6, 2026· Updated May 7, 2026

CVE-2026-43583

CVE-2026-43583

Description

OpenClaw versions 2026.4.10 before 2026.4.14 fail to persist session context during delivery queue recovery for media replay. Attackers can exploit recovered queued outbound media to bypass group tool policy enforcement and weaken channel media restrictions after service restart or recovery.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
>= 2026.4.10, < 2026.4.142026.4.14

Affected products

2
  • OpenClaw/Openclawreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*range: >=2026.4.10,<2026.4.14

Patches

1
48aae82bbc19

fix(outbound): replay queued session context (#66025)

https://github.com/openclaw/openclawAgustin RiveraApr 13, 2026via ghsa
6 files changed · +68 0
  • CHANGELOG.md+1 0 modified
    @@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
     - Browser/CDP: let local attach-only `manual-cdp` profiles reuse the local loopback CDP control plane under strict default policy and remote-class probe timeouts, so tabs/snapshot stop falsely reporting a live local browser session as not running. (#65611, #66080) Thanks @mbelinky.
     - Cron/scheduler: stop inventing short retries when cron next-run calculation returns no valid future slot, and keep a maintenance wake armed so enabled unscheduled jobs recover without entering a refire loop. (#66019, #66083) Thanks @mbelinky.
     - Cron/scheduler: preserve the active error-backoff floor when maintenance repair recomputes a missing cron next-run, so recurring errored jobs do not resume early after a transient next-run resolution failure. (#66019, #66083, #66113) Thanks @mbelinky.
    +- Outbound/delivery-queue: persist the originating outbound `session` context on queued delivery entries and replay it during recovery, so write-ahead-queued sends keep their original outbound media policy context after restart instead of evaluating against a missing session. (#66025) Thanks @eleqtrizit.
     
     ## 2026.4.12
     
    
  • src/infra/outbound/deliver.ts+1 0 modified
    @@ -507,6 +507,7 @@ export async function deliverOutboundPayloads(
             forceDocument: params.forceDocument,
             silent: params.silent,
             mirror: params.mirror,
    +        session: params.session,
             gatewayClientScopes: params.gatewayClientScopes,
           }).catch(() => null); // Best-effort — don't block delivery if queue write fails.
     
    
  • src/infra/outbound/delivery-queue.recovery.test.ts+18 0 modified
    @@ -165,6 +165,15 @@ describe("delivery-queue recovery", () => {
               text: "a",
               mediaUrls: ["https://example.com/a.png"],
             },
    +        session: {
    +          key: "agent:main:main",
    +          agentId: "agent-main",
    +          requesterAccountId: "acct-1",
    +          requesterSenderId: "sender-1",
    +          requesterSenderName: "Sender One",
    +          requesterSenderUsername: "sender.one",
    +          requesterSenderE164: "+15551234567",
    +        },
           },
           tmpDir(),
         );
    @@ -183,6 +192,15 @@ describe("delivery-queue recovery", () => {
               text: "a",
               mediaUrls: ["https://example.com/a.png"],
             },
    +        session: {
    +          key: "agent:main:main",
    +          agentId: "agent-main",
    +          requesterAccountId: "acct-1",
    +          requesterSenderId: "sender-1",
    +          requesterSenderName: "Sender One",
    +          requesterSenderUsername: "sender.one",
    +          requesterSenderE164: "+15551234567",
    +        },
           }),
         );
       });
    
  • src/infra/outbound/delivery-queue-recovery.ts+1 0 modified
    @@ -104,6 +104,7 @@ function buildRecoveryDeliverParams(entry: QueuedDelivery, cfg: OpenClawConfig)
         forceDocument: entry.forceDocument,
         silent: entry.silent,
         mirror: entry.mirror,
    +    session: entry.session,
         gatewayClientScopes: entry.gatewayClientScopes,
         skipQueue: true, // Prevent re-enqueueing during recovery.
       } satisfies Parameters<DeliverFn>[0];
    
  • src/infra/outbound/delivery-queue.storage.test.ts+43 0 modified
    @@ -33,6 +33,12 @@ describe("delivery-queue storage", () => {
                 text: "hello",
                 mediaUrls: ["https://example.com/file.png"],
               },
    +          session: {
    +            key: "agent:main:main",
    +            agentId: "agent-main",
    +            requesterAccountId: "acct-1",
    +            requesterSenderId: "sender-1",
    +          },
             },
             tmpDir(),
           );
    @@ -53,6 +59,12 @@ describe("delivery-queue storage", () => {
               text: "hello",
               mediaUrls: ["https://example.com/file.png"],
             },
    +        session: {
    +          key: "agent:main:main",
    +          agentId: "agent-main",
    +          requesterAccountId: "acct-1",
    +          requesterSenderId: "sender-1",
    +        },
             retryCount: 0,
           });
           expect(entry.payloads).toEqual([{ text: "hello" }]);
    @@ -169,6 +181,37 @@ describe("delivery-queue storage", () => {
           expect(entry.gatewayClientScopes).toEqual(["operator.write"]);
         });
     
    +    it("persists session context for recovery replay", async () => {
    +      const id = await enqueueTextDelivery(
    +        {
    +          channel: "telegram",
    +          to: "2",
    +          payloads: [{ text: "b" }],
    +          session: {
    +            key: "agent:main:main",
    +            agentId: "agent-main",
    +            requesterAccountId: "acct-1",
    +            requesterSenderId: "sender-1",
    +            requesterSenderName: "Sender One",
    +            requesterSenderUsername: "sender.one",
    +            requesterSenderE164: "+15551234567",
    +          },
    +        },
    +        tmpDir(),
    +      );
    +
    +      const entry = readQueuedEntry(tmpDir(), id);
    +      expect(entry.session).toEqual({
    +        key: "agent:main:main",
    +        agentId: "agent-main",
    +        requesterAccountId: "acct-1",
    +        requesterSenderId: "sender-1",
    +        requesterSenderName: "Sender One",
    +        requesterSenderUsername: "sender.one",
    +        requesterSenderE164: "+15551234567",
    +      });
    +    });
    +
         it("backfills lastAttemptAt for legacy retry entries during load", async () => {
           const id = await enqueueTextDelivery({
             channel: "whatsapp",
    
  • src/infra/outbound/delivery-queue-storage.ts+4 0 modified
    @@ -4,6 +4,7 @@ import type { ReplyPayload } from "../../auto-reply/types.js";
     import { resolveStateDir } from "../../config/paths.js";
     import { generateSecureUuid } from "../secure-random.js";
     import type { OutboundMirror } from "./mirror.js";
    +import type { OutboundSessionContext } from "./session-context.js";
     import type { OutboundChannel } from "./targets.js";
     
     const QUEUE_DIRNAME = "delivery-queue";
    @@ -26,6 +27,8 @@ export type QueuedDeliveryPayload = {
       forceDocument?: boolean;
       silent?: boolean;
       mirror?: OutboundMirror;
    +  /** Session context needed to preserve outbound media policy on recovery. */
    +  session?: OutboundSessionContext;
       /** Gateway caller scopes at enqueue time, preserved for recovery replay. */
       gatewayClientScopes?: readonly string[];
     };
    @@ -144,6 +147,7 @@ export async function enqueueDelivery(
         forceDocument: params.forceDocument,
         silent: params.silent,
         mirror: params.mirror,
    +    session: params.session,
         gatewayClientScopes: params.gatewayClientScopes,
         retryCount: 0,
       });
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

6

News mentions

0

No linked articles in our index yet.