fix(security): scope session tools and webhook secret fallback

This commit is contained in:
Peter Steinberger
2026-02-16 03:43:51 +01:00
parent fbe6d7c701
commit c6c53437f7
21 changed files with 796 additions and 22 deletions

View File

@@ -8,6 +8,8 @@ import { truncateUtf16Safe } from "../../utils.js";
import { jsonResult, readStringParam } from "./common.js";
import {
createAgentToAgentPolicy,
listSpawnedSessionKeys,
resolveEffectiveSessionToolsVisibility,
resolveSessionReference,
SessionListRow,
resolveSandboxedSessionToolContext,
@@ -167,7 +169,6 @@ async function isSpawnedSessionAllowed(params: {
return false;
}
}
export function createSessionsHistoryTool(opts?: {
agentSessionKey?: string;
sandboxed?: boolean;
@@ -189,11 +190,12 @@ export function createSessionsHistoryTool(opts?: {
agentSessionKey: opts?.agentSessionKey,
sandboxed: opts?.sandboxed,
});
const effectiveRequesterKey = requesterInternalKey ?? alias;
const resolvedSession = await resolveSessionReference({
sessionKey: sessionKeyParam,
alias,
mainKey,
requesterInternalKey,
requesterInternalKey: effectiveRequesterKey,
restrictToSpawned,
});
if (!resolvedSession.ok) {
@@ -203,9 +205,9 @@ export function createSessionsHistoryTool(opts?: {
const resolvedKey = resolvedSession.key;
const displayKey = resolvedSession.displayKey;
const resolvedViaSessionId = resolvedSession.resolvedViaSessionId;
if (restrictToSpawned && requesterInternalKey && !resolvedViaSessionId) {
if (restrictToSpawned && !resolvedViaSessionId && resolvedKey !== effectiveRequesterKey) {
const ok = await isSpawnedSessionAllowed({
requesterSessionKey: requesterInternalKey,
requesterSessionKey: effectiveRequesterKey,
targetSessionKey: resolvedKey,
});
if (!ok) {
@@ -215,11 +217,22 @@ export function createSessionsHistoryTool(opts?: {
});
}
}
const visibility = resolveEffectiveSessionToolsVisibility({
cfg,
sandboxed: opts?.sandboxed === true,
});
const a2aPolicy = createAgentToAgentPolicy(cfg);
const requesterAgentId = resolveAgentIdFromSessionKey(requesterInternalKey);
const requesterAgentId = resolveAgentIdFromSessionKey(effectiveRequesterKey);
const targetAgentId = resolveAgentIdFromSessionKey(resolvedKey);
const isCrossAgent = requesterAgentId !== targetAgentId;
if (isCrossAgent && visibility !== "all") {
return jsonResult({
status: "forbidden",
error:
"Session history visibility is restricted. Set tools.sessions.visibility=all to allow cross-agent access.",
});
}
if (isCrossAgent) {
if (!a2aPolicy.enabled) {
return jsonResult({
@@ -236,6 +249,28 @@ export function createSessionsHistoryTool(opts?: {
}
}
if (!isCrossAgent) {
if (visibility === "self" && resolvedKey !== effectiveRequesterKey) {
return jsonResult({
status: "forbidden",
error:
"Session history visibility is restricted to the current session (tools.sessions.visibility=self).",
});
}
if (visibility === "tree" && resolvedKey !== effectiveRequesterKey) {
const spawned = await listSpawnedSessionKeys({
requesterSessionKey: effectiveRequesterKey,
});
if (!spawned.has(resolvedKey)) {
return jsonResult({
status: "forbidden",
error:
"Session history visibility is restricted to the current session tree (tools.sessions.visibility=tree).",
});
}
}
}
const limit =
typeof params.limit === "number" && Number.isFinite(params.limit)
? Math.max(1, Math.floor(params.limit))