From f09cee84f2715b4af26c22e861db2f573cf80424 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 13:21:52 +0100 Subject: [PATCH] refactor: dedupe google chat lowercase helpers --- extensions/googlechat/src/approval-auth.ts | 3 ++- extensions/googlechat/src/auth.ts | 13 ++++++------ extensions/googlechat/src/channel.ts | 14 +++++++------ extensions/googlechat/src/monitor-access.ts | 21 ++++++++++++-------- extensions/googlechat/src/monitor-webhook.ts | 3 ++- extensions/googlechat/src/targets.ts | 9 +++++---- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/extensions/googlechat/src/approval-auth.ts b/extensions/googlechat/src/approval-auth.ts index 84154718cd79..30c223a2d0cb 100644 --- a/extensions/googlechat/src/approval-auth.ts +++ b/extensions/googlechat/src/approval-auth.ts @@ -2,6 +2,7 @@ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers, } from "openclaw/plugin-sdk/approval-auth-runtime"; +import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import { resolveGoogleChatAccount } from "./accounts.js"; import { isGoogleChatUserTarget, normalizeGoogleChatTarget } from "./targets.js"; @@ -10,7 +11,7 @@ function normalizeGoogleChatApproverId(value: string | number): string | undefin if (!normalized || !isGoogleChatUserTarget(normalized)) { return undefined; } - const suffix = normalized.slice("users/".length).trim().toLowerCase(); + const suffix = normalizeLowercaseStringOrEmpty(normalized.slice("users/".length)); if (!suffix || suffix.includes("@")) { return undefined; } diff --git a/extensions/googlechat/src/auth.ts b/extensions/googlechat/src/auth.ts index dd20d1267f73..65ad879b21a6 100644 --- a/extensions/googlechat/src/auth.ts +++ b/extensions/googlechat/src/auth.ts @@ -1,4 +1,5 @@ import { GoogleAuth, OAuth2Client } from "google-auth-library"; +import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; const CHAT_SCOPE = "https://www.googleapis.com/auth/chat.bot"; @@ -113,9 +114,7 @@ export async function verifyGoogleChatRequest(params: { audience, }); const payload = ticket.getPayload(); - const email = String(payload?.email ?? "") - .trim() - .toLowerCase(); + const email = normalizeLowercaseStringOrEmpty(String(payload?.email ?? "")); if (!payload?.email_verified) { return { ok: false, reason: "email not verified" }; } @@ -125,13 +124,13 @@ export async function verifyGoogleChatRequest(params: { if (!ADDON_ISSUER_PATTERN.test(email)) { return { ok: false, reason: `invalid issuer: ${email}` }; } - const expectedAddOnPrincipal = params.expectedAddOnPrincipal?.trim().toLowerCase(); + const expectedAddOnPrincipal = normalizeLowercaseStringOrEmpty( + params.expectedAddOnPrincipal ?? "", + ); if (!expectedAddOnPrincipal) { return { ok: false, reason: "missing add-on principal binding" }; } - const tokenPrincipal = String(payload?.sub ?? "") - .trim() - .toLowerCase(); + const tokenPrincipal = normalizeLowercaseStringOrEmpty(String(payload?.sub ?? "")); if (!tokenPrincipal || tokenPrincipal !== expectedAddOnPrincipal) { return { ok: false, diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index b881263ad2a8..8dc3598a1888 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -21,6 +21,7 @@ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, } from "openclaw/plugin-sdk/status-helpers"; +import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import { googlechatMessageActions } from "./actions.js"; import { googleChatApprovalAuth } from "./approval-auth.js"; import { @@ -73,12 +74,13 @@ const loadGoogleChatChannelRuntime = createLazyRuntimeNamedExport( ); const formatAllowFromEntry = (entry: string) => - entry - .trim() - .replace(/^(googlechat|google-chat|gchat):/i, "") - .replace(/^user:/i, "") - .replace(/^users\//i, "") - .toLowerCase(); + normalizeLowercaseStringOrEmpty( + entry + .trim() + .replace(/^(googlechat|google-chat|gchat):/i, "") + .replace(/^user:/i, "") + .replace(/^users\//i, ""), + ); const googleChatConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: "googlechat", diff --git a/extensions/googlechat/src/monitor-access.ts b/extensions/googlechat/src/monitor-access.ts index bb386dc9d9d0..f6330b14794d 100644 --- a/extensions/googlechat/src/monitor-access.ts +++ b/extensions/googlechat/src/monitor-access.ts @@ -1,5 +1,8 @@ import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound"; -import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "openclaw/plugin-sdk/text-runtime"; import { GROUP_POLICY_BLOCKED_LABEL, createChannelPairingController, @@ -22,7 +25,7 @@ function normalizeUserId(raw?: string | null): string { if (!trimmed) { return ""; } - return trimmed.replace(/^users\//i, "").toLowerCase(); + return normalizeLowercaseStringOrEmpty(trimmed.replace(/^users\//i, "")); } function isEmailLike(value: string): boolean { @@ -40,9 +43,9 @@ export function isSenderAllowed( return true; } const normalizedSenderId = normalizeUserId(senderId); - const normalizedEmail = senderEmail?.trim().toLowerCase() ?? ""; + const normalizedEmail = normalizeLowercaseStringOrEmpty(senderEmail ?? ""); return allowFrom.some((entry) => { - const normalized = String(entry).trim().toLowerCase(); + const normalized = normalizeLowercaseStringOrEmpty(String(entry)); if (!normalized) { return false; } @@ -81,7 +84,7 @@ function resolveGroupConfig(params: { return { entry: undefined, allowlistConfigured: false, deprecatedNameMatch: false }; } const entry = entries[groupId]; - const normalizedGroupName = groupName?.trim().toLowerCase() ?? ""; + const normalizedGroupName = normalizeLowercaseStringOrEmpty(groupName ?? ""); const deprecatedNameMatch = !entry && Boolean( @@ -91,7 +94,9 @@ function resolveGroupConfig(params: { if (!trimmed || trimmed === "*" || /^spaces\//i.test(trimmed)) { return false; } - return trimmed === groupName || trimmed.toLowerCase() === normalizedGroupName; + return ( + trimmed === groupName || normalizeLowercaseStringOrEmpty(trimmed) === normalizedGroupName + ); }), ); const fallback = entries["*"]; @@ -129,7 +134,7 @@ function warnDeprecatedUsersEmailEntries(logVerbose: (message: string) => void, return; } const key = deprecated - .map((v) => v.toLowerCase()) + .map((v) => normalizeLowercaseStringOrEmpty(v)) .toSorted() .join(","); if (warnedDeprecatedUsersEmailAllowFrom.has(key)) { @@ -152,7 +157,7 @@ function warnMutableGroupKeysConfigured( return; } const warningKey = mutableKeys - .map((key) => key.toLowerCase()) + .map((key) => normalizeLowercaseStringOrEmpty(key)) .toSorted() .join(","); if (warnedMutableGroupKeys.has(warningKey)) { diff --git a/extensions/googlechat/src/monitor-webhook.ts b/extensions/googlechat/src/monitor-webhook.ts index a0fb4e1907cc..939bc7c211e5 100644 --- a/extensions/googlechat/src/monitor-webhook.ts +++ b/extensions/googlechat/src/monitor-webhook.ts @@ -1,4 +1,5 @@ import type { IncomingMessage, ServerResponse } from "node:http"; +import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import { readJsonWebhookBodyOrReject, resolveWebhookTargetWithAuthOrReject, @@ -22,7 +23,7 @@ function extractBearerToken(header: unknown): string { : typeof header === "string" ? header : ""; - return authHeader.toLowerCase().startsWith("bearer ") + return normalizeLowercaseStringOrEmpty(authHeader).startsWith("bearer ") ? authHeader.slice("bearer ".length).trim() : ""; } diff --git a/extensions/googlechat/src/targets.ts b/extensions/googlechat/src/targets.ts index f4c5b3051ec9..cf092cfab4d0 100644 --- a/extensions/googlechat/src/targets.ts +++ b/extensions/googlechat/src/targets.ts @@ -1,3 +1,4 @@ +import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; import { findGoogleChatDirectMessage } from "./api.js"; @@ -12,23 +13,23 @@ export function normalizeGoogleChatTarget(raw?: string | null): string | undefin .replace(/^space:(spaces\/)?/i, "spaces/"); if (isGoogleChatUserTarget(normalized)) { const suffix = normalized.slice("users/".length); - return suffix.includes("@") ? `users/${suffix.toLowerCase()}` : normalized; + return suffix.includes("@") ? `users/${normalizeLowercaseStringOrEmpty(suffix)}` : normalized; } if (isGoogleChatSpaceTarget(normalized)) { return normalized; } if (normalized.includes("@")) { - return `users/${normalized.toLowerCase()}`; + return `users/${normalizeLowercaseStringOrEmpty(normalized)}`; } return normalized; } export function isGoogleChatUserTarget(value: string): boolean { - return value.toLowerCase().startsWith("users/"); + return normalizeLowercaseStringOrEmpty(value).startsWith("users/"); } export function isGoogleChatSpaceTarget(value: string): boolean { - return value.toLowerCase().startsWith("spaces/"); + return normalizeLowercaseStringOrEmpty(value).startsWith("spaces/"); } function stripMessageSuffix(target: string): string {