diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 2bed089961..2581c49052 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1303,6 +1303,38 @@ export class ClineProvider return await fileExistsAtPath(promptFilePath) } + /** + * Merges allowed commands from global state and workspace configuration + * with proper validation and deduplication + */ + private mergeAllowedCommands(globalStateCommands?: string[]): string[] { + try { + // Validate and sanitize global state commands + const validGlobalCommands = Array.isArray(globalStateCommands) + ? globalStateCommands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0) + : [] + + // Get workspace configuration commands + const workspaceCommands = + vscode.workspace.getConfiguration(Package.name).get("allowedCommands") || [] + + // Validate and sanitize workspace commands + const validWorkspaceCommands = Array.isArray(workspaceCommands) + ? workspaceCommands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0) + : [] + + // Combine and deduplicate commands + // Global state takes precedence over workspace configuration + const mergedCommands = [...new Set([...validGlobalCommands, ...validWorkspaceCommands])] + + return mergedCommands + } catch (error) { + console.error("Error merging allowed commands:", error) + // Return empty array as fallback to prevent crashes + return [] + } + } + async getStateToPostToWebview() { const { apiConfiguration, @@ -1314,6 +1346,7 @@ export class ClineProvider alwaysAllowWriteOutsideWorkspace, alwaysAllowWriteProtected, alwaysAllowExecute, + allowedCommands, alwaysAllowBrowser, alwaysAllowMcp, alwaysAllowModeSwitch, @@ -1381,7 +1414,7 @@ export class ClineProvider const telemetryKey = process.env.POSTHOG_API_KEY const machineId = vscode.env.machineId - const allowedCommands = vscode.workspace.getConfiguration(Package.name).get("allowedCommands") || [] + const mergedAllowedCommands = this.mergeAllowedCommands(allowedCommands) const cwd = this.cwd // Check if there's a system prompt override for the current mode @@ -1420,7 +1453,7 @@ export class ClineProvider enableCheckpoints: enableCheckpoints ?? true, shouldShowAnnouncement: telemetrySetting !== "unset" && lastShownAnnouncementId !== this.latestAnnouncementId, - allowedCommands, + allowedCommands: mergedAllowedCommands, soundVolume: soundVolume ?? 0.5, browserViewportSize: browserViewportSize ?? "900x600", screenshotQuality: screenshotQuality ?? 75, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 663ccec5cc..caf9044e1b 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -566,15 +566,22 @@ export const webviewMessageHandler = async ( case "cancelTask": await provider.cancelTask() break - case "allowedCommands": - await provider.context.globalState.update("allowedCommands", message.commands) + case "allowedCommands": { + // Validate and sanitize the commands array + const commands = message.commands ?? [] + const validCommands = Array.isArray(commands) + ? commands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0) + : [] + + await updateGlobalState("allowedCommands", validCommands) // Also update workspace settings. await vscode.workspace .getConfiguration(Package.name) - .update("allowedCommands", message.commands, vscode.ConfigurationTarget.Global) + .update("allowedCommands", validCommands, vscode.ConfigurationTarget.Global) break + } case "openCustomModesSettings": { const customModesFilePath = await provider.customModesManager.getCustomModesFilePath()