langterm
Version:
Secure CLI tool that translates natural language to shell commands using local AI models via Ollama, with project memory system, reusable command templates (hooks), MCP (Model Context Protocol) support, and dangerous command detection
128 lines (111 loc) • 5.53 kB
JavaScript
import readline from 'readline';
import chalk from 'chalk';
// Dangerous command patterns for security detection
export const DANGEROUS_PATTERNS = [
// File system destruction
{ pattern: /rm\s+-rf\s+\/(?:\s|$)/, description: 'Recursive deletion of root directory' },
{ pattern: /rm\s+-rf\s+\/\*/, description: 'Deletion of all root directory contents' },
{ pattern: /rm\s+-rf\s+~/, description: 'Recursive deletion of home directory' },
{ pattern: /rm\s+-rf\s+\$HOME/, description: 'Recursive deletion of home directory' },
{ pattern: />\s*\/dev\/[sh]d[a-z]/, description: 'Overwriting disk device' },
{ pattern: /dd\s+.*of=\/dev\/[sh]d[a-z]/, description: 'Dangerous dd operation on disk device' },
// Fork bombs
{ pattern: /:\(\)\s*{\s*:\|:&\s*}/, description: 'Fork bomb' },
{ pattern: /\.\(\)\s*{\s*\.\|\.&\s*}/, description: 'Fork bomb variant' },
// Windows specific dangerous commands
{ pattern: /format\s+[cC]:/i, description: 'Formatting C: drive' },
{ pattern: /del\s+\/[fF]\s+\/[sS]\s+\/[qQ]\s+[cC]:\\/, description: 'Deleting all files on C: drive' },
{ pattern: /rd\s+\/[sS]\s+\/[qQ]\s+[cC]:\\/, description: 'Removing C: drive directory' },
// Permission changes
{ pattern: /chmod\s+-R\s+777\s+\//, description: 'Making entire system world-writable' },
{ pattern: /chmod\s+000\s+\//, description: 'Removing all permissions from root' },
{ pattern: /chown\s+-R.*\/(?:\s|$)/, description: 'Changing ownership of entire system' },
// System file corruption
{ pattern: />\s*\/dev\/null\s+2>&1/, description: 'Redirecting to /dev/null (when combined with dangerous operations)' },
{ pattern: /mv\s+\/\s+\/dev\/null/, description: 'Moving root to /dev/null' },
{ pattern: />\s*\/etc\/passwd/, description: 'Overwriting password file' },
{ pattern: />\s*\/etc\/shadow/, description: 'Overwriting shadow password file' },
// Network and system commands
{ pattern: /iptables\s+-F/, description: 'Flushing all firewall rules' },
{ pattern: /shutdown\s+-h\s+now/, description: 'Immediate system shutdown' },
{ pattern: /init\s+0/, description: 'System halt' },
{ pattern: /reboot\s+-f/, description: 'Forced system reboot' },
// Package management destruction
{ pattern: /apt-get\s+remove\s+--purge\s+\*/, description: 'Removing all packages' },
{ pattern: /yum\s+remove\s+\*/, description: 'Removing all packages' },
{ pattern: /pacman\s+-Rscn\s+\*/, description: 'Removing all packages' },
];
// Patterns that require confirmation but aren't necessarily destructive
export const WARNING_PATTERNS = [
{ pattern: /sudo\s+/, description: 'Command requires elevated privileges' },
{ pattern: /rm\s+-rf/, description: 'Recursive force deletion' },
{ pattern: /rm\s+-f/, description: 'Force deletion' },
{ pattern: />\s+\w/, description: 'File overwrite operation' },
{ pattern: /curl.*\|\s*sh/, description: 'Executing remote script' },
{ pattern: /wget.*\|\s*sh/, description: 'Executing remote script' },
];
/**
* Analyzes a command for dangerous or warning patterns
* @param {string} command - The command to analyze
* @returns {object} Analysis result with isDangerous, isWarning, and description
*/
export function checkDangerousCommand(command) {
// Check for dangerous patterns
for (const { pattern, description } of DANGEROUS_PATTERNS) {
if (pattern.test(command)) {
return { isDangerous: true, isWarning: false, description };
}
}
// Check for warning patterns
for (const { pattern, description } of WARNING_PATTERNS) {
if (pattern.test(command)) {
return { isDangerous: false, isWarning: true, description };
}
}
return { isDangerous: false, isWarning: false, description: null };
}
/**
* Prompts user for input
* @param {string} question - The question to ask
* @returns {Promise<string>} User's response
*/
export async function prompt(question) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer);
});
});
}
/**
* Requires explicit confirmation for dangerous commands
* @param {string} command - The dangerous command
* @param {string} description - Description of the danger
* @returns {Promise<boolean>} Whether user confirmed
*/
export async function confirmDangerousCommand(command, description) {
console.log(chalk.red('\n⚠️ DANGEROUS COMMAND DETECTED ⚠️'));
console.log(chalk.red(`Risk: ${description}`));
console.log(chalk.yellow(`Command: ${command}`));
console.log(chalk.red('\nThis command could cause serious damage to your system!'));
console.log(chalk.yellow('Are you ABSOLUTELY SURE you want to run this?'));
const response = await prompt('Type "YES I AM SURE" to continue, or anything else to cancel: ');
return response === 'YES I AM SURE';
}
/**
* Requires confirmation for warning commands
* @param {string} command - The risky command
* @param {string} description - Description of the risk
* @returns {Promise<boolean>} Whether user confirmed
*/
export async function confirmWarningCommand(command, description) {
console.log(chalk.yellow('\n⚠️ Warning: Potentially risky command'));
console.log(chalk.yellow(`Note: ${description}`));
console.log(chalk.cyan(`Command: ${command}`));
const response = await prompt('Type "yes" to continue, or anything else to cancel: ');
return response.toLowerCase() === 'yes';
}