Limit hook CLI tool authority [AI] (#81065)

* fix: limit hook cli tool authority

* docs: add changelog entry for PR merge
This commit is contained in:
Pavan Kumar Gondhi
2026-05-13 07:24:41 +05:30
committed by GitHub
parent ac3aaad70c
commit a3fda2ada9
5 changed files with 40 additions and 1 deletions

View File

@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Limit hook CLI tool authority [AI]. (#81065) Thanks @pgondhi987.
- Require admin scope for node device token management [AI]. (#81067) Thanks @pgondhi987.
- Restrict chat sender allowlist matching [AI]. (#80898) Thanks @pgondhi987.
- Sessions: redact persisted tool result detail metadata before writing transcripts so diagnostic secrets do not survive tool output redaction. (#80444) Thanks @nimbleenigma.

View File

@@ -82,6 +82,7 @@ export function createCronPromptExecutor(params: {
resolvedVerboseLevel: VerboseLevel;
thinkLevel: ThinkLevel | undefined;
timeoutMs: number;
senderIsOwner: boolean;
messageChannel: string | undefined;
suppressExecNotifyOnExit: boolean;
resolvedDelivery: {
@@ -177,7 +178,7 @@ export function createCronPromptExecutor(params: {
onExecutionPhase: params.onExecutionPhase,
bootstrapPromptWarningSignaturesSeen,
bootstrapPromptWarningSignature,
senderIsOwner: true,
senderIsOwner: params.senderIsOwner,
});
bootstrapPromptWarningSignaturesSeen = resolveBootstrapWarningSignaturesSeen(
result.meta?.systemPromptReport,
@@ -314,6 +315,7 @@ export async function executeCronRun(params: {
) => void;
thinkLevel: ThinkLevel | undefined;
timeoutMs: number;
senderIsOwner: boolean;
suppressExecNotifyOnExit: boolean;
runStartedAt?: number;
}): Promise<CronExecutionResult> {
@@ -350,6 +352,7 @@ export async function executeCronRun(params: {
abortReason: params.abortReason,
onExecutionStarted: params.onExecutionStarted,
onExecutionPhase: params.onExecutionPhase,
senderIsOwner: params.senderIsOwner,
});
const runStartedAt = params.runStartedAt ?? Date.now();

View File

@@ -319,6 +319,7 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
resolvedVerboseLevel: "off",
thinkLevel: undefined,
timeoutMs: 60_000,
senderIsOwner: true,
messageChannel: "messagechat",
suppressExecNotifyOnExit: true,
toolPolicy: {

View File

@@ -121,9 +121,40 @@ describe("runCronIsolatedAgentTurn isolated session identity", () => {
const runRequest = requireFirstMockArg(runCliAgentMock, "runCliAgentMock") as {
sessionId?: string;
sessionKey?: string;
senderIsOwner?: boolean;
};
expect(runRequest.sessionId).toBe("isolated-cli-run-1");
expect(runRequest.sessionKey).toBe("agent:default:cron:cli-monitor:run:isolated-cli-run-1");
expect(runRequest.sessionKey).not.toBe("agent:default:cron:cli-monitor");
expect(runRequest.senderIsOwner).toBe(true);
});
it("runs externally sourced CLI hook turns without owner tool authority", async () => {
isCliProviderMock.mockReturnValue(true);
mockRunCronFallbackPassthrough();
runCliAgentMock.mockResolvedValue({
payloads: [{ text: "done" }],
meta: { agentMeta: { usage: { input: 10, output: 20 } } },
});
const result = await runCronIsolatedAgentTurn(
makeIsolatedAgentTurnParams({
sessionKey: "hook:webhook:cli-monitor",
job: makeIsolatedAgentTurnJob({
payload: {
kind: "agentTurn",
message: "test",
externalContentSource: "webhook",
},
}),
}),
);
expect(result.status).toBe("ok");
expect(runCliAgentMock).toHaveBeenCalledOnce();
const runRequest = requireFirstMockArg(runCliAgentMock, "runCliAgentMock") as {
senderIsOwner?: boolean;
};
expect(runRequest.senderIsOwner).toBe(false);
});
});

View File

@@ -462,6 +462,7 @@ type PreparedCronRunContext = {
resolvedDelivery: ResolvedCronDeliveryTarget;
deliveryRequested: boolean;
suppressExecNotifyOnExit: boolean;
senderIsOwner: boolean;
toolPolicy: ReturnType<typeof resolveCronToolPolicy>;
skillsSnapshot: SkillSnapshot;
liveSelection: CronLiveSelection;
@@ -792,6 +793,7 @@ async function prepareCronRunContext(params: {
resolvedDelivery,
deliveryRequested,
suppressExecNotifyOnExit: deliveryPlan.mode === "none",
senderIsOwner: !isExternalHook,
toolPolicy,
skillsSnapshot,
liveSelection,
@@ -1145,6 +1147,7 @@ export async function runCronIsolatedAgentTurn(params: {
thinkLevel: prepared.context.thinkLevel,
timeoutMs: prepared.context.timeoutMs,
suppressExecNotifyOnExit: prepared.context.suppressExecNotifyOnExit,
senderIsOwner: prepared.context.senderIsOwner,
});
if (isAborted()) {
return prepared.context.withRunSession({