| Package | Affected versions | Patched versions |
|---|---|---|
| openclaw | < 2026.3.24 | 2026.3.24 |
| openclaw | < 2026.3.24 | 2026.3.24 |
Fixed in OpenClaw 2026.3.24, the current shipping release.
The shared /allowlist command persists channel authorization config through writeConfigFile(...) but does not re-validate gateway client scopes for internal gateway callers. Because chat.send is intentionally reachable to operator.write callers and still creates a generic command-authorized internal context, an authenticated write-scoped gateway client can indirectly mutate channel allowFrom and groupAllowFrom policy that direct config.patch correctly reserves to operator.admin.
This is not just a generic code smell. The current code already shows the intended boundary by adding sink-side internal admin checks to shared /config and /plugins writes, but /allowlist was left behind.
The gateway's documented scope split is clear:
chat.send is a write-scoped action.The vulnerable path is:
operator.write.chat.send, which is intentionally allowed for that scope.chat.send builds an internal message context with CommandAuthorized: true and carries GatewayClientScopes into the reply pipeline.resolveCommandAuthorization(...) converts that internal message into isAuthorizedSender=true in the common case where no stricter commands.allowFrom override is configured./allowlist add|remove accepts that generic command authorization and proceeds into its config-backed edit path.plugin.allowlist.applyConfigEdit(...), validates the result, and persists it with writeConfigFile(validated.config).operator.admin before the persistent write occurs.That creates a direct control-plane mismatch:
config.patch rejects the same caller with missing scope: operator.admin./allowlist add dm ... or /allowlist add group ... reached through chat.send can still rewrite channel authorization state.Running one of these products? Scan your site to see whether it exposes a vulnerable version.
Scan my site freeoperator.write can persist first-party channel authorization policy./allowlist plumbing./allowlistMirror the existing hardened pattern from /config and /plugins.
Before any config-backed /allowlist add|remove write, require:
operator.admin for internal gateway channelsThis should happen before plugin.allowlist.applyConfigEdit(...) and before writeConfigFile(...).
configWrites policy and pairing-store behavior are useful secondary controls, but they do not replace the missing privilege check between operator.write and operator.admin.