VYPR
Medium severity4.2NVD Advisory· Published May 11, 2026· Updated May 13, 2026

CVE-2026-44991

CVE-2026-44991

Description

OpenClaw before 2026.4.21 contains an authorization bypass vulnerability in command-auth.ts that allows non-owner senders to execute owner-enforced slash commands when wildcard inbound senders are configured without explicit owner allowFrom settings. Attackers can exploit this by sending commands like /send, /config, or /debug on affected channels to bypass owner-only command authorization checks.

Affected products

1

Patches

2
995febb7b1e8

fix: require owner identity for owner-enforced commands (#69774)

https://github.com/openclaw/openclawDevin RobisonApr 21, 2026via nvd-ref
3 files changed · +47 3
  • CHANGELOG.md+4 0 modified
    @@ -14,6 +14,10 @@ Docs: https://docs.openclaw.ai
     - Image generation: log failed provider/model candidates at warn level before automatic provider fallback, so OpenAI image failures are visible in the gateway log even when a later provider succeeds.
     - npm/install: mirror the `node-domexception` alias into root `package.json` `overrides`, so npm installs stop surfacing the deprecated `google-auth-library -> gaxios -> node-fetch -> fetch-blob -> node-domexception` chain pulled through Pi/Google runtime deps. Thanks @vincentkoc.
     
    +### Fixes
    +
    +- Auth/commands: require owner identity (an owner-candidate match or internal `operator.admin`) for owner-enforced commands instead of treating wildcard channel `allowFrom` or empty owner-candidate lists as sufficient, so non-owner senders can no longer reach owner-only commands through a permissive fallback when `enforceOwnerForCommands=true` and `commands.ownerAllowFrom` is unset. (#69774) Thanks @drobison00.
    +
     ## 2026.4.20
     
     ### Changes
    
  • src/auto-reply/command-auth.ts+1 3 modified
    @@ -706,9 +706,7 @@ export function resolveCommandAuthorization(params: {
           ? true
           : ownerAllowlistConfigured
             ? senderIsOwner
    -        : ownerState.allowAll ||
    -          ownerState.ownerCandidatesForCommands.length === 0 ||
    -          Boolean(matchedCommandOwner);
    +        : senderIsOwnerByScope || Boolean(matchedCommandOwner);
       const isAuthorizedSender = resolveCommandSenderAuthorization({
         commandAuthorized,
         isOwnerForCommands,
    
  • src/auto-reply/command-control.test.ts+42 0 modified
    @@ -159,6 +159,48 @@ describe("resolveCommandAuthorization", () => {
         expect(otherAuth.isAuthorizedSender).toBe(false);
       });
     
    +  it("rejects wildcard channel senders when the plugin enforces owner-only commands", () => {
    +    setActivePluginRegistry(
    +      createTestRegistry([
    +        {
    +          pluginId: "discord",
    +          plugin: {
    +            ...createOutboundTestPlugin({
    +              id: "discord",
    +              outbound: { deliveryMode: "direct" },
    +            }),
    +            commands: { enforceOwnerForCommands: true },
    +            config: {
    +              listAccountIds: () => ["default"],
    +              resolveAccount: () => ({}),
    +              resolveAllowFrom: () => ["*"],
    +              formatAllowFrom,
    +            },
    +          },
    +          source: "test",
    +        },
    +      ]),
    +    );
    +    const cfg = {
    +      channels: { discord: { allowFrom: ["*"] } },
    +    } as OpenClawConfig;
    +
    +    const auth = resolveCommandAuthorization({
    +      ctx: {
    +        Provider: "discord",
    +        Surface: "discord",
    +        ChatType: "direct",
    +        From: "discord:123",
    +        SenderId: "123",
    +      } as MsgContext,
    +      cfg,
    +      commandAuthorized: true,
    +    });
    +
    +    expect(auth.senderIsOwner).toBe(false);
    +    expect(auth.isAuthorizedSender).toBe(false);
    +  });
    +
       it("uses explicit owner allowlist when allowFrom is empty", () => {
         const cfg = {
           commands: { ownerAllowFrom: ["whatsapp:+15551234567"] },
    
2aa93d44a1b2

fix: require owner identity for owner-enforced commands (#69774)

https://github.com/openclaw/openclawDevin RobisonApr 21, 2026via nvd-ref
3 files changed · +47 3
  • CHANGELOG.md+4 0 modified
    @@ -9,6 +9,10 @@ Docs: https://docs.openclaw.ai
     - Onboard/wizard: simplify the security disclaimer copy (drop the yellow banner and warning icon in favor of plain-prose paragraphs), and flip remaining onboarding pickers with long dynamic option lists to searchable autocompletes (search provider, plugin configure, model provider filter).
     - Ollama/onboard: populate the cloud-only model list from `ollama.com/api/tags` so `openclaw onboard` reflects the live cloud catalog instead of a static three-model seed; cap the discovered list at 500 and fall back to the previous hardcoded suggestions when ollama.com is unreachable or returns no models. (#68463) Thanks @BruceMacD.
     
    +### Fixes
    +
    +- Auth/commands: require owner identity (an owner-candidate match or internal `operator.admin`) for owner-enforced commands instead of treating wildcard channel `allowFrom` or empty owner-candidate lists as sufficient, so non-owner senders can no longer reach owner-only commands through a permissive fallback when `enforceOwnerForCommands=true` and `commands.ownerAllowFrom` is unset. (#69774) Thanks @drobison00.
    +
     ## 2026.4.20
     
     ### Changes
    
  • src/auto-reply/command-auth.ts+1 3 modified
    @@ -706,9 +706,7 @@ export function resolveCommandAuthorization(params: {
           ? true
           : ownerAllowlistConfigured
             ? senderIsOwner
    -        : ownerState.allowAll ||
    -          ownerState.ownerCandidatesForCommands.length === 0 ||
    -          Boolean(matchedCommandOwner);
    +        : senderIsOwnerByScope || Boolean(matchedCommandOwner);
       const isAuthorizedSender = resolveCommandSenderAuthorization({
         commandAuthorized,
         isOwnerForCommands,
    
  • src/auto-reply/command-control.test.ts+42 0 modified
    @@ -159,6 +159,48 @@ describe("resolveCommandAuthorization", () => {
         expect(otherAuth.isAuthorizedSender).toBe(false);
       });
     
    +  it("rejects wildcard channel senders when the plugin enforces owner-only commands", () => {
    +    setActivePluginRegistry(
    +      createTestRegistry([
    +        {
    +          pluginId: "discord",
    +          plugin: {
    +            ...createOutboundTestPlugin({
    +              id: "discord",
    +              outbound: { deliveryMode: "direct" },
    +            }),
    +            commands: { enforceOwnerForCommands: true },
    +            config: {
    +              listAccountIds: () => ["default"],
    +              resolveAccount: () => ({}),
    +              resolveAllowFrom: () => ["*"],
    +              formatAllowFrom,
    +            },
    +          },
    +          source: "test",
    +        },
    +      ]),
    +    );
    +    const cfg = {
    +      channels: { discord: { allowFrom: ["*"] } },
    +    } as OpenClawConfig;
    +
    +    const auth = resolveCommandAuthorization({
    +      ctx: {
    +        Provider: "discord",
    +        Surface: "discord",
    +        ChatType: "direct",
    +        From: "discord:123",
    +        SenderId: "123",
    +      } as MsgContext,
    +      cfg,
    +      commandAuthorized: true,
    +    });
    +
    +    expect(auth.senderIsOwner).toBe(false);
    +    expect(auth.isAuthorizedSender).toBe(false);
    +  });
    +
       it("uses explicit owner allowlist when allowFrom is empty", () => {
         const cfg = {
           commands: { ownerAllowFrom: ["whatsapp:+15551234567"] },
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

4

News mentions

0

No linked articles in our index yet.