mirror of https://github.com/openclaw/openclaw.git
harden session-status tool visibility guard for all callers
This commit is contained in:
parent
5cca380840
commit
4d369a3400
|
|
@ -731,6 +731,47 @@ describe("session_status tool", () => {
|
|||
expect(updateSessionStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("blocks unsandboxed same-agent bare main session_status outside self visibility", async () => {
|
||||
resetSessionStore({
|
||||
"agent:main:main": {
|
||||
sessionId: "s-parent",
|
||||
updatedAt: 10,
|
||||
providerOverride: "anthropic",
|
||||
modelOverride: "claude-sonnet-4-6",
|
||||
},
|
||||
"agent:main:subagent:child": {
|
||||
sessionId: "s-child",
|
||||
updatedAt: 20,
|
||||
},
|
||||
});
|
||||
mockConfig = {
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
tools: {
|
||||
sessions: { visibility: "self" },
|
||||
agentToAgent: { enabled: true, allow: ["*"] },
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openai/gpt-5.4" },
|
||||
models: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const tool = getSessionStatusTool("agent:main:subagent:child");
|
||||
|
||||
await expect(
|
||||
tool.execute("call-self-visibility-bare-main", {
|
||||
sessionKey: "main",
|
||||
model: "default",
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
"Session status visibility is restricted to the current session (tools.sessions.visibility=self).",
|
||||
);
|
||||
|
||||
expect(updateSessionStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("blocks unsandboxed same-agent session_status outside tree visibility before mutation", async () => {
|
||||
resetSessionStore({
|
||||
"agent:main:main": {
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ export function createSessionStatusTool(opts?: {
|
|||
const requesterAgentId = resolveAgentIdFromSessionKey(
|
||||
opts?.agentSessionKey ?? effectiveRequesterKey,
|
||||
);
|
||||
const visibilityRequesterKey = effectiveRequesterKey.trim();
|
||||
const visibilityRequesterKey = (opts?.agentSessionKey ?? effectiveRequesterKey).trim();
|
||||
const usesLegacyMainAlias = alias === mainKey;
|
||||
const isLegacyMainVisibilityKey = (sessionKey: string) => {
|
||||
const trimmed = sessionKey.trim();
|
||||
|
|
@ -282,7 +282,8 @@ export function createSessionStatusTool(opts?: {
|
|||
|
||||
const requestedKeyParam = readStringParam(params, "sessionKey");
|
||||
let requestedKeyRaw = requestedKeyParam ?? opts?.agentSessionKey;
|
||||
let resolvedTargetViaSessionId = false;
|
||||
const requestedKeyInput = requestedKeyRaw?.trim() ?? "";
|
||||
let resolvedViaSessionId = false;
|
||||
if (!requestedKeyRaw?.trim()) {
|
||||
throw new Error("sessionKey required");
|
||||
}
|
||||
|
|
@ -357,7 +358,7 @@ export function createSessionStatusTool(opts?: {
|
|||
}
|
||||
// If resolution points at another agent, enforce A2A policy before switching stores.
|
||||
ensureAgentAccess(resolveAgentIdFromSessionKey(visibleSession.key));
|
||||
resolvedTargetViaSessionId = true;
|
||||
resolvedViaSessionId = true;
|
||||
requestedKeyRaw = visibleSession.key;
|
||||
agentId = resolveAgentIdFromSessionKey(visibleSession.key);
|
||||
storePath = resolveStorePath(cfg.session?.store, { agentId });
|
||||
|
|
@ -395,13 +396,15 @@ export function createSessionStatusTool(opts?: {
|
|||
throw new Error(`Unknown ${kind}: ${requestedKeyRaw}`);
|
||||
}
|
||||
|
||||
if (resolvedTargetViaSessionId || (opts?.sandboxed === true && !isExplicitAgentKey)) {
|
||||
const access = visibilityGuard.check(
|
||||
normalizeVisibilityTargetSessionKey(resolved.key, agentId),
|
||||
);
|
||||
if (!access.allowed) {
|
||||
throw new Error(access.error);
|
||||
}
|
||||
// Preserve caller-scoped raw-key/current lookups as "self" for visibility checks.
|
||||
const visibilityTargetKey =
|
||||
!resolvedViaSessionId &&
|
||||
(requestedKeyInput === "current" || resolved.key === requestedKeyInput)
|
||||
? visibilityRequesterKey
|
||||
: normalizeVisibilityTargetSessionKey(resolved.key, agentId);
|
||||
const access = visibilityGuard.check(visibilityTargetKey);
|
||||
if (!access.allowed) {
|
||||
throw new Error(access.error);
|
||||
}
|
||||
|
||||
const configured = resolveDefaultModelForAgent({ cfg, agentId });
|
||||
|
|
|
|||
Loading…
Reference in New Issue