agent-ecosystem/src/main/utils/toolApprovalRules.ts
iliya 73661c80a4 feat: auto-allow all tools toggle + fix team color fallback
- Add autoAllowAll setting: overrides all individual auto-allow rules,
  instantly approves every tool call without user interaction
- Settings panel: "Auto-allow all tools" checkbox at top, individual
  checkboxes greyed out when all-allow is active
- Fix team color badge: use getTeamColorSet(teamName) as fallback
  instead of getMemberColorByName which uses a different palette
  and produced wrong colors (yellow instead of pink)
2026-03-21 12:25:06 +02:00

151 lines
3.8 KiB
TypeScript

import type { ToolApprovalSettings } from '@shared/types/team';
// ---------------------------------------------------------------------------
// Safe bash command prefixes — commands that never need manual approval
// ---------------------------------------------------------------------------
const SAFE_PREFIXES: readonly string[] = [
// Version control
'git ',
'git\t',
// Package managers
'pnpm ',
'npm ',
'npx ',
'yarn ',
// File inspection (read-only)
'ls',
'cat ',
'head ',
'tail ',
'wc ',
'less ',
'more ',
// Output
'echo ',
'printf ',
// System info
'pwd',
'whoami',
'hostname',
'date',
'uname',
// Search & find (read-only)
'find ',
'grep ',
'rg ',
'fd ',
'ag ',
// Directory & file info
'tree ',
'which ',
'type ',
'file ',
// Text processing (read-only)
'diff ',
'sort ',
'uniq ',
'tr ',
'cut ',
// Path utilities
'basename ',
'dirname ',
'realpath ',
'readlink ',
// Environment
'env',
'printenv',
// Scripting one-liners (read-only)
'node -e',
'node --eval',
'python -c',
'python3 -c',
];
// ---------------------------------------------------------------------------
// Dangerous patterns — these OVERRIDE safe prefixes and always need approval
// ---------------------------------------------------------------------------
const DANGEROUS_PATTERNS: readonly RegExp[] = [
/\brm\s/, // rm (with space to avoid false positives like "rmdir" intent)
/\brm$/, // bare "rm" at end
/\bsudo\b/,
/\bchmod\b/,
/\bchown\b/,
/\bcurl\b.*\|\s*(ba)?sh/,
/\bwget\b.*\|\s*(ba)?sh/,
/\bmkfs\b/,
/\bdd\b/,
/\bkill\b/,
/\bkillall\b/,
/\bpkill\b/,
/>\s*\//, // redirect to absolute path root
/\beval\b/,
/\bexec\b/,
/\bformat\b/,
/\bshutdown\b/,
/\breboot\b/,
];
// ---------------------------------------------------------------------------
// File edit tools that can be auto-allowed
// ---------------------------------------------------------------------------
const FILE_EDIT_TOOLS = new Set(['Edit', 'Write', 'NotebookEdit']);
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
export interface AutoAllowResult {
autoAllow: boolean;
reason?: string;
}
/**
* Determines whether a tool call should be auto-allowed based on user settings.
*
* Logic:
* 1. File edit tools — auto-allow if `autoAllowFileEdits` is enabled
* 2. Bash commands — check dangerous patterns FIRST (always block),
* then check safe prefixes (auto-allow if `autoAllowSafeBash` is enabled)
* 3. Everything else — requires manual approval
*/
export function shouldAutoAllow(
settings: ToolApprovalSettings,
toolName: string,
toolInput: Record<string, unknown>
): AutoAllowResult {
// Auto-allow ALL tools (overrides everything)
if (settings.autoAllowAll) {
return { autoAllow: true, reason: 'auto_allow_all' };
}
// File edit auto-allow
if (settings.autoAllowFileEdits && FILE_EDIT_TOOLS.has(toolName)) {
return { autoAllow: true, reason: 'auto_allow_category' };
}
// Safe bash auto-allow
if (settings.autoAllowSafeBash && toolName === 'Bash') {
const command = typeof toolInput.command === 'string' ? toolInput.command.trim() : '';
if (!command) return { autoAllow: false };
// Dangerous patterns override safe prefixes — check FIRST
for (const pattern of DANGEROUS_PATTERNS) {
if (pattern.test(command)) {
return { autoAllow: false };
}
}
// Check safe prefixes
for (const prefix of SAFE_PREFIXES) {
const trimmedPrefix = prefix.trimEnd();
if (command === trimmedPrefix || command.startsWith(prefix)) {
return { autoAllow: true, reason: 'auto_allow_category' };
}
}
}
return { autoAllow: false };
}