mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 09:38:39 +00:00
fix(googlechat): deprecate users/<email> allowlists (#16243)
This commit is contained in:
committed by
GitHub
parent
3967ece625
commit
c8424bf29a
@@ -2,21 +2,21 @@ import { describe, expect, it } from "vitest";
|
||||
import { isSenderAllowed } from "./monitor.js";
|
||||
|
||||
describe("isSenderAllowed", () => {
|
||||
it("matches allowlist entries with users/<email>", () => {
|
||||
expect(isSenderAllowed("users/123", "Jane@Example.com", ["users/jane@example.com"])).toBe(true);
|
||||
});
|
||||
|
||||
it("matches allowlist entries with raw email", () => {
|
||||
expect(isSenderAllowed("users/123", "Jane@Example.com", ["jane@example.com"])).toBe(true);
|
||||
});
|
||||
|
||||
it("does not treat users/<email> entries as email allowlist (deprecated form)", () => {
|
||||
expect(isSenderAllowed("users/123", "Jane@Example.com", ["users/jane@example.com"])).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("still matches user id entries", () => {
|
||||
expect(isSenderAllowed("users/abc", "jane@example.com", ["users/abc"])).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects non-matching emails", () => {
|
||||
expect(isSenderAllowed("users/123", "jane@example.com", ["users/other@example.com"])).toBe(
|
||||
false,
|
||||
);
|
||||
it("rejects non-matching raw email entries", () => {
|
||||
expect(isSenderAllowed("users/123", "jane@example.com", ["other@example.com"])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,31 @@ function logVerbose(core: GoogleChatCoreRuntime, runtime: GoogleChatRuntimeEnv,
|
||||
}
|
||||
}
|
||||
|
||||
const warnedDeprecatedUsersEmailAllowFrom = new Set<string>();
|
||||
function warnDeprecatedUsersEmailEntries(
|
||||
core: GoogleChatCoreRuntime,
|
||||
runtime: GoogleChatRuntimeEnv,
|
||||
entries: string[],
|
||||
) {
|
||||
const deprecated = entries.map((v) => String(v).trim()).filter((v) => /^users\/.+@.+/i.test(v));
|
||||
if (deprecated.length === 0) {
|
||||
return;
|
||||
}
|
||||
const key = deprecated
|
||||
.map((v) => v.toLowerCase())
|
||||
.sort()
|
||||
.join(",");
|
||||
if (warnedDeprecatedUsersEmailAllowFrom.has(key)) {
|
||||
return;
|
||||
}
|
||||
warnedDeprecatedUsersEmailAllowFrom.add(key);
|
||||
logVerbose(
|
||||
core,
|
||||
runtime,
|
||||
`Deprecated allowFrom entry detected: "users/<email>" is no longer treated as an email allowlist. Use raw email (alice@example.com) or immutable user id (users/<id>). entries=${deprecated.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeWebhookPath(raw: string): string {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -285,6 +310,11 @@ function normalizeUserId(raw?: string | null): string {
|
||||
return trimmed.replace(/^users\//i, "").toLowerCase();
|
||||
}
|
||||
|
||||
function isEmailLike(value: string): boolean {
|
||||
// Keep this intentionally loose; allowlists are user-provided config.
|
||||
return value.includes("@");
|
||||
}
|
||||
|
||||
export function isSenderAllowed(
|
||||
senderId: string,
|
||||
senderEmail: string | undefined,
|
||||
@@ -300,22 +330,19 @@ export function isSenderAllowed(
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
if (normalized === normalizedSenderId) {
|
||||
return true;
|
||||
|
||||
// Accept `googlechat:<id>` but treat `users/...` as an *ID* only (deprecated `users/<email>`).
|
||||
const withoutPrefix = normalized.replace(/^(googlechat|google-chat|gchat):/i, "");
|
||||
if (withoutPrefix.startsWith("users/")) {
|
||||
return normalizeUserId(withoutPrefix) === normalizedSenderId;
|
||||
}
|
||||
if (normalizedEmail && normalized === normalizedEmail) {
|
||||
return true;
|
||||
|
||||
// Raw email allowlist entries remain supported for usability.
|
||||
if (normalizedEmail && isEmailLike(withoutPrefix)) {
|
||||
return withoutPrefix === normalizedEmail;
|
||||
}
|
||||
if (normalizedEmail && normalized.replace(/^users\//i, "") === normalizedEmail) {
|
||||
return true;
|
||||
}
|
||||
if (normalized.replace(/^users\//i, "") === normalizedSenderId) {
|
||||
return true;
|
||||
}
|
||||
if (normalized.replace(/^(googlechat|google-chat|gchat):/i, "") === normalizedSenderId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return withoutPrefix.replace(/^users\//i, "") === normalizedSenderId;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -473,6 +500,11 @@ async function processMessageWithPipeline(params: {
|
||||
}
|
||||
|
||||
if (groupUsers.length > 0) {
|
||||
warnDeprecatedUsersEmailEntries(
|
||||
core,
|
||||
runtime,
|
||||
groupUsers.map((v) => String(v)),
|
||||
);
|
||||
const ok = isSenderAllowed(
|
||||
senderId,
|
||||
senderEmail,
|
||||
@@ -493,6 +525,7 @@ async function processMessageWithPipeline(params: {
|
||||
? await core.channel.pairing.readAllowFromStore("googlechat").catch(() => [])
|
||||
: [];
|
||||
const effectiveAllowFrom = [...configAllowFrom, ...storeAllowFrom];
|
||||
warnDeprecatedUsersEmailEntries(core, runtime, effectiveAllowFrom);
|
||||
const commandAllowFrom = isGroup ? groupUsers.map((v) => String(v)) : effectiveAllowFrom;
|
||||
const useAccessGroups = config.commands?.useAccessGroups !== false;
|
||||
const senderAllowedForCommands = isSenderAllowed(senderId, senderEmail, commandAllowFrom);
|
||||
|
||||
@@ -55,7 +55,7 @@ async function promptAllowFrom(params: {
|
||||
}): Promise<OpenClawConfig> {
|
||||
const current = params.cfg.channels?.["googlechat"]?.dm?.allowFrom ?? [];
|
||||
const entry = await params.prompter.text({
|
||||
message: "Google Chat allowFrom (user id or email)",
|
||||
message: "Google Chat allowFrom (users/<id> or raw email; avoid users/<email>)",
|
||||
placeholder: "users/123456789, name@example.com",
|
||||
initialValue: current[0] ? String(current[0]) : undefined,
|
||||
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
|
||||
|
||||
Reference in New Issue
Block a user