openclaw/src/cli/message-secret-scope.ts
Josh Avant da34f81ce2
fix(secrets): scope message SecretRef resolution and harden doctor/status paths (#48728)
* fix(secrets): scope message runtime resolution and harden doctor/status

* docs: align message/doctor/status SecretRef behavior notes

* test(cli): accept scoped targetIds wiring in secret-resolution coverage

* fix(secrets): keep scoped allowedPaths isolation and tighten coverage gate

* fix(secrets): avoid default-account coercion in scoped target selection

* test(doctor): cover inactive telegram secretref inspect path

* docs

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

* changelog

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

---------

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-03-17 00:01:34 -05:00

84 lines
2.2 KiB
TypeScript

import { normalizeAccountId } from "../routing/session-key.js";
import { isDeliverableMessageChannel, normalizeMessageChannel } from "../utils/message-channel.js";
function resolveScopedChannelCandidate(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
const normalized = normalizeMessageChannel(value);
if (!normalized || !isDeliverableMessageChannel(normalized)) {
return undefined;
}
return normalized;
}
function resolveChannelFromTargetValue(target: unknown): string | undefined {
if (typeof target !== "string") {
return undefined;
}
const trimmed = target.trim();
if (!trimmed) {
return undefined;
}
const separator = trimmed.indexOf(":");
if (separator <= 0) {
return undefined;
}
return resolveScopedChannelCandidate(trimmed.slice(0, separator));
}
function resolveChannelFromTargets(targets: unknown): string | undefined {
if (!Array.isArray(targets)) {
return undefined;
}
const seen = new Set<string>();
for (const target of targets) {
const channel = resolveChannelFromTargetValue(target);
if (channel) {
seen.add(channel);
}
}
if (seen.size !== 1) {
return undefined;
}
return [...seen][0];
}
function resolveScopedAccountId(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
const trimmed = value.trim();
if (!trimmed) {
return undefined;
}
return normalizeAccountId(trimmed);
}
export function resolveMessageSecretScope(params: {
channel?: unknown;
target?: unknown;
targets?: unknown;
fallbackChannel?: string | null;
accountId?: unknown;
fallbackAccountId?: string | null;
}): {
channel?: string;
accountId?: string;
} {
const channel =
resolveScopedChannelCandidate(params.channel) ??
resolveChannelFromTargetValue(params.target) ??
resolveChannelFromTargets(params.targets) ??
resolveScopedChannelCandidate(params.fallbackChannel);
const accountId =
resolveScopedAccountId(params.accountId) ??
resolveScopedAccountId(params.fallbackAccountId ?? undefined);
return {
...(channel ? { channel } : {}),
...(accountId ? { accountId } : {}),
};
}