mirror of
https://github.com/simstudioai/sim.git
synced 2026-03-15 03:00:33 -04:00
fix(execute-command): static import, user-configurable timeout, overlap guard, shell-safe regex
- Promote DEFAULT_EXECUTION_TIMEOUT_MS to static top-level import - Add timeout subBlock so users can configure command timeout - Add overlapping replacement assertion to prevent corruption - Tighten tag regex to require non-whitespace start, avoiding shell redirection false matches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { isExecuteCommandEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { DEFAULT_EXECUTION_TIMEOUT_MS } from '@/lib/execution/constants'
|
||||
import { normalizeName, REFERENCE, SPECIAL_REFERENCE_PREFIXES } from '@/executor/constants'
|
||||
import { type OutputSchema, resolveBlockReference } from '@/executor/utils/block-reference'
|
||||
import {
|
||||
@@ -151,7 +152,7 @@ function collectTagReplacements(
|
||||
blockOutputSchemas: Record<string, OutputSchema>
|
||||
): Replacement[] {
|
||||
const tagPattern = new RegExp(
|
||||
`${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`,
|
||||
`${REFERENCE.START}(\\S[^${REFERENCE.START}${REFERENCE.END}]*)${REFERENCE.END}`,
|
||||
'g'
|
||||
)
|
||||
|
||||
@@ -217,6 +218,16 @@ function resolveCommandVariables(
|
||||
|
||||
allReplacements.sort((a, b) => a.index - b.index)
|
||||
|
||||
for (let i = 1; i < allReplacements.length; i++) {
|
||||
const prev = allReplacements[i - 1]
|
||||
const curr = allReplacements[i]
|
||||
if (curr.index < prev.index + prev.length) {
|
||||
throw new Error(
|
||||
`Overlapping variable references detected at positions ${prev.index} and ${curr.index}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let resolved = command
|
||||
for (let i = allReplacements.length - 1; i >= 0; i--) {
|
||||
const { index, length, value } = allReplacements[i]
|
||||
@@ -310,7 +321,6 @@ export async function POST(req: NextRequest) {
|
||||
}
|
||||
|
||||
const body = await req.json()
|
||||
const { DEFAULT_EXECUTION_TIMEOUT_MS } = await import('@/lib/execution/constants')
|
||||
|
||||
const {
|
||||
command,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TerminalIcon } from '@/components/icons'
|
||||
import { isTruthy } from '@/lib/core/config/env'
|
||||
import { DEFAULT_EXECUTION_TIMEOUT_MS } from '@/lib/execution/constants'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { ExecuteCommandOutput } from '@/tools/execute-command/types'
|
||||
|
||||
@@ -59,6 +60,13 @@ IMPORTANT FORMATTING RULES:
|
||||
required: false,
|
||||
placeholder: '/path/to/directory',
|
||||
},
|
||||
{
|
||||
id: 'timeout',
|
||||
title: 'Timeout (ms)',
|
||||
type: 'short-input',
|
||||
required: false,
|
||||
placeholder: String(DEFAULT_EXECUTION_TIMEOUT_MS),
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['execute_command_run'],
|
||||
@@ -66,6 +74,7 @@ IMPORTANT FORMATTING RULES:
|
||||
inputs: {
|
||||
command: { type: 'string', description: 'Shell command to execute' },
|
||||
workingDirectory: { type: 'string', description: 'Working directory for the command' },
|
||||
timeout: { type: 'number', description: 'Execution timeout in milliseconds' },
|
||||
},
|
||||
outputs: {
|
||||
stdout: { type: 'string', description: 'Standard output from the command' },
|
||||
|
||||
@@ -19,7 +19,7 @@ export const executeCommandRunTool: ToolConfig<ExecuteCommandInput, ExecuteComma
|
||||
timeout: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Execution timeout in milliseconds',
|
||||
default: DEFAULT_EXECUTION_TIMEOUT_MS,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user