nikto
Scan web servers for vulnerabilities like outdated software, misconfigurations, and security issues to identify potential attack vectors.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| target | Yes | Target URL | |
| port | No | Port(s) to scan | |
| ssl | No | Force SSL mode | |
| timeout | No | Timeout for requests | |
| useragent | No | User-Agent string | |
| tuning | No | Tuning mode | |
| output | No | Output file | |
| proxy | No | Use proxy | |
| basicAuth | No | Basic authentication credentials (username:password) | |
| root | No | Root directory | |
| cookies | No | Cookies to include | |
| rawOptions | No | Raw nikto options |
Implementation Reference
- src/index.ts:553-587 (handler)Core handler function that executes the Nikto web vulnerability scanner by spawning the 'nikto' process, sanitizes options, captures output, extracts findings (lines starting with '+'), and logs results.async function runNikto(target: string, rawOptions: string[] = []): Promise<{ fullOutput: string; findings: string[] }> { console.error(`Executing Nikto: target=${target}, raw_options=${rawOptions.join(' ')}`); if (!target.startsWith('http://') && !target.startsWith('https://')) { throw new Error("Target must be a valid URL starting with http:// or https://"); } let options: string[]; try { options = sanitizeOptions(rawOptions); } catch (error: any) { throw error; } let fullOutput = ""; let findings: string[] = []; try { const baseArgs = ['-h', target, ...options]; fullOutput += `--- Vulnerability Scan ---\nExecuting: nikto ${baseArgs.join(' ')}\n`; try { const result = await runSpawnCommand('nikto', baseArgs); fullOutput += `Exit Code: ${result.code}\nStdout:\n${result.stdout}\nStderr:\n${result.stderr}\n`; findings = result.stdout.split('\n').filter(line => line.startsWith('+') && !line.includes('+ No web server')).map(line => line.trim()); } catch (error: any) { fullOutput += `Nikto command failed to execute: ${error.message}\n`; } if (currentUserSession.mode === UserMode.PROFESSIONAL) { await logMessage(`Ran Nikto against ${target}.\nOptions: ${options.join(' ')}\nFound: ${findings.length} potential issues.`); } return { fullOutput, findings }; } catch (error: any) { console.error("Fatal error setting up Nikto execution:", error); if (currentUserSession.mode === UserMode.PROFESSIONAL) { await logMessage(`Nikto FAILED fatally before execution.\nTarget: ${target}\nOptions: ${options.join(' ')}\nError: ${error.message}`); } throw new Error(`Nikto setup failed fatally: ${error.message}`); } }
- src/index.ts:1142-1155 (schema)Zod schema defining the input parameters and validation for the 'nikto' tool, including target URL (required), optional ports, SSL, timeouts, auth, and raw options.const niktoToolSchema = z.object({ target: z.string().url().describe("Target URL"), port: z.string().optional().describe("Port(s) to scan"), ssl: z.boolean().optional().describe("Force SSL mode"), timeout: z.number().int().optional().describe("Timeout for requests"), useragent: z.string().optional().describe("User-Agent string"), tuning: z.string().optional().describe("Tuning mode"), output: z.string().optional().describe("Output file"), proxy: z.string().optional().describe("Use proxy"), basicAuth: z.string().optional().describe("Basic authentication credentials (username:password)"), root: z.string().optional().describe("Root directory"), cookies: z.string().optional().describe("Cookies to include"), rawOptions: z.array(z.string()).optional().describe("Raw nikto options") }).describe(
- src/index.ts:605-613 (registration)Declares 'nikto' capability in the MCP server's capabilities object, enabling tool discovery."setMode": {}, "generateWordlist": {}, "cancelScan": {}, "createClientReport": {}, "nmapScan": {}, "runJohnTheRipper": {}, "runHashcat": {}, "gobuster": {}, "nikto": {}
- src/index.ts:1159-1191 (registration)Registers the 'nikto' tool with the MCP server using server.tool(), constructs options from inputs, calls runNikto handler, formats response for student/professional modes.server.tool("nikto", niktoToolSchema.shape, async (args /*: z.infer<typeof niktoToolSchema> */ /*, extra */) => { const { target, port, ssl, timeout, useragent, tuning, output, proxy, basicAuth, root, cookies, rawOptions } = args; console.error(`Received nikto request:`, args); try { const constructedOptions: string[] = []; constructedOptions.push('-nointeractive'); if (port) constructedOptions.push('-p', port); if (ssl) constructedOptions.push('-ssl'); if (timeout) constructedOptions.push('-Timeout', timeout.toString()); if (useragent) constructedOptions.push('-useragent', useragent); if (tuning) constructedOptions.push('-Tuning', tuning); if (output) constructedOptions.push('-o', output); if (proxy) constructedOptions.push('-useproxy', proxy); if (basicAuth) { constructedOptions.push('-id', basicAuth); } if (root) constructedOptions.push('-root', root); if (cookies) constructedOptions.push('-Cookies', cookies); if (rawOptions) constructedOptions.push(...rawOptions); const { fullOutput, findings } = await runNikto(target, constructedOptions); const responseContent: any[] = []; if (currentUserSession.mode === UserMode.STUDENT) { responseContent.push({ type: "text", text: `Found ${findings.length} issues at ${target}` }); if (findings.length > 0) { /* Categorize and add explanations */ } else { responseContent.push({ type: "text", text: "\n**No significant issues found...**" }); } } else { responseContent.push({ type: "text", text: `Found ${findings.length} issues at ${target}` }); if (findings.length > 0) responseContent.push({ type: "text", text: "\n**Findings:**\n- " + findings.join('\n- ') }); responseContent.push({ type: "text", text: "\n**Full Output:**\n" + fullOutput }); if (findings.some(f => f.toLowerCase().includes('injection'))) { /* Suggest follow-up */ } } return { content: responseContent }; } catch (error: any) { return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true }; } });
- src/index.ts:273-291 (helper)Generic helper function to spawn external commands (like 'nikto'), capture stdout/stderr, and handle process lifecycle. Used by runNikto.async function runSpawnCommand(command: string, args: string[]): Promise<{ stdout: string; stderr: string; code: number | null }> { return new Promise((resolve, reject) => { console.error(`Attempting to spawn: ${command} ${args.join(' ')}`); // Added for debugging const process = spawn(command, args); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('error', (error) => { // Explicitly catch spawn errors (e.g., command not found) console.error(`Spawn error for command "${command}": ${error.message}`); reject(new Error(`Failed to start command "${command}": ${error.message}`)); }); process.on('close', (code) => { console.error(`Command "${command}" exited with code ${code}`); // Added for debugging resolve({ stdout, stderr, code }); }); }); }