Files
openclaw/extensions/telegram/src/dm-session-key.ts
Peter Steinberger c39abcecda fix(security): report DM isolation from effective routing (#121741)
* fix(security): audit effective DM session ownership

Resolve admitted DM principals through canonical route, account, identity-link, and channel-owned session policy before reporting shared-session risk. Doctor now renders the structured channel security owner instead of duplicating the global-only default-account heuristic.\n\nCloses #121711

* chore(plugin-sdk): refresh API contract baseline

* fix(telegram): preserve direct peer SDK export

* fix(telegram): preserve direct peer resolver signature

* fix(ci): use supported DM audit grouping

* fix(protocol): refresh approval reviewer Swift models
2026-08-10 15:34:37 -07:00

78 lines
2.4 KiB
TypeScript

import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import {
buildAgentSessionKey,
normalizeAccountId,
type ResolvedAgentRoute,
} from "openclaw/plugin-sdk/routing";
export function resolveTelegramDirectPeerId(params: {
chatId: number | string;
senderId?: number | string | null;
}) {
const normalized = params.senderId == null ? "" : String(params.senderId).trim();
return normalized || String(params.chatId);
}
export function resolveTelegramNamedAccountBaseSessionKey(
defaultAccountId: string,
params: {
cfg: OpenClawConfig;
route: Pick<ResolvedAgentRoute, "agentId" | "accountId" | "matchedBy" | "sessionKey">;
chatId: number | string;
isGroup: boolean;
senderId?: string | number | null;
},
): string {
const routeAccountId = normalizeAccountId(params.route.accountId);
const normalizedDefaultAccountId = normalizeAccountId(defaultAccountId);
const isNamedAccountFallback =
routeAccountId !== normalizedDefaultAccountId && params.route.matchedBy === "default";
if (!isNamedAccountFallback || params.isGroup) {
return params.route.sessionKey;
}
return buildAgentSessionKey({
agentId: params.route.agentId,
channel: "telegram",
accountId: params.route.accountId,
peer: {
kind: "direct",
id: resolveTelegramDirectPeerId({
chatId: params.chatId,
senderId: params.senderId,
}),
},
dmScope: "per-account-channel-peer",
identityLinks: params.cfg.session?.identityLinks,
});
}
export function resolveTelegramSecurityDmRoute(
defaultAccountId: string,
params: {
cfg: OpenClawConfig;
accountId: string;
route: ResolvedAgentRoute;
principalId?: string;
},
): { kind: "core" | "isolated" } | { sessionKey: string } {
if (params.principalId !== undefined) {
return {
sessionKey: resolveTelegramNamedAccountBaseSessionKey(defaultAccountId, {
...params,
chatId: params.principalId,
isGroup: false,
senderId: params.principalId,
}),
};
}
const isNamedAccountFallback =
normalizeAccountId(params.accountId) !== normalizeAccountId(defaultAccountId) &&
params.route.matchedBy === "default";
if (isNamedAccountFallback || params.route.dmScope === "per-account-channel-peer") {
return { kind: "isolated" };
}
return params.route.dmScope === "main"
? { sessionKey: params.route.sessionKey }
: { kind: "core" };
}