UNPKG

ludus-mcp

Version:

MCP server for managing Ludus cybersecurity training environments through natural language commands

177 lines (171 loc) 8.26 kB
import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; export function createSetRangeConfigTool(logger, cliWrapper) { return { name: 'set_range_config', description: `Set a range configuration in Ludus using an existing configuration file. **WORKING DIRECTORY**: All file paths are relative to ~/.ludus-mcp/range-config-templates/ **PATH USAGE**: - Use relative paths only: "base-configs/acme.yml", "user1/config.yaml", "mp/settings.yml" - DO NOT use: "~/.ludus-mcp/...", "/absolute/paths", or "../traversal" **EXAMPLES**: - file: "base-configs/acme.yml" → sets from ~/.ludus-mcp/range-config-templates/base-configs/acme.yml - file: "user1/config.yaml" → sets from ~/.ludus-mcp/range-config-templates/user1/config.yaml CRITICAL LLM BEHAVIOR: CREDENTIAL REDACTION REQUIRED When displaying configuration content in conversation, you MUST replace any non-range-specific credentials such as API keys for external services, passwords not specific to the cyber range environment, or similar credentials with "REDACTED-CREDENTIAL" to prevent credential exposure in chat logs. IMPORTANT LLM BEHAVIORAL PROMPTS: - SAFETY FIRST: Ludus operations can be destructive and time-consuming - CHECK EXISTING STATE: Use list_user_ranges or get_range_status before major operations - ADMIN vs USER: Admin operations (--user flag) affect other users' ranges - be explicit - Configuration changes overwrite existing settings - warn users about data loss - Force flag should only be used when explicitly requested by the user - REDACT credentials when showing config content to users CRITICAL WORKFLOW REMINDER: - This step SETS the active configuration - it's required before deployment - deploy_range uses the currently SET config, not just any config file - Always set_range_config first, then deploy_range - Typical workflow: write_range_config → validate_range_config → set_range_config → deploy_range`, inputSchema: { type: 'object', properties: { file: { type: 'string', description: 'Relative file path (e.g., "base-configs/acme.yml") of the range configuration file. Must be within ~/.ludus-mcp/range-config-templates/', }, user: { type: 'string', description: 'User ID to set the configuration for (admin only)', }, force: { type: 'boolean', description: 'Force the configuration update even with testing enabled', default: false, }, verbose: { type: 'boolean', description: 'Enable verbose output', default: false, }, }, required: ['file'], }, }; } export async function handleSetRangeConfig(args, logger, cliWrapper) { const { file, user, force = false, verbose = false } = args; logger.info('Setting range configuration', { file, user, force, verbose }); try { // Resolve the file path const homeDir = os.homedir(); const configTemplatesDir = path.join(homeDir, '.ludus-mcp', 'range-config-templates'); const resolvedFilePath = path.join(configTemplatesDir, file); // Ensure the base directory exists if (!fs.existsSync(configTemplatesDir)) { fs.mkdirSync(configTemplatesDir, { recursive: true }); logger.warn('Created ~/.ludus-mcp/range-config-templates directory', { path: configTemplatesDir }); } // Security validation: ensure the resolved path is within allowed boundaries const relativePath = path.relative(configTemplatesDir, resolvedFilePath); if (relativePath && (relativePath.startsWith('..') || path.isAbsolute(relativePath))) { const errorMessage = `Access denied: file path '${file}' is outside allowed directory. Files must be within ~/.ludus-mcp/range-config-templates/`; logger.error('Security validation failed for file access', { file, resolvedFilePath, relativePath, configTemplatesDir }); return { success: false, file: file, error: errorMessage, message: `Failed to set range configuration: ${errorMessage}` }; } // Check if the file exists if (!fs.existsSync(resolvedFilePath)) { const errorMessage = `Configuration file not found: ${file}`; logger.error('Configuration file not found', { file, resolvedFilePath }); return { success: false, file: file, error: errorMessage, message: `Failed to set range configuration: ${errorMessage}` }; } // Build the command arguments const cmdArgs = ['config', 'set']; // Add file flag cmdArgs.push('-f', resolvedFilePath); // Add optional flags if (force) { cmdArgs.push('--force'); } if (verbose) { cmdArgs.push('--verbose'); } if (user) { cmdArgs.push('--user', user); } // Execute the command const result = await cliWrapper.executeCommand('range', cmdArgs); const targetUser = user || 'current user'; if (result.success) { return { content: [ { type: 'text', text: `Range configuration set successfully for ${targetUser}\n\n` + `Configuration file: ${file}\n` + `${force ? ' Force mode was enabled\n' : ''}` + `Output:\n` + `\`\`\`\n${result.rawOutput || result.message}\n\`\`\`\n\n` + `Next steps:\n` + `- Use get_range_status to check current range state\n` + `- Use deploy_range to deploy this configuration\n` + `- Use list_user_ranges to see all configured ranges` } ] }; } else { return { content: [ { type: 'text', text: `Failed to set range configuration for ${targetUser}\n\n` + `Configuration file: ${file}\n` + `Error Details:\n` + `\`\`\`\n${result.rawOutput || result.message}\n\`\`\`\n\n` + `Troubleshooting:\n` + `- Verify the configuration file exists and is valid\n` + `- Check file permissions and path accessibility\n` + `- Use validate_range_config to check the file first\n` + `- Try with --force flag if testing mode is preventing the update\n` + `- Ensure you have permissions for the specified user` } ] }; } } catch (error) { logger.error('Set range config failed', { file, user, error: error.message }); return { content: [ { type: 'text', text: `Error setting range configuration\n\n` + `File: ${file}\n` + `👤 User: ${user || 'current user'}\n` + `Error: ${error.message}\n\n` + `Troubleshooting:\n` + `- Verify Ludus CLI is properly configured\n` + `- Check network connectivity to Ludus server\n` + `- Ensure the configuration file path is correct\n` + `- Validate the config file format with validate_range_config` } ] }; } } //# sourceMappingURL=setRangeConfig.js.map