Medium severity4.2NVD Advisory· Published Apr 9, 2026· Updated Apr 16, 2026
CVE-2026-35617
CVE-2026-35617
Description
OpenClaw before 2026.3.25 contains an authorization bypass vulnerability in Google Chat group policy enforcement that relies on mutable space display names. Attackers can rebind group policies by changing or colliding space display names to gain unauthorized access to protected resources.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.28 | 2026.3.28 |
Affected products
1Patches
111ea1f67863dGoogle Chat: require stable group ids (#55131)
2 files changed · +175 −8
extensions/googlechat/src/monitor-access.test.ts+125 −0 modified@@ -230,4 +230,129 @@ describe("googlechat inbound access policy", () => { expect(logVerbose).toHaveBeenCalledWith("googlechat: drop control command from users/alice"); }); + + it("does not match group policy by mutable space displayName when the stable id differs", async () => { + primeCommonDefaults(); + createChannelPairingController.mockReturnValue({ + readAllowFromStore: vi.fn(async () => []), + issueChallenge: vi.fn(), + }); + resolveDmGroupAccessWithLists.mockReturnValue({ + decision: "allow", + effectiveAllowFrom: [], + effectiveGroupAllowFrom: ["users/alice"], + }); + resolveMentionGatingWithBypass.mockReturnValue({ + shouldSkip: false, + effectiveWasMentioned: true, + }); + const logVerbose = vi.fn(); + + const { applyGoogleChatInboundAccessPolicy } = await import("./monitor-access.js"); + + await expect( + applyGoogleChatInboundAccessPolicy({ + account: { + accountId: "default", + config: { + groups: { + "Finance Ops": { + users: ["users/alice"], + requireMention: true, + systemPrompt: "finance-only prompt", + }, + }, + }, + } as never, + config: { + channels: { googlechat: {} }, + commands: { useAccessGroups: true }, + } as never, + core: createCore() as never, + space: { name: "spaces/BBB", displayName: "Finance Ops" } as never, + message: { + annotations: [ + { + type: "USER_MENTION", + userMention: { user: { name: "users/app" } }, + }, + ], + } as never, + isGroup: true, + senderId: "users/alice", + senderName: "Alice", + senderEmail: "alice@example.com", + rawBody: "show quarter close status", + logVerbose, + }), + ).resolves.toEqual({ ok: false }); + + expect(logVerbose).toHaveBeenCalledWith( + "Deprecated Google Chat group key detected: group routing now requires stable space ids (spaces/<spaceId>). Update channels.googlechat.groups keys: Finance Ops", + ); + expect(logVerbose).toHaveBeenCalledWith( + "drop group message (deprecated mutable group key matched, space=spaces/BBB)", + ); + }); + + it("fails closed instead of falling back to wildcard when a deprecated room key matches", async () => { + primeCommonDefaults(); + resolveAllowlistProviderRuntimeGroupPolicy.mockReturnValue({ + groupPolicy: "open", + providerMissingFallbackApplied: false, + }); + createChannelPairingController.mockReturnValue({ + readAllowFromStore: vi.fn(async () => []), + issueChallenge: vi.fn(), + }); + resolveDmGroupAccessWithLists.mockReturnValue({ + decision: "allow", + effectiveAllowFrom: [], + effectiveGroupAllowFrom: ["users/alice"], + }); + resolveMentionGatingWithBypass.mockReturnValue({ + shouldSkip: false, + effectiveWasMentioned: true, + }); + const logVerbose = vi.fn(); + + const { applyGoogleChatInboundAccessPolicy } = await import("./monitor-access.js"); + + await expect( + applyGoogleChatInboundAccessPolicy({ + account: { + accountId: "default", + config: { + groupPolicy: "open", + groups: { + "*": { + users: ["users/alice"], + }, + "Finance Ops": { + allow: false, + users: ["users/bob"], + }, + }, + }, + } as never, + config: { + channels: { googlechat: {} }, + commands: { useAccessGroups: true }, + } as never, + core: createCore() as never, + space: { name: "spaces/BBB", displayName: "Finance Ops" } as never, + message: { annotations: [] } as never, + isGroup: true, + senderId: "users/alice", + senderName: "Alice", + senderEmail: "alice@example.com", + rawBody: "show quarter close status", + logVerbose, + }), + ).resolves.toEqual({ ok: false }); + + expect(logVerbose).toHaveBeenCalledWith( + "drop group message (deprecated mutable group key matched, space=spaces/BBB)", + ); + }); });
extensions/googlechat/src/monitor-access.ts+50 −8 modified@@ -78,16 +78,29 @@ function resolveGroupConfig(params: { const entries = groups ?? {}; const keys = Object.keys(entries); if (keys.length === 0) { - return { entry: undefined, allowlistConfigured: false }; - } - const normalizedName = groupName?.trim().toLowerCase(); - const candidates = [groupId, groupName ?? "", normalizedName ?? ""].filter(Boolean); - let entry = candidates.map((candidate) => entries[candidate]).find(Boolean); - if (!entry && normalizedName) { - entry = entries[normalizedName]; + return { entry: undefined, allowlistConfigured: false, deprecatedNameMatch: false }; } + const entry = entries[groupId]; + const normalizedGroupName = groupName?.trim().toLowerCase() ?? ""; + const deprecatedNameMatch = + !entry && + Boolean( + groupName && + keys.some((key) => { + const trimmed = key.trim(); + if (!trimmed || trimmed === "*" || /^spaces\//i.test(trimmed)) { + return false; + } + return trimmed === groupName || trimmed.toLowerCase() === normalizedGroupName; + }), + ); const fallback = entries["*"]; - return { entry: entry ?? fallback, allowlistConfigured: true, fallback }; + return { + entry: deprecatedNameMatch ? undefined : (entry ?? fallback), + allowlistConfigured: true, + fallback, + deprecatedNameMatch, + }; } function extractMentionInfo(annotations: GoogleChatAnnotation[], botUser?: string | null) { @@ -108,6 +121,7 @@ function extractMentionInfo(annotations: GoogleChatAnnotation[], botUser?: strin } const warnedDeprecatedUsersEmailAllowFrom = new Set<string>(); +const warnedMutableGroupKeys = new Set<string>(); function warnDeprecatedUsersEmailEntries(logVerbose: (message: string) => void, entries: string[]) { const deprecated = entries.map((v) => String(v).trim()).filter((v) => /^users\/.+@.+/i.test(v)); @@ -127,6 +141,29 @@ function warnDeprecatedUsersEmailEntries(logVerbose: (message: string) => void, ); } +function warnMutableGroupKeysConfigured( + logVerbose: (message: string) => void, + groups?: Record<string, GoogleChatGroupEntry>, +) { + const mutableKeys = Object.keys(groups ?? {}) + .map((key) => key.trim()) + .filter((key) => key && key !== "*" && !/^spaces\//i.test(key)); + if (mutableKeys.length === 0) { + return; + } + const warningKey = mutableKeys + .map((key) => key.toLowerCase()) + .sort() + .join(","); + if (warnedMutableGroupKeys.has(warningKey)) { + return; + } + warnedMutableGroupKeys.add(warningKey); + logVerbose( + `Deprecated Google Chat group key detected: group routing now requires stable space ids (spaces/<spaceId>). Update channels.googlechat.groups keys: ${mutableKeys.join(", ")}`, + ); +} + export async function applyGoogleChatInboundAccessPolicy(params: { account: ResolvedGoogleChatAccount; config: OpenClawConfig; @@ -185,6 +222,7 @@ export async function applyGoogleChatInboundAccessPolicy(params: { blockedLabel: GROUP_POLICY_BLOCKED_LABEL.space, log: logVerbose, }); + warnMutableGroupKeysConfigured(logVerbose, account.config.groups ?? undefined); const groupConfigResolved = resolveGroupConfig({ groupId: spaceId, groupName: space.displayName ?? null, @@ -195,6 +233,10 @@ export async function applyGoogleChatInboundAccessPolicy(params: { let effectiveWasMentioned: boolean | undefined; if (isGroup) { + if (groupConfigResolved.deprecatedNameMatch) { + logVerbose(`drop group message (deprecated mutable group key matched, space=${spaceId})`); + return { ok: false }; + } const groupAllowlistConfigured = groupConfigResolved.allowlistConfigured; const routeAccess = evaluateGroupRouteAccessForPolicy({ groupPolicy,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/openclaw/openclaw/commit/11ea1f67863d88b6cbcb229dd368a45e07094bffnvdPatchWEB
- github.com/advisories/GHSA-52q4-3xjc-6778ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-52q4-3xjc-6778nvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-35617ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-authorization-bypass-via-group-policy-rebinding-with-mutable-space-displaynamenvdThird Party AdvisoryWEB
News mentions
0No linked articles in our index yet.