VYPR
Moderate severityNVD Advisory· Published Feb 8, 2026· Updated Feb 23, 2026

BurtTheCoder mcp-maigret search_username index.ts command injection

CVE-2026-2130

Description

A vulnerability was determined in BurtTheCoder mcp-maigret up to 1.0.12. This affects an unknown part of the file src/index.ts of the component search_username. Executing a manipulation of the argument Username can lead to command injection. The attack may be launched remotely. Upgrading to version 1.0.13 is able to mitigate this issue. This patch is called b1ae073c4b3e789ab8de36dc6ca8111ae9399e7a. Upgrading the affected component is advised.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Command injection in BurtTheCoder mcp-maigret (≤1.0.12) via unsanitized username argument passed to exec() is patched in v1.0.13.

Vulnerability

Overview

CVE-2026-2130 describes a command injection vulnerability in the search_username component of the MCP server mcp-maigret, a tool for OSINT username searches across public sources. The flaw resides in the src/index.ts file, where the Username argument passed to the tool was directly concatenated into a shell command executed via the Node.js exec() function. By supplying a specially crafted username containing shell metacharacters, a remote attacker could execute arbitrary commands on the server hosting the MCP server. The patch (commit b1ae073c4b3e789ab8de36dc6ca8111ae9399e7a) replaces exec() with execFile() and adds strict input validation for usernames, URLs, and tags, entirely eliminating shell interpolation [1][2][3].

Exploitation and

Impact

The vulnerability is remotely exploitable without authentication, as the search_username tool accepts user-supplied username strings and passes them to the operating system shell. Attackers with network access to the MCP server (e.g., via an MCP-compatible client like Claude Desktop) could inject arbitrary OS commands by embedding shell operators such as ;, |, or backticks in the username field. Successful exploitation grants the attacker the same privileges as the Node.js process, which could lead to data exfiltration, lateral movement, or full host compromise [1][2].

Mitigation and

Recommendations

Upgrading to version 1.0.13 is the recommended fix. The patch introduces comprehensive input validation functions (isValidUsername, isValidUrl, isValidTag) that restrict usernames to alphanumeric characters, underscores, hyphens, and periods (max 100 characters), and switches to execFile() for safe command execution without shell interpretation [1][4]. Administrators should update immediately and review any MCP server deployments for similar unsafe patterns.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
mcp-maigretnpm
< 1.0.131.0.13

Affected products

2

Patches

1
b1ae073c4b3e

Fix critical command injection vulnerabilities

2 files changed · +127 29
  • README.md+22 2 modified
    @@ -15,6 +15,23 @@ This tool is designed for legitimate OSINT research purposes. Please:
     - Use responsibly and ethically
     - Be aware that some sites may rate-limit or block automated searches
     
    +## Security
    +
    +This server implements several security measures to prevent command injection attacks:
    +
    +### Input Validation
    +- **Usernames**: Only alphanumeric characters, underscores, hyphens, and periods are allowed (max 100 characters)
    +- **URLs**: Must be valid HTTP/HTTPS URLs without shell metacharacters
    +- **Tags**: Only alphanumeric characters, underscores, and hyphens are allowed
    +
    +### Safe Command Execution
    +- Uses `execFile()` instead of `exec()` to prevent shell interpolation
    +- All command arguments are passed as arrays, not concatenated strings
    +- Docker commands are executed without shell interpretation
    +
    +### Reporting Security Issues
    +If you discover a security vulnerability, please report it by opening an issue or contacting the maintainers directly. We take security seriously and will respond promptly.
    +
     ## Requirements
     
     - Node.js (v18 or later)
    @@ -108,10 +125,10 @@ npm run build
     - Name: `search_username`
     - Description: Search for a username across social networks and sites
     - Parameters:
    -  * `username` (required): Username to search for
    +  * `username` (required): Username to search for (alphanumeric, underscores, hyphens, periods only; max 100 chars)
       * `format` (optional, default: "pdf"): Output format (txt, html, pdf, json, csv, xmind)
       * `use_all_sites` (optional, default: false): Use all available sites instead of top 500
    -  * `tags` (optional): Array of tags to filter sites (e.g., ["photo", "dating"])
    +  * `tags` (optional): Array of tags to filter sites (alphanumeric, underscores, hyphens only)
     
     Example:
     ```json
    @@ -175,6 +192,9 @@ docker ps
     - "MAIGRET_REPORTS_DIR environment variable must be set": Add the environment variable to your configuration
     - "Error creating reports directory": Check directory permissions and path
     - "Error executing maigret": Check Docker logs and ensure the container has proper permissions
    +- "Invalid username": Username contains invalid characters. Use only alphanumeric, underscores, hyphens, and periods
    +- "Invalid URL": URL is malformed or contains prohibited characters
    +- "Invalid tag": Tag contains invalid characters. Use only alphanumeric, underscores, and hyphens
     
     ## Contributing
     
    
  • src/index.ts+105 27 modified
    @@ -7,13 +7,13 @@ import {
       ListToolsRequestSchema,
       McpError,
     } from '@modelcontextprotocol/sdk/types.js';
    -import { exec } from 'child_process';
    +import { execFile } from 'child_process';
     import { promisify } from 'util';
     import { platform } from 'os';
     import { existsSync, mkdirSync } from 'fs';
     import { join } from 'path';
     
    -const execAsync = promisify(exec);
    +const execFileAsync = promisify(execFile);
     const DOCKER_IMAGE = 'soxoj/maigret:latest';
     
     interface SearchUsernameArgs {
    @@ -37,6 +37,51 @@ function sanitizeFilename(filename: string): string {
       return filename.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_');
     }
     
    +/**
    + * Validates username to prevent command injection.
    + * Only allows alphanumeric characters, underscores, hyphens, and periods.
    + */
    +function isValidUsername(username: string): boolean {
    +  // Max length check to prevent DoS
    +  if (username.length === 0 || username.length > 100) {
    +    return false;
    +  }
    +  // Only allow safe characters commonly found in usernames
    +  return /^[a-zA-Z0-9_.-]+$/.test(username);
    +}
    +
    +/**
    + * Validates URL to prevent command injection.
    + * Must be a valid HTTP/HTTPS URL.
    + */
    +function isValidUrl(urlString: string): boolean {
    +  try {
    +    const url = new URL(urlString);
    +    // Only allow http and https protocols
    +    if (url.protocol !== 'http:' && url.protocol !== 'https:') {
    +      return false;
    +    }
    +    // Check for suspicious characters that could be used for injection
    +    if (/[;&|`$(){}[\]<>\\]/.test(urlString)) {
    +      return false;
    +    }
    +    return true;
    +  } catch {
    +    return false;
    +  }
    +}
    +
    +/**
    + * Validates tags to prevent command injection.
    + * Only allows alphanumeric characters, underscores, and hyphens.
    + */
    +function isValidTag(tag: string): boolean {
    +  if (tag.length === 0 || tag.length > 50) {
    +    return false;
    +  }
    +  return /^[a-zA-Z0-9_-]+$/.test(tag);
    +}
    +
     function isSearchUsernameArgs(args: unknown): args is SearchUsernameArgs {
       if (!args || typeof args !== 'object') return false;
       const a = args as Record<string, unknown>;
    @@ -96,10 +141,14 @@ class MaigretServer {
         });
       }
     
    -  private async execCommand(command: string): Promise<ExecResult> {
    -    console.error('Executing command:', command);
    +  /**
    +   * Executes a command safely using execFile (no shell interpolation).
    +   * Arguments are passed as an array to prevent command injection.
    +   */
    +  private async execCommand(cmd: string, args: string[]): Promise<ExecResult> {
    +    console.error('Executing command:', cmd, args.join(' '));
         try {
    -      const result = await execAsync(command, {
    +      const result = await execFileAsync(cmd, args, {
             maxBuffer: 10 * 1024 * 1024
           });
           console.error('Command output:', result.stdout);
    @@ -115,18 +164,18 @@ class MaigretServer {
         try {
           console.error('Checking Docker...');
           try {
    -        await this.execCommand('docker --version');
    +        await this.execCommand('docker', ['--version']);
           } catch (error) {
             throw new Error('Docker is not installed or not running. Please install Docker and try again.');
           }
     
           console.error('Checking if maigret image exists...');
           try {
    -        await this.execCommand(`docker image inspect ${DOCKER_IMAGE}`);
    +        await this.execCommand('docker', ['image', 'inspect', DOCKER_IMAGE]);
             console.error('Maigret image found');
           } catch (error) {
             console.error('Maigret image not found, pulling...');
    -        await this.execCommand(`docker pull ${DOCKER_IMAGE}`);
    +        await this.execCommand('docker', ['pull', DOCKER_IMAGE]);
             console.error('Maigret image pulled successfully');
           }
         } catch (error) {
    @@ -208,38 +257,57 @@ class MaigretServer {
                   );
                 }
     
    -            const { 
    -              username, 
    +            const {
    +              username,
                   format = 'pdf',
                   use_all_sites = false,
                   tags = []
                 } = request.params.arguments;
     
    +            // Security: Validate username to prevent command injection
    +            if (!isValidUsername(username)) {
    +              throw new McpError(
    +                ErrorCode.InvalidParams,
    +                'Invalid username. Username must contain only alphanumeric characters, underscores, hyphens, and periods (max 100 characters).'
    +              );
    +            }
    +
    +            // Security: Validate all tags
    +            for (const tag of tags) {
    +              if (!isValidTag(tag)) {
    +                throw new McpError(
    +                  ErrorCode.InvalidParams,
    +                  `Invalid tag: "${tag}". Tags must contain only alphanumeric characters, underscores, and hyphens.`
    +                );
    +              }
    +            }
    +
                 const safeUsername = sanitizeFilename(username);
                 const reportPath = join(this.reportsDir, `report_${safeUsername}.${format}`);
     
    -            // Build command arguments
    -            const args = [
    +            // Build docker command arguments (passed as array to prevent shell injection)
    +            const dockerArgs = [
    +              'run', '--rm',
    +              '-v', `${this.reportsDir}:/app/reports`,
    +              DOCKER_IMAGE,
                   username,
                   `--${format}`,
                   '--no-color',
                   '--no-progressbar',
    -              '-n', '200'  // Increase max connections from default 100 to 200
    +              '-n', '200'
                 ];
     
                 if (use_all_sites) {
    -              args.push('-a');
    +              dockerArgs.push('-a');
                 }
     
                 if (tags.length > 0) {
    -              args.push('--tags', tags.join(','));
    +              dockerArgs.push('--tags', tags.join(','));
                 }
     
    -            // Run maigret in Docker
    -            const { stdout, stderr } = await this.execCommand(
    -              `docker run --rm -v "${this.reportsDir}:/app/reports" ${DOCKER_IMAGE} ${args.join(' ')}`
    -            );
    -            
    +            // Run maigret in Docker using execFile (safe from shell injection)
    +            const { stdout, stderr } = await this.execCommand('docker', dockerArgs);
    +
                 return {
                   content: [
                     {
    @@ -260,19 +328,29 @@ class MaigretServer {
     
                 const { url, format = 'pdf' } = request.params.arguments;
     
    -            const args = [
    +            // Security: Validate URL to prevent command injection
    +            if (!isValidUrl(url)) {
    +              throw new McpError(
    +                ErrorCode.InvalidParams,
    +                'Invalid URL. Must be a valid HTTP or HTTPS URL without special shell characters.'
    +              );
    +            }
    +
    +            // Build docker command arguments (passed as array to prevent shell injection)
    +            const dockerArgs = [
    +              'run', '--rm',
    +              '-v', `${this.reportsDir}:/app/reports`,
    +              DOCKER_IMAGE,
                   '--parse', url,
                   `--${format}`,
                   '--no-color',
                   '--no-progressbar',
    -              '--timeout', '60',  // 60 second timeout per request
    -              '-n', '200'  // Increase max connections from default 100 to 200
    +              '--timeout', '60',
    +              '-n', '200'
                 ];
     
    -            // Run maigret in Docker
    -            const { stdout, stderr } = await this.execCommand(
    -              `docker run --rm -v "${this.reportsDir}:/app/reports" ${DOCKER_IMAGE} ${args.join(' ')}`
    -            );
    +            // Run maigret in Docker using execFile (safe from shell injection)
    +            const { stdout, stderr } = await this.execCommand('docker', dockerArgs);
     
                 return {
                   content: [
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.