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.operator.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.
{
"github_reviewed": true,
"github_reviewed_at": "2026-03-30T18:52:38Z",
"cwe_ids": [
"CWE-269"
],
"severity": "HIGH",
"nvd_published_at": null
}