fix(googlechat): deprecate users/<email> allowlists (#16243)

This commit is contained in:
Peter Steinberger
2026-02-14 15:31:26 +01:00
committed by GitHub
parent 3967ece625
commit c8424bf29a
5 changed files with 59 additions and 24 deletions

View File

@@ -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);