Overview
CVE-2026-32911 describes an authorization bypass in OpenClaw versions 2026.2.22 and 2026.2.23 where a dmPolicy set to allowlist with an empty allowedUserIds can result in an open gate to dispatch unauthorized messages to downstream agents and tools. This is a classic Broken Object Level Authorization (BOLA) flaw (CWE-863) that can let attackers with limited or no resource ownership access act on protected objects. The impact is broader when such policy checks are embedded in plugins or modules that handle inter-service messaging, effectively undermining per-object security guarantees in a microservice or plugin-rich Go (Gin) environment.
Affected Versions
OpenClaw 2026.2.22-2026.2.23
Code Fix Example
Go (Gin) API Security Remediation
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Policy struct {
Policy string
AllowedUserIDs []string
}
func getUserID(c *gin.Context) string {
if v, ok := c.Get("userID"); ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// Vulnerable: empty allowlist path grants access (explicit bypass)
func vulnerableAuthorize(p Policy, userID string) bool {
if p.Policy == "allowlist" {
if len(p.AllowedUserIDs) == 0 {
// Vulnerability: empty allowlist opens access
return true
}
for _, id := range p.AllowedUserIDs {
if id == userID {
return true
}
}
return false
}
return false
}
// Fixed: empty allowlist must deny by default
func fixedAuthorize(p Policy, userID string) bool {
if p.Policy == "allowlist" {
if len(p.AllowedUserIDs) == 0 {
// Deny when allowlist is empty
return false
}
for _, id := range p.AllowedUserIDs {
if id == userID {
return true
}
}
return false
}
return false
}
// Vulnerable route demonstrating the bypass
func vulnerableHandler(c *gin.Context) {
userID := getUserID(c)
p := Policy{Policy: "allowlist", AllowedUserIDs: []string{}} // empty allowlist
if !vulnerableAuthorize(p, userID) {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.String(http.StatusOK, "VULNERABLE: Access granted to resource %s", c.Param("id"))
}
// Fixed route demonstrating proper guard
func fixedHandler(c *gin.Context) {
userID := getUserID(c)
p := Policy{Policy: "allowlist", AllowedUserIDs: []string{}} // empty allowlist
if !fixedAuthorize(p, userID) {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.String(http.StatusOK, "FIXED: Access granted to resource %s", c.Param("id"))
}
func main() {
r := gin.Default()
// Demo: inject a known user; in real apps, use proper authentication middleware
r.Use(func(c *gin.Context) {
c.Set("userID", "alice")
c.Next()
})
r.GET("/vuln/doc/:id", vulnerableHandler)
r.GET("/fix/doc/:id", fixedHandler)
r.Run(":8080")
}