Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const globalSettingsSchema = z.object({
alwaysAllowSubtasks: z.boolean().optional(),
alwaysAllowExecute: z.boolean().optional(),
allowedCommands: z.array(z.string()).optional(),
blacklistedCommands: z.array(z.string()).optional(),
allowedMaxRequests: z.number().nullish(),
autoCondenseContext: z.boolean().optional(),
autoCondenseContextPercent: z.number().optional(),
Expand Down Expand Up @@ -131,6 +132,7 @@ export const GLOBAL_SETTINGS_KEYS = keysOf<GlobalSettings>()([
"alwaysAllowSubtasks",
"alwaysAllowExecute",
"allowedCommands",
"blacklistedCommands",
"allowedMaxRequests",
"autoCondenseContext",
"autoCondenseContextPercent",
Expand Down
9 changes: 9 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,15 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
.getConfiguration(Package.name)
.update("allowedCommands", message.commands, vscode.ConfigurationTarget.Global)

break
case "blacklistedCommands":
await provider.context.globalState.update("blacklistedCommands", message.commands)

// Also update workspace settings.
await vscode.workspace
.getConfiguration(Package.name)
.update("blacklistedCommands", message.commands, vscode.ConfigurationTarget.Global)

break
case "openCustomModesSettings": {
const customModesFilePath = await provider.customModesManager.getCustomModesFilePath()
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export type ExtensionState = Pick<
| "alwaysAllowSubtasks"
| "alwaysAllowExecute"
| "allowedCommands"
| "blacklistedCommands"
| "allowedMaxRequests"
| "browserToolEnabled"
| "browserViewportSize"
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface WebviewMessage {
| "getListApiConfiguration"
| "customInstructions"
| "allowedCommands"
| "blacklistedCommands"
| "alwaysAllowReadOnly"
| "alwaysAllowReadOnlyOutsideWorkspace"
| "alwaysAllowWrite"
Expand Down
5 changes: 3 additions & 2 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
alwaysAllowExecute,
alwaysAllowMcp,
allowedCommands,
blacklistedCommands,
writeDelayMs,
mode,
setMode,
Expand Down Expand Up @@ -840,9 +841,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
const isAllowedCommand = useCallback(
(message: ClineMessage | undefined): boolean => {
if (message?.type !== "ask") return false
return validateCommand(message.text || "", allowedCommands || [])
return validateCommand(message.text || "", allowedCommands || [], blacklistedCommands || [])
},
[allowedCommands],
[allowedCommands, blacklistedCommands],
)

const isAutoApproved = useCallback(
Expand Down
70 changes: 70 additions & 0 deletions webview-ui/src/components/settings/AutoApproveSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
alwaysAllowSubtasks?: boolean
alwaysAllowExecute?: boolean
allowedCommands?: string[]
blacklistedCommands?: string[]
setCachedStateField: SetCachedStateField<
| "alwaysAllowReadOnly"
| "alwaysAllowReadOnlyOutsideWorkspace"
Expand All @@ -39,6 +40,7 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
| "alwaysAllowSubtasks"
| "alwaysAllowExecute"
| "allowedCommands"
| "blacklistedCommands"
>
}

Expand All @@ -56,11 +58,13 @@ export const AutoApproveSettings = ({
alwaysAllowSubtasks,
alwaysAllowExecute,
allowedCommands,
blacklistedCommands,
setCachedStateField,
...props
}: AutoApproveSettingsProps) => {
const { t } = useAppTranslation()
const [commandInput, setCommandInput] = useState("")
const [blacklistCommandInput, setBlacklistCommandInput] = useState("")

const handleAddCommand = () => {
const currentCommands = allowedCommands ?? []
Expand All @@ -73,6 +77,17 @@ export const AutoApproveSettings = ({
}
}

const handleAddBlacklistCommand = () => {
const currentBlacklistedCommands = blacklistedCommands ?? []

if (blacklistCommandInput && !currentBlacklistedCommands.includes(blacklistCommandInput)) {
const newBlacklistedCommands = [...currentBlacklistedCommands, blacklistCommandInput]
setCachedStateField("blacklistedCommands", newBlacklistedCommands)
setBlacklistCommandInput("")
vscode.postMessage({ type: "blacklistedCommands", commands: newBlacklistedCommands })
}
}

return (
<div {...props}>
<SectionHeader description={t("settings:autoApprove.description")}>
Expand Down Expand Up @@ -239,6 +254,61 @@ export const AutoApproveSettings = ({
</Button>
))}
</div>

<div className="mt-4">
<label className="block font-medium mb-1" data-testid="blacklisted-commands-heading">
{t("settings:autoApprove.execute.blacklistedCommands")}
</label>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:autoApprove.execute.blacklistedCommandsDescription")}
</div>
</div>

<div className="flex gap-2">
<Input
value={blacklistCommandInput}
onChange={(e: any) => setBlacklistCommandInput(e.target.value)}
onKeyDown={(e: any) => {
if (e.key === "Enter") {
e.preventDefault()
handleAddBlacklistCommand()
}
}}
placeholder={t("settings:autoApprove.execute.blacklistCommandPlaceholder")}
className="grow"
data-testid="blacklist-command-input"
/>
<Button
className="h-8"
onClick={handleAddBlacklistCommand}
data-testid="add-blacklist-command-button">
{t("settings:autoApprove.execute.addBlacklistButton")}
</Button>
</div>

<div className="flex flex-wrap gap-2">
{(blacklistedCommands ?? []).map((cmd, index) => (
<Button
key={index}
variant="destructive"
data-testid={`remove-blacklist-command-${index}`}
onClick={() => {
const newBlacklistedCommands = (blacklistedCommands ?? []).filter(
(_, i) => i !== index,
)
setCachedStateField("blacklistedCommands", newBlacklistedCommands)
vscode.postMessage({
type: "blacklistedCommands",
commands: newBlacklistedCommands,
})
}}>
<div className="flex flex-row items-center gap-1">
<div>{cmd}</div>
<X className="text-foreground scale-75" />
</div>
</Button>
))}
</div>
</div>
)}
</Section>
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
alwaysAllowReadOnly,
alwaysAllowReadOnlyOutsideWorkspace,
allowedCommands,
blacklistedCommands,
allowedMaxRequests,
language,
alwaysAllowBrowser,
Expand Down Expand Up @@ -256,6 +257,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
vscode.postMessage({ type: "alwaysAllowBrowser", bool: alwaysAllowBrowser })
vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp })
vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] })
vscode.postMessage({ type: "blacklistedCommands", commands: blacklistedCommands ?? [] })
vscode.postMessage({ type: "allowedMaxRequests", value: allowedMaxRequests ?? undefined })
vscode.postMessage({ type: "autoCondenseContext", bool: autoCondenseContext })
vscode.postMessage({ type: "autoCondenseContextPercent", value: autoCondenseContextPercent })
Expand Down Expand Up @@ -578,6 +580,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
alwaysAllowSubtasks={alwaysAllowSubtasks}
alwaysAllowExecute={alwaysAllowExecute}
allowedCommands={allowedCommands}
blacklistedCommands={blacklistedCommands}
setCachedStateField={setCachedStateField}
/>
)}
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface ExtensionStateContextType extends ExtensionState {
setShowRooIgnoredFiles: (value: boolean) => void
setShowAnnouncement: (value: boolean) => void
setAllowedCommands: (value: string[]) => void
setBlacklistedCommands: (value: string[]) => void
setAllowedMaxRequests: (value: number | undefined) => void
setSoundEnabled: (value: boolean) => void
setSoundVolume: (value: number) => void
Expand Down Expand Up @@ -154,6 +155,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
taskHistory: [],
shouldShowAnnouncement: false,
allowedCommands: [],
blacklistedCommands: [],
soundEnabled: false,
soundVolume: 0.5,
ttsEnabled: false,
Expand Down Expand Up @@ -332,6 +334,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAlwaysAllowSubtasks: (value) => setState((prevState) => ({ ...prevState, alwaysAllowSubtasks: value })),
setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
setBlacklistedCommands: (value) => setState((prevState) => ({ ...prevState, blacklistedCommands: value })),
setAllowedMaxRequests: (value) => setState((prevState) => ({ ...prevState, allowedMaxRequests: value })),
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })),
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/ca/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Comandes d'auto-execució permeses",
"allowedCommandsDescription": "Prefixos de comandes que poden ser executats automàticament quan \"Aprovar sempre operacions d'execució\" està habilitat. Afegeix * per permetre totes les comandes (usar amb precaució).",
"commandPlaceholder": "Introduïu prefix de comanda (ex. 'git ')",
"addButton": "Afegir"
"addButton": "Afegir",
"blacklistedCommands": "Comandes Bloquejades",
"blacklistedCommandsDescription": "Prefixos de comandes que seran bloquejats de l'execució fins i tot quan \"Aprovar sempre operacions d'execució\" estigui habilitat. Aquestes comandes sempre requeriran aprovació manual.",
"blacklistCommandPlaceholder": "Introduïu prefix de comanda a bloquejar (ex. 'rm ')",
"addBlacklistButton": "Afegir a la Llista Negra"
},
"apiRequestLimit": {
"title": "Màximes Sol·licituds",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/de/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Erlaubte Auto-Ausführungsbefehle",
"allowedCommandsDescription": "Befehlspräfixe, die automatisch ausgeführt werden können, wenn 'Ausführungsoperationen immer genehmigen' aktiviert ist. Fügen Sie * hinzu, um alle Befehle zu erlauben (mit Vorsicht verwenden).",
"commandPlaceholder": "Befehlspräfix eingeben (z.B. 'git ')",
"addButton": "Hinzufügen"
"addButton": "Hinzufügen",
"blacklistedCommands": "Gesperrte Befehle",
"blacklistedCommandsDescription": "Befehlspräfixe, die auch bei aktivierter Option \"Ausführungsoperationen immer genehmigen\" blockiert werden. Diese Befehle erfordern immer eine manuelle Genehmigung.",
"blacklistCommandPlaceholder": "Befehlspräfix zum Blockieren eingeben (z.B. 'rm ')",
"addBlacklistButton": "Zur Sperrliste hinzufügen"
},
"apiRequestLimit": {
"title": "Maximale Anfragen",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Allowed Auto-Execute Commands",
"allowedCommandsDescription": "Command prefixes that can be auto-executed when \"Always approve execute operations\" is enabled. Add * to allow all commands (use with caution).",
"commandPlaceholder": "Enter command prefix (e.g., 'git ')",
"addButton": "Add"
"addButton": "Add",
"blacklistedCommands": "Blacklisted Commands",
"blacklistedCommandsDescription": "Command prefixes that will be blocked from execution even when \"Always approve execute operations\" is enabled. These commands will always require manual approval.",
"blacklistCommandPlaceholder": "Enter command prefix to block (e.g., 'rm ')",
"addBlacklistButton": "Add to Blacklist"
},
"apiRequestLimit": {
"title": "Max Requests",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/es/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Comandos de auto-ejecución permitidos",
"allowedCommandsDescription": "Prefijos de comandos que pueden ser ejecutados automáticamente cuando \"Aprobar siempre operaciones de ejecución\" está habilitado. Añade * para permitir todos los comandos (usar con precaución).",
"commandPlaceholder": "Ingrese prefijo de comando (ej. 'git ')",
"addButton": "Añadir"
"addButton": "Añadir",
"blacklistedCommands": "Comandos Bloqueados",
"blacklistedCommandsDescription": "Prefijos de comandos que serán bloqueados de la ejecución incluso cuando \"Aprobar siempre operaciones de ejecución\" esté habilitado. Estos comandos siempre requerirán aprobación manual.",
"blacklistCommandPlaceholder": "Ingrese prefijo de comando a bloquear (ej. 'rm ')",
"addBlacklistButton": "Agregar a Lista de Bloqueo"
},
"apiRequestLimit": {
"title": "Solicitudes máximas",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/fr/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Commandes auto-exécutables autorisées",
"allowedCommandsDescription": "Préfixes de commandes qui peuvent être auto-exécutés lorsque \"Toujours approuver les opérations d'exécution\" est activé. Ajoutez * pour autoriser toutes les commandes (à utiliser avec précaution).",
"commandPlaceholder": "Entrez le préfixe de commande (ex. 'git ')",
"addButton": "Ajouter"
"addButton": "Ajouter",
"blacklistedCommands": "Commandes Bloquées",
"blacklistedCommandsDescription": "Préfixes de commandes qui seront bloqués de l'exécution même lorsque \"Toujours approuver les opérations d'exécution\" est activé. Ces commandes nécessiteront toujours une approbation manuelle.",
"blacklistCommandPlaceholder": "Entrez le préfixe de commande à bloquer (ex. 'rm ')",
"addBlacklistButton": "Ajouter à la Liste de Blocage"
},
"apiRequestLimit": {
"title": "Requêtes maximales",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/hi/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "अनुमत स्वतः-निष्पादन कमांड",
"allowedCommandsDescription": "कमांड प्रीफिक्स जो स्वचालित रूप से निष्पादित किए जा सकते हैं जब \"निष्पादन ऑपरेशन हमेशा अनुमोदित करें\" सक्षम है। सभी कमांड की अनुमति देने के लिए * जोड़ें (सावधानी से उपयोग करें)।",
"commandPlaceholder": "कमांड प्रीफिक्स दर्ज करें (उदा. 'git ')",
"addButton": "जोड़ें"
"addButton": "जोड़ें",
"blacklistedCommands": "ब्लॉक की गई कमांड्स",
"blacklistedCommandsDescription": "कमांड प्रीफिक्स जो \"हमेशा execute ऑपरेशन्स को approve करें\" सक्षम होने पर भी execution से ब्लॉक हो जाएंगे। इन कमांड्स के लिए हमेशा मैन्युअल approval की आवश्यकता होगी।",
"blacklistCommandPlaceholder": "ब्लॉक करने के लिए कमांड प्रीफिक्स दर्ज करें (उदा. 'rm ')",
"addBlacklistButton": "ब्लैकलिस्ट में जोड़ें"
},
"apiRequestLimit": {
"title": "अधिकतम अनुरोध",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/it/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Comandi di auto-esecuzione consentiti",
"allowedCommandsDescription": "Prefissi di comando che possono essere auto-eseguiti quando \"Approva sempre operazioni di esecuzione\" è abilitato. Aggiungi * per consentire tutti i comandi (usare con cautela).",
"commandPlaceholder": "Inserisci prefisso comando (es. 'git ')",
"addButton": "Aggiungi"
"addButton": "Aggiungi",
"blacklistedCommands": "Comandi Bloccati",
"blacklistedCommandsDescription": "Prefissi di comandi che saranno bloccati dall'esecuzione anche quando \"Approva sempre operazioni di esecuzione\" è abilitato. Questi comandi richiederanno sempre approvazione manuale.",
"blacklistCommandPlaceholder": "Inserisci prefisso comando da bloccare (es. 'rm ')",
"addBlacklistButton": "Aggiungi alla Lista Nera"
},
"apiRequestLimit": {
"title": "Richieste massime",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/ja/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "許可された自動実行コマンド",
"allowedCommandsDescription": "「実行操作を常に承認」が有効な場合に自動実行できるコマンドプレフィックス。すべてのコマンドを許可するには * を追加します(注意して使用してください)。",
"commandPlaceholder": "コマンドプレフィックスを入力(例:'git ')",
"addButton": "追加"
"addButton": "追加",
"blacklistedCommands": "ブロックされたコマンド",
"blacklistedCommandsDescription": "「実行操作を常に承認する」が有効になっている場合でも、実行がブロックされるコマンドプレフィックス。これらのコマンドは常に手動承認が必要です。",
"blacklistCommandPlaceholder": "ブロックするコマンドプレフィックスを入力(例:'rm ')",
"addBlacklistButton": "ブラックリストに追加"
},
"apiRequestLimit": {
"title": "最大リクエスト数",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/ko/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "허용된 자동 실행 명령",
"allowedCommandsDescription": "\"실행 작업 항상 승인\"이 활성화되었을 때 자동 실행될 수 있는 명령 접두사. 모든 명령을 허용하려면 * 추가(주의해서 사용)",
"commandPlaceholder": "명령 접두사 입력(예: 'git ')",
"addButton": "추가"
"addButton": "추가",
"blacklistedCommands": "차단된 명령어",
"blacklistedCommandsDescription": "\"실행 작업 항상 승인\"이 활성화되어 있어도 실행이 차단될 명령 접두사입니다. 이러한 명령어는 항상 수동 승인이 필요합니다.",
"blacklistCommandPlaceholder": "차단할 명령 접두사 입력(예: 'rm ')",
"addBlacklistButton": "블랙리스트에 추가"
},
"apiRequestLimit": {
"title": "최대 요청 수",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/nl/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Toegestane automatisch uit te voeren commando's",
"allowedCommandsDescription": "Commando-prefixen die automatisch kunnen worden uitgevoerd als 'Altijd goedkeuren voor uitvoeren' is ingeschakeld. Voeg * toe om alle commando's toe te staan (gebruik met voorzichtigheid).",
"commandPlaceholder": "Voer commando-prefix in (bijv. 'git ')",
"addButton": "Toevoegen"
"addButton": "Toevoegen",
"blacklistedCommands": "Geblokkeerde Commando's",
"blacklistedCommandsDescription": "Commando-prefixen die geblokkeerd worden van uitvoering, zelfs wanneer \"Altijd uitvoeringsoperaties goedkeuren\" is ingeschakeld. Deze commando's vereisen altijd handmatige goedkeuring.",
"blacklistCommandPlaceholder": "Voer commando-prefix in om te blokkeren (bijv. 'rm ')",
"addBlacklistButton": "Toevoegen aan Zwarte Lijst"
},
"apiRequestLimit": {
"title": "Maximale verzoeken",
Expand Down
6 changes: 5 additions & 1 deletion webview-ui/src/i18n/locales/pl/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@
"allowedCommands": "Dozwolone polecenia auto-wykonania",
"allowedCommandsDescription": "Prefiksy poleceń, które mogą być automatycznie wykonywane, gdy \"Zawsze zatwierdzaj operacje wykonania\" jest włączone. Dodaj * aby zezwolić na wszystkie polecenia (używaj z ostrożnością).",
"commandPlaceholder": "Wprowadź prefiks polecenia (np. 'git ')",
"addButton": "Dodaj"
"addButton": "Dodaj",
"blacklistedCommands": "Zablokowane Polecenia",
"blacklistedCommandsDescription": "Prefiksy poleceń, które będą zablokowane przed wykonaniem, nawet gdy \"Zawsze zatwierdzaj operacje wykonywania\" jest włączone. Te polecenia zawsze będą wymagać ręcznego zatwierdzenia.",
"blacklistCommandPlaceholder": "Wprowadź prefiks polecenia do zablokowania (np. 'rm ')",
"addBlacklistButton": "Dodaj do Czarnej Listy"
},
"apiRequestLimit": {
"title": "Maksymalna liczba żądań",
Expand Down
Loading