pulseengine-mcp-security 0.4.4

Security middleware and validation for MCP servers - PulseEngine MCP Framework
Documentation

pulseengine-mcp-security

Security middleware and input validation for MCP servers

License

This crate provides security middleware for MCP servers, including input validation, request sanitization, and protection against common web vulnerabilities.

What This Protects Against

Input Validation:

  • JSON injection attacks
  • XSS prevention in tool parameters
  • SQL injection prevention (when parameters go to databases)
  • Path traversal attacks in file operations
  • Command injection in system tools

Request Protection:

  • Request size limits to prevent DoS
  • Rate limiting integration
  • CORS policy enforcement
  • Security headers (HSTS, CSP, etc.)
  • Parameter type validation

Real-World Testing

This security layer is actively used in the Loxone MCP Server where it:

  • Validates 30+ tool parameters against injection attacks
  • Sanitizes device names and commands for safe execution
  • Protects file system operations from path traversal
  • Enforces request size limits for HTTP transport
  • Integrates with authentication for complete security

Quick Start

[dependencies]
pulseengine-mcp-security = "0.2.0"
pulseengine-mcp-protocol = "0.2.0"
serde_json = "1.0"

Basic Usage

Input Validation

use pulseengine_mcp_security::{SecurityValidator, ValidationRules};
use mcp_protocol::CallToolRequestParam;

// Create validator with rules
let validator = SecurityValidator::new(ValidationRules {
    max_string_length: 1000,
    max_array_size: 100,
    allow_html: false,
    allow_scripts: false,
    max_depth: 10,
});

// Validate tool parameters
let request = CallToolRequestParam {
    name: "get_file".to_string(),
    arguments: Some(serde_json::json!({
        "path": "/safe/path/file.txt"
    })),
};

match validator.validate_tool_request(&request) {
    Ok(sanitized_request) => {
        // Safe to process
        println!("Request is safe: {:?}", sanitized_request);
    }
    Err(e) => {
        println!("Security violation: {}", e);
        // Return error to client
    }
}

CORS Configuration

use pulseengine_mcp_security::{CorsConfig, SecurityMiddleware};

let cors_config = CorsConfig {
    allow_origins: vec![
        "https://your-dashboard.com".to_string(),
        "http://localhost:3000".to_string(), // Development
    ],
    allow_methods: vec!["GET".to_string(), "POST".to_string()],
    allow_headers: vec![
        "Content-Type".to_string(),
        "Authorization".to_string(),
    ],
    max_age: 3600,
};

let middleware = SecurityMiddleware::new(cors_config);

Request Size Limits

use pulseengine_mcp_security::SecurityConfig;

let config = SecurityConfig {
    max_request_size: 1024 * 1024, // 1MB limit
    max_parameter_count: 50,
    max_string_length: 10000,
    enable_xss_protection: true,
    enable_sql_injection_protection: true,
};

Current Status

Solid foundation with room for growth. The basic security validations work well and catch common attack vectors, but this area can always be improved.

What works reliably:

  • ✅ Basic input sanitization and validation
  • ✅ Request size and parameter limits
  • ✅ CORS policy enforcement
  • ✅ XSS prevention in string parameters
  • ✅ Path traversal prevention
  • ✅ Integration with mcp-server framework

Areas for improvement:

  • 🔧 More sophisticated injection detection
  • 📝 Better examples for different attack scenarios
  • 🧪 Security testing utilities
  • 🔧 More granular validation rules

Validation Features

String Sanitization

use pulseengine_mcp_security::sanitize_string;

// Remove potentially dangerous content
let clean = sanitize_string(
    "<script>alert('xss')</script>Hello World",
    &ValidationRules::default()
);
// Result: "Hello World"

// Validate file paths
let safe_path = sanitize_file_path("../../../etc/passwd")?;
// Error: Path traversal attempt detected

Parameter Validation

use pulseengine_mcp_security::validate_parameters;

let params = serde_json::json!({
    "device_name": "Living Room Light",
    "action": "on",
    "brightness": 75
});

// Validate against schema and security rules
let validated = validate_parameters(&params, &schema, &security_rules)?;

SQL Injection Prevention

use pulseengine_mcp_security::check_sql_injection;

let user_input = "'; DROP TABLE users; --";
if check_sql_injection(user_input) {
    return Err("SQL injection attempt detected".into());
}

Middleware Integration

With HTTP Transport

use pulseengine_mcp_security::SecurityMiddleware;
use axum::{Router, middleware};

let app = Router::new()
    .route("/mcp", post(handle_mcp_request))
    .layer(middleware::from_fn(SecurityMiddleware::validate_request))
    .layer(middleware::from_fn(SecurityMiddleware::cors_handler));

With MCP Server

use mcp_server::ServerConfig;
use pulseengine_mcp_security::SecurityConfig;

let security_config = SecurityConfig {
    enable_validation: true,
    max_request_size: 1024 * 1024,
    // ... other security settings
};

let server_config = ServerConfig {
    security_config,
    // ... other config
};

// Security validation happens automatically

Security Rules

Predefined Rule Sets

use pulseengine_mcp_security::{ValidationRules, SecurityLevel};

// Strict security for public-facing servers
let strict_rules = ValidationRules::strict();

// Moderate security for internal tools
let moderate_rules = ValidationRules::moderate();

// Minimal security for development
let dev_rules = ValidationRules::development();

Custom Validation Rules

use pulseengine_mcp_security::ValidationRules;

let custom_rules = ValidationRules {
    max_string_length: 500,
    max_array_size: 20,
    allow_html: false,
    allow_scripts: false,
    max_depth: 5,
    blocked_patterns: vec![
        r"(?i)(union|select|insert|delete|drop|exec)".to_string(),
        r"<script[^>]*>.*?</script>".to_string(),
    ],
    required_patterns: vec![
        // Must match UUID format for device IDs
        r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$".to_string(),
    ],
};

Security Headers

use pulseengine_mcp_security::SecurityHeaders;

let headers = SecurityHeaders::strict()
    .with_hsts(31536000) // 1 year
    .with_csp("default-src 'self'; script-src 'none'")
    .with_frame_options("DENY")
    .with_content_type_options()
    .with_referrer_policy("strict-origin-when-cross-origin");

Real-World Examples

File Access Validation

// From Loxone implementation - validating file paths
use pulseengine_mcp_security::validate_file_path;

fn handle_read_file(path: &str) -> Result<String, SecurityError> {
    // Prevent path traversal
    let safe_path = validate_file_path(path)?;
    
    // Ensure it's within allowed directory
    if !safe_path.starts_with("/safe/directory/") {
        return Err(SecurityError::unauthorized_path(path));
    }
    
    // Safe to read file
    std::fs::read_to_string(safe_path)
}

Device Command Validation

// Validate device control commands
fn validate_device_command(device: &str, action: &str) -> Result<(), SecurityError> {
    // Validate device identifier (prevent injection)
    if !device.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
        return Err(SecurityError::invalid_device_name(device));
    }
    
    // Validate action against whitelist
    let allowed_actions = ["on", "off", "dim", "up", "down", "stop"];
    if !allowed_actions.contains(&action) {
        return Err(SecurityError::invalid_action(action));
    }
    
    Ok(())
}

Contributing

Security is an ongoing concern and improvements are always welcome. Most valuable contributions:

  1. Security research - Finding new attack vectors or validation gaps
  2. Performance optimization - Security checks with minimal overhead
  3. Testing utilities - Tools for security testing and validation
  4. Real-world examples - Security patterns from actual deployments

If you find a security issue, please follow responsible disclosure practices.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Repository: https://github.com/avrabe/mcp-loxone

Note: This crate is part of a larger MCP framework that will be published as a separate repository.