GitHub Security Advisory: GHSA-vqvg-86cc-cg83
Package: openclaw (NPM)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-862: Missing Authorization
Vulnerable versions: < 2026.3.24
Patched in: 2026.3.24

Summary: OpenClaw: Mutating internal `/allowlist` chat commands missed `operator.admin` scope enforcement

Details:
> Fixed in OpenClaw 2026.3.24, the current shipping release.

**Title**  
Mutating internal `/allowlist` chat commands missed `operator.admin` scope enforcement

**CWE**  
CWE-862 Missing Authorization

**CVSS v3.1**  
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N  
Base score: **6.5 (Medium)**

**Severity Assessment**  
Medium. This is a real authorization flaw in OpenClaw’s internal control plane. The issue does not require host access, trusted local state tampering, or multi-tenant assumptions, but exploitation does require an already authenticated internal Gateway caller with `operator.write`.

**Impact**  
An authenticated internal Gateway caller limited to `operator.write` can perform state-changing `/allowlist` actions without `operator.admin`, even though comparable mutating internal chat commands already require `operator.admin`. The reachable effects are persistent changes to config-backed `allowFrom` entries and pairing-store-backed allowlist entries.

This is not a semantic-modeling complaint and not a generic “trusted operator can do things” claim. It is a missing authorization check inside OpenClaw’s own internal scope model, where peer mutating command surfaces already distinguish `operator.write` from `operator.admin`.

**Affected Component**  
Verified against the latest published GitHub release tag `v2026.3.23` (`ccfeecb6887cd97937e33a71877ad512741e82b2`), published `2026-03-23T23:15:50Z`.

Exact vulnerable path on the shipped tag:
- `src/auto-reply/reply/commands-allowlist.ts:251-254`
  - `/allowlist` authorization uses only `rejectUnauthorizedCommand(...)`.
- `src/auto-reply/reply/commands-allowlist.ts:386-524`
  - mutating config and pairing-store writes happen here, but there is no `requireGatewayClientScopeForInternalChannel(..., operator.admin, ...)`.

Reachability and scope model:
- `src/gateway/method-scopes.ts:94-109`
  - `chat.send` is a write-scoped method.
- `src/gateway/server.chat.gateway-server-chat.test.ts:539-559`
  - existing runtime coverage proves `chat.send` routes slash commands without an agent run.
- `src/auto-reply/command-auth.ts:574-577`
  - internal callers become `senderIsOwner` only when `GatewayClientScopes` includes `operator.admin`.

Comparable internal mutating command paths already enforce `operator.admin`:
- `src/auto-reply/reply/commands-config.ts:64-73`
- `src/auto-reply/reply/commands-mcp.ts:89-96`
- `src/auto-reply/reply/commands-plugins.ts:387-394`
- `src/auto-reply/reply/commands-acp.ts:98-106`

Version history:
- Introduced by commit `555b2578a8cc6e1b93f717496935ead97bfbed8b` (`feat: add /allowlist command`)
- Earliest released affected tag found: `v2026.1.20`
- Latest released affected tag verified: `v2026.3.23`

**Technical Reproduction**  
1. Check out the shipped release tag `v2026.3.23`.
2. Use an internal command context with:
   - `Provider = "webchat"`
   - `Surface = "webchat"`
   - `GatewayClientScopes = ["operator.write"]`
   - `params.command.channel = "webchat"`
3. Route a s