UNPKG

@eladtest/mcp

Version:

MCP server for shellfirm - provides interactive command validation with captcha

314 lines (313 loc) 11.4 kB
"use strict"; /** * TypeScript wrapper for Shellfirm WASM module * * This module provides a clean TypeScript interface to the Rust-based * command validation engine compiled to WASM. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.initShellfirmWasm = initShellfirmWasm; exports.validateCommand = validateCommand; exports.validateCommandSimple = validateCommandSimple; exports.validateSplitCommand = validateSplitCommand; exports.validateSplitCommandWithOptions = validateSplitCommandWithOptions; exports.getAllPatterns = getAllPatterns; exports.getPatternGroups = getPatternGroups; exports.getPatternsForGroup = getPatternsForGroup; exports.createFileExistenceCache = createFileExistenceCache; exports.testWasmModule = testWasmModule; const path = __importStar(require("path")); const fs = __importStar(require("fs")); const logger_js_1 = require("./logger.js"); // Import WASM module - we'll handle the import dynamically let wasmModule = null; /** * Initialize the WASM module */ async function initShellfirmWasm() { if (wasmModule) { return; // Already initialized } try { // Try to load from the pkg directory (relative to compiled lib folder) const pkgPath = path.resolve(__dirname, '..', 'pkg', 'shellfirm_core.js'); if (fs.existsSync(pkgPath)) { // In CommonJS, prefer require with a filesystem path (no file:// URL) // @ts-ignore - require is available in CommonJS builds wasmModule = require(pkgPath); } else { await (0, logger_js_1.error)('wasm', { message: 'Pkg directory not found, trying node_modules' }); // Fallback: try from node_modules if installed as a dependency // @ts-ignore - require is available in CommonJS builds wasmModule = require('shellfirm_core'); await (0, logger_js_1.error)('wasm', { message: 'Initialized from node_modules', level: 'info' }); } // Initialize the WASM module if (wasmModule.init) { wasmModule.init(); } } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Failed to load module', error: String(error) }); throw new Error(`Failed to initialize Shellfirm WASM module: ${error}`); } } /** * Validate a command using the WASM module */ async function validateCommand(command, options = {}) { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { // Create WASM validation options const wasmOptions = new wasmModule.WasmValidationOptions(); if (options.deny_pattern_ids && options.deny_pattern_ids.length > 0) { wasmOptions.set_deny_pattern_ids(JSON.stringify(options.deny_pattern_ids)); } if (options.allowed_severities && options.allowed_severities.length > 0) { wasmOptions.set_allowed_severities(JSON.stringify(options.allowed_severities)); } // Validate the command const result = wasmModule.validate_command_wasm(command, wasmOptions); // Parse the matches from JSON and extract properties before freeing const matches = JSON.parse(result.matches); const should_challenge = result.should_challenge; const should_deny = result.should_deny; // Clean up WASM objects wasmOptions.free(); result.free(); return { matches, should_challenge, should_deny, }; } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Validation error', error: String(error) }); throw new Error(`Command validation failed: ${error}`); } } /** * Simple command validation without options (for backward compatibility) */ async function validateCommandSimple(command) { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { const result = wasmModule.validate_command_simple_wasm(command); const matches = JSON.parse(result.matches); const should_challenge = result.should_challenge; const should_deny = result.should_deny; // Clean up WASM objects result.free(); return { matches, should_challenge, should_deny, }; } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Simple validation error', error: String(error) }); throw new Error(`Command validation failed: ${error}`); } } /** * Validate a command by parsing, splitting, and checking each part * This is the recommended function for command validation as it handles * complex shell commands with operators like &, |, &&, || */ async function validateSplitCommand(command) { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { const result = wasmModule.validate_command_with_split_wasm(command); const matches = JSON.parse(result.matches); const should_challenge = result.should_challenge; const should_deny = result.should_deny; // Clean up WASM objects result.free(); return { matches, should_challenge, should_deny, }; } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Split command validation error', error: String(error) }); throw new Error(`Command validation failed: ${error}`); } } /** * Validate a split command with options */ async function validateSplitCommandWithOptions(command, options = {}) { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } let wasmOptions = null; let result = null; try { // Create WASM validation options wasmOptions = new wasmModule.WasmValidationOptions(); if (options.deny_pattern_ids && options.deny_pattern_ids.length > 0) { wasmOptions.set_deny_pattern_ids(JSON.stringify(options.deny_pattern_ids)); } if (options.allowed_severities && options.allowed_severities.length > 0) { wasmOptions.set_allowed_severities(JSON.stringify(options.allowed_severities)); } // Validate the command using the split command function result = wasmModule.validate_command_with_options_wasm(command, wasmOptions); // At this point, both wasmOptions and result are guaranteed to be non-null if (!wasmOptions || !result) { throw new Error('Failed to create WASM objects'); } // Parse the matches from JSON and extract properties before freeing const matches = JSON.parse(result.matches); const should_challenge = result.should_challenge; const should_deny = result.should_deny; return { matches, should_challenge, should_deny, }; } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Split command validation with options error', error: String(error) }); throw new Error(`Command validation failed: ${error}`); } finally { // Clean up WASM objects safely try { if (result && result.ptr && typeof result.ptr === 'number' && result.ptr !== 0) { result.free(); result.ptr = 0; } } catch { } try { if (wasmOptions && wasmOptions.ptr && typeof wasmOptions.ptr === 'number' && wasmOptions.ptr !== 0) { wasmOptions.free(); wasmOptions.ptr = 0; } } catch { } } } /** * Get all available patterns */ async function getAllPatterns() { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { const patternsJson = wasmModule.get_all_patterns_wasm(); return JSON.parse(patternsJson); } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Get patterns error', error: String(error) }); throw new Error(`Failed to get patterns: ${error}`); } } /** * Get pattern groups/categories */ async function getPatternGroups() { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { const groupsJson = wasmModule.get_pattern_groups_wasm(); return JSON.parse(groupsJson); } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Get groups error', error: String(error) }); throw new Error(`Failed to get pattern groups: ${error}`); } } /** * Get patterns for a specific group */ async function getPatternsForGroup(group) { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } try { const patternsJson = wasmModule.get_patterns_for_group_wasm(group); return JSON.parse(patternsJson); } catch (error) { await (0, logger_js_1.error)('wasm', { message: 'Get group patterns error', error: String(error) }); throw new Error(`Failed to get patterns for group ${group}: ${error}`); } } /** * Create file existence cache from file system paths * This is a utility function to help with file existence checking in WASM */ function createFileExistenceCache(filePaths) { const cache = {}; for (const filePath of filePaths) { try { cache[filePath] = fs.existsSync(filePath); } catch { cache[filePath] = false; } } return cache; } /** * Test function to verify WASM module is working */ async function testWasmModule() { await initShellfirmWasm(); if (!wasmModule) { throw new Error('WASM module not initialized'); } return wasmModule.test_wasm_module(); }