UNPKG

nikto-mcp

Version:

A secure MCP (Model Context Protocol) server that enables AI agents to interact with Nikto web server scanner

119 lines 4.36 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateScanOptions = validateScanOptions; exports.sanitizeInput = sanitizeInput; const zod_1 = require("zod"); const index_1 = require("../config/index"); // Helper functions for type coercion const coerceToNumber = (value) => { if (typeof value === 'number') { return value; } if (typeof value === 'string') { const trimmed = value.trim(); if (trimmed === '') { return undefined; } const num = Number(trimmed); if (!isNaN(num)) { return num; } } return value; // Let Zod handle the validation error }; const coerceToBoolean = (value) => { if (typeof value === 'boolean') { return value; } if (typeof value === 'string') { const trimmed = value.trim().toLowerCase(); if (trimmed === 'true' || trimmed === '1') { return true; } if (trimmed === 'false' || trimmed === '0') { return false; } if (trimmed === '') { return undefined; } } if (typeof value === 'number') { if (value === 1) { return true; } if (value === 0) { return false; } } return value; // Let Zod handle the validation error }; const scanOptionsSchema = zod_1.z .object({ target: zod_1.z .string() .min(1, 'Target is required') .refine((value) => { // Basic validation for URL, IP, hostname, or hostname:port const urlPattern = /^https?:\/\/.+/; const ipPattern = /^(\d{1,3}\.){3}\d{1,3}$/; const hostnamePattern = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?(\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?)*$/; // Check for hostname:port format const parts = value.split(':'); if (parts.length === 2 && parts[0] && parts[1]) { const hostname = parts[0]; const portStr = parts[1]; const port = parseInt(portStr, 10); if ((hostnamePattern.test(hostname) || ipPattern.test(hostname)) && !isNaN(port) && port > 0 && port <= 65535) { return true; } } return urlPattern.test(value) || ipPattern.test(value) || hostnamePattern.test(value); }, 'Invalid target: must be a valid URL, IP address, or hostname'), port: zod_1.z.preprocess(coerceToNumber, zod_1.z.number().int().min(1).max(65535).optional().default(80)), ssl: zod_1.z.preprocess(coerceToBoolean, zod_1.z.boolean().optional().default(false)), nossl: zod_1.z.preprocess(coerceToBoolean, zod_1.z.boolean().optional().default(false)), nolookup: zod_1.z.preprocess(coerceToBoolean, zod_1.z.boolean().optional().default(false)), timeout: zod_1.z.preprocess(coerceToNumber, zod_1.z.number().positive().optional().default(index_1.config.defaultTimeout)), vhost: zod_1.z .string() .optional() .refine((value) => { if (!value) { return true; } // Validate hostname format for vhost const hostnamePattern = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?(\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?)*$/; return hostnamePattern.test(value); }, 'Invalid vhost: must be a valid hostname'), outputFormat: zod_1.z.enum(['json', 'text']).optional().default('json'), dryRun: zod_1.z.preprocess(coerceToBoolean, zod_1.z.boolean().optional().default(false)), }) .refine((data) => !(data.ssl && data.nossl), { message: 'Cannot specify both ssl and nossl options', path: ['ssl', 'nossl'], }); function validateScanOptions(options) { try { return scanOptionsSchema.parse(options); } catch (error) { if (error instanceof zod_1.z.ZodError) { const message = error.issues .map((e) => `${e.path.join('.')}: ${e.message}`) .join(', '); throw new Error(`Validation error: ${message}`); } throw error; } } function sanitizeInput(input) { // Remove potentially dangerous characters for shell commands return input .replace(/[;&|`$<>\\]/g, '') .replace(/\n|\r/g, '') .trim(); } //# sourceMappingURL=scan.validator.js.map