feat: block dangerous bash commands (rm -rf, sudo, etc.)

Implements GitHub issue #103 - automatic blocking of dangerous commands:

Blocked categories:
- Destructive delete: rm -rf /, rm -rf ~, rm -rf .git
- Privilege escalation: sudo, su -, doas
- System damage: dd disk wipe, chmod 777 /, mkfs, fork bomb
- Network attacks: curl | bash, wget | bash, reverse shells
- Git destructive: force push to main, reset --hard, clean -fd
- Credential exposure: cat .env, echo $SECRET

Features:
- Cannot be bypassed even with auto-approve or -y flag
- Clear error messages explaining why command was blocked
- Severity levels: critical, high, medium
- Tested with 12 test cases (all pass)

Closes #103
This commit is contained in:
2026-02-05 19:09:37 -05:00
parent 8ae1218627
commit a30b3bb60f
3 changed files with 380 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
/**
* Dangerous Command Blocker Service
*
* Detects and blocks dangerous bash commands before execution.
* This is a safety feature that cannot be bypassed, even with auto-approve.
*/
import {
BLOCKED_PATTERNS,
BLOCKED_COMMAND_MESSAGES,
type BlockedPattern,
type DangerCategory,
} from "@constants/dangerous-commands";
/**
* Result of checking a command for danger
*/
export interface DangerCheckResult {
blocked: boolean;
pattern?: BlockedPattern;
message?: string;
}
/**
* Check if a command matches any blocked pattern
*/
export const checkDangerousCommand = (command: string): DangerCheckResult => {
// Normalize command for checking
const normalizedCommand = command.trim();
for (const pattern of BLOCKED_PATTERNS) {
if (pattern.pattern.test(normalizedCommand)) {
return {
blocked: true,
pattern,
message: formatBlockedMessage(pattern),
};
}
}
return { blocked: false };
};
/**
* Format a user-friendly blocked message
*/
const formatBlockedMessage = (pattern: BlockedPattern): string => {
const categoryDescription =
BLOCKED_COMMAND_MESSAGES.CATEGORY_DESCRIPTIONS[pattern.category];
const lines = [
`[${BLOCKED_COMMAND_MESSAGES.BLOCKED_PREFIX}] ${pattern.name}: ${pattern.description}`,
"",
`Category: ${formatCategoryName(pattern.category)}`,
`Severity: ${pattern.severity.toUpperCase()}`,
"",
categoryDescription,
"",
BLOCKED_COMMAND_MESSAGES.CANNOT_BYPASS,
BLOCKED_COMMAND_MESSAGES.SUGGESTION,
];
return lines.join("\n");
};
/**
* Format category name for display
*/
const formatCategoryName = (category: DangerCategory): string => {
const names: Record<DangerCategory, string> = {
destructive_delete: "Destructive Delete",
privilege_escalation: "Privilege Escalation",
system_damage: "System Damage",
network_attack: "Network Attack",
git_destructive: "Git Destructive",
credential_exposure: "Credential Exposure",
};
return names[category];
};
/**
* Get all blocked patterns (for configuration UI)
*/
export const getBlockedPatterns = (): readonly BlockedPattern[] => {
return BLOCKED_PATTERNS;
};
/**
* Check if a command is safe (inverse of dangerous check)
*/
export const isCommandSafe = (command: string): boolean => {
return !checkDangerousCommand(command).blocked;
};
/**
* Get categories of dangerous commands
*/
export const getDangerCategories = (): DangerCategory[] => {
return [
"destructive_delete",
"privilege_escalation",
"system_damage",
"network_attack",
"git_destructive",
"credential_exposure",
];
};