refactor(security): simplify system.run approval model

This commit is contained in:
Peter Steinberger
2026-03-11 01:42:47 +00:00
parent 5716e52417
commit 68c674d37c
32 changed files with 332 additions and 207 deletions

View File

@@ -7,7 +7,7 @@ import { callGatewayTool } from "./tools/gateway.js";
export type RequestExecApprovalDecisionParams = {
id: string;
command: string;
command?: string;
commandArgv?: string[];
systemRunPlan?: SystemRunApprovalPlan;
env?: Record<string, string>;
@@ -35,8 +35,8 @@ function buildExecApprovalRequestToolParams(
): ExecApprovalRequestToolParams {
return {
id: params.id,
command: params.command,
commandArgv: params.commandArgv,
...(params.command ? { command: params.command } : {}),
...(params.commandArgv ? { commandArgv: params.commandArgv } : {}),
systemRunPlan: params.systemRunPlan,
env: params.env,
cwd: params.cwd,
@@ -150,7 +150,7 @@ export async function requestExecApprovalDecision(
type HostExecApprovalParams = {
approvalId: string;
command: string;
command?: string;
commandArgv?: string[];
systemRunPlan?: SystemRunApprovalPlan;
env?: Record<string, string>;

View File

@@ -125,7 +125,7 @@ export async function executeNodeHostCommand(
throw new Error("invalid system.run.prepare response");
}
const runArgv = prepared.plan.argv;
const runRawCommand = prepared.plan.rawCommand ?? prepared.cmdText;
const runRawCommand = prepared.plan.commandText;
const runCwd = prepared.plan.cwd ?? params.workdir;
const runAgentId = prepared.plan.agentId ?? params.agentId;
const runSessionKey = prepared.plan.sessionKey ?? params.sessionKey;
@@ -238,8 +238,6 @@ export async function executeNodeHostCommand(
// Register first so the returned approval ID is actionable immediately.
const registration = await registerExecApprovalRequestForHostOrThrow({
approvalId,
command: prepared.cmdText,
commandArgv: prepared.plan.argv,
systemRunPlan: prepared.plan,
env: nodeEnv,
workdir: runCwd,
@@ -391,7 +389,7 @@ export async function executeNodeHostCommand(
warningText,
approvalSlug,
approvalId,
command: prepared.cmdText,
command: prepared.plan.commandText,
cwd: runCwd,
host: "node",
nodeId,

View File

@@ -135,11 +135,10 @@ function setupNodeInvokeMock(params: {
function createSystemRunPreparePayload(cwd: string | null) {
return {
payload: {
cmdText: "echo hi",
plan: {
argv: ["echo", "hi"],
cwd,
rawCommand: "echo hi",
commandText: "echo hi",
agentId: null,
sessionKey: null,
},
@@ -662,10 +661,9 @@ describe("nodes run", () => {
onApprovalRequest: (approvalParams) => {
expect(approvalParams).toMatchObject({
id: expect.any(String),
command: "echo hi",
commandArgv: ["echo", "hi"],
systemRunPlan: expect.objectContaining({
argv: ["echo", "hi"],
commandText: "echo hi",
}),
nodeId: NODE_ID,
host: "node",

View File

@@ -97,11 +97,11 @@ describe("createNodesTool screen_record duration guardrails", () => {
if (payload?.command === "system.run.prepare") {
return {
payload: {
cmdText: "echo hi",
plan: {
argv: ["bash", "-lc", "echo hi"],
cwd: null,
rawCommand: null,
commandText: 'bash -lc "echo hi"',
commandPreview: "echo hi",
agentId: null,
sessionKey: null,
},

View File

@@ -664,7 +664,7 @@ export function createNodesTool(options?: {
}
const runParams = {
command: prepared.plan.argv,
rawCommand: prepared.plan.rawCommand ?? prepared.cmdText,
rawCommand: prepared.plan.commandText,
cwd: prepared.plan.cwd ?? cwd,
env,
timeoutMs: commandTimeoutMs,
@@ -699,8 +699,6 @@ export function createNodesTool(options?: {
{ ...gatewayOpts, timeoutMs: APPROVAL_TIMEOUT_MS + 5_000 },
{
id: approvalId,
command: prepared.cmdText,
commandArgv: prepared.plan.argv,
systemRunPlan: prepared.plan,
cwd: prepared.plan.cwd ?? cwd,
nodeId,