Skip to content

Security

github-actions[bot] edited this page Mar 23, 2026 · 20 revisions

Security

This page documents the security architecture of the MATLAB MCP Server, covering authentication, input validation, secure configuration, deployment best practices, and vulnerability reporting.

Authentication & Authorization

Session-Based Access Control

The server implements session isolation to separate requests from different users or agents:

graph TD
    A["AI Agent / Client"] -->|Session ID| B["MCP Server"]
    B -->|Session Lookup| C["Session Manager"]
    C -->|Temp Directory| D["Isolated Workspace"]
    D -->|clear all| E["MATLAB Engine"]
    A2["Another Agent"] -->|Different Session ID| B
    B -->|Session Lookup| C2["Session Manager"]
    C2 -->|Different Temp Dir| D2["Isolated Workspace"]
Loading
  • Each session has a unique ID and isolated temporary directory
  • Sessions are authenticated implicitly by their ID (no username/password in stdio mode)
  • For multi-user SSE deployments, reverse proxy authentication is required (see below)

SSE Transport Authentication

When using SSE (Server-Sent Events) for multi-user deployments:

server:
  transport: "sse"
  host: "127.0.0.1"        # Never expose directly to internet
  port: 8765

security:
  require_proxy_auth: true  # Set to true ONLY after setting up proxy auth

Required setup:

  1. Place the server behind a reverse proxy (nginx, Caddy, Traefik, Apache)
  2. Configure the proxy with authentication (JWT, OAuth, OIDC, mTLS, basic auth)
  3. The proxy forwards authenticated requests with a header (e.g., X-User-ID)
  4. Set require_proxy_auth: true to acknowledge this is configured

Example nginx config with basic auth:

upstream matlab_mcp {
    server 127.0.0.1:8765;
}

server {
    listen 443 ssl http2;
    server_name matlab.example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        auth_basic "MATLAB MCP Server";
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        proxy_pass http://matlab_mcp;
        proxy_set_header X-User-ID $remote_user;
    }
}

The server logs a warning at startup if SSE transport is enabled without require_proxy_auth: true.


Input Validation & Sanitization

MATLAB Code Validation

All user-provided MATLAB code is checked before execution by the SecurityValidator class:

graph LR
    A["User Code"] -->|Security Check| B{Blocked Functions?}
    B -->|Found| C["❌ Reject"]
    B -->|Not Found| D["✓ Execute"]
    C --> E["Error Response"]
    D --> F["Job Executor"]
Loading

Function Blocklist

By default, these functions are blocked:

Function Risk Alternative
system() OS command injection Use MATLAB's built-in functions (e.g., fileread, writematrix)
unix() Unix command injection
dos() Windows command injection
! Shell escape operator
eval() Arbitrary code execution Use function handles or feval() with validated function names
feval() Function name string injection Whitelist approved function names in code review
evalc() Evaluate and capture
evalin() Context-aware eval
assignin() Variable injection Direct assignment in workspace
perl() Perl script injection
python() Python script injection Use MATLAB's Python integration with validated scripts
web() Network requests Use webread() / webwrite() with validated URLs

Smart Scanning

The validator strips string literals and comments before checking, preventing false positives:

% ✓ SAFE (will NOT trigger blocklist):
disp('system call is dangerous')      % "system" inside string
% system('ls')                         % "system" inside comment
msg = "unix-style paths";              % "unix" inside string
help_url = "https://system.example.com"; % "system" inside URL string

% ❌ BLOCKED (actual function calls):
system('rm -rf /')                     % Direct call
result = eval(code_string)             % Direct call
data = evalin('base', 'x*2')           % Direct call

Scanning algorithm:

  1. Remove all MATLAB comments (% to end-of-line, %{...%} block comments)
  2. Remove all string literals ('...' and "...", including escaped quotes)
  3. Check remaining code for blocked function names using regex

Filename Sanitization

File operations (upload_data, delete_file, read_script, etc.) sanitize filenames to prevent path traversal attacks:

% ❌ REJECTED:
../../../etc/passwd       % Path traversal
..\..\windows\system32    % Windows traversal
/etc/shadow               % Absolute path
C:\Windows\System32       % Absolute Windows path

Allowed characters:

  • Letters: a-zA-Z
  • Digits: 0-9
  • Punctuation: ._-

Examples of valid filenames:

  • data.csv
  • my-script_v2.m
  • simulation.results.2024.mat

Implementation (src/matlab_mcp/security/validator.py):

import re

VALID_FILENAME = re.compile(r'^[a-zA-Z0-9._\-]+$')

def validate_filename(filename: str) -> bool:
    """Check filename contains only safe characters."""
    return bool(VALID_FILENAME.match(filename)) and len(filename) > 0

Upload Size Limits

security:
  max_upload_size_mb: 100  # Default limit

Files exceeding this limit are rejected before being written to disk:

async def upload_data_impl(..., max_size_mb: int = 100) -> dict:
    max_bytes = max_size_mb * (1024 ** 2)
    if len(decoded_content) > max_bytes:
        return {"status": "error", "message": f"File exceeds {max_size_mb}MB"}

Security Configuration

Complete Security Settings

# config.yaml
security:
  # Function blocklist
  blocked_functions_enabled: true
  blocked_functions:
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
    - "web"
    # Add additional functions as needed

  # Upload protection
  max_upload_size_mb: 100

  # Multi-user SSE security
  require_proxy_auth: false  # Set to true only after configuring reverse proxy auth

execution:
  # Workspace isolation between sessions
  workspace_isolation: true  # Clears variables between jobs

sessions:
  # Session expiration
  session_timeout: 3600  # Seconds (1 hour)
  max_sessions: 50
  job_retention_seconds: 86400  # 24 hours

Environment Variable Overrides

All security settings can be overridden via environment variables using the MATLAB_MCP_ prefix:

# Override max upload size
export MATLAB_MCP_SECURITY_MAX_UPLOAD_SIZE_MB=500

# Disable function blocklist (NOT recommended)
export MATLAB_MCP_SECURITY_BLOCKED_FUNCTIONS_ENABLED=false

# Require proxy auth for SSE
export MATLAB_MCP_SECURITY_REQUIRE_PROXY_AUTH=true

Recommended Values

Setting Personal Team Production
blocked_functions_enabled true true true
max_upload_size_mb 100 500 100
workspace_isolation true true true
session_timeout 3600 1800 1800
max_sessions 50 20 10
require_proxy_auth false true true
transport stdio sse sse

Workspace Isolation

When workspace_isolation: true (default), the server executes these commands between jobs in the same session:

clear all;          % Remove all variables
clear global;       % Remove global variables
clear functions;    % Forget function definitions
fclose all;         % Close file handles
restoredefaultpath; % Restore default MATLAB path

Benefits:

  • Variables from one execution don't leak to the next
  • File handles don't persist across jobs
  • Function caches are cleared
  • MATLAB path is reset to defaults

Disable if needed (not recommended):

execution:
  workspace_isolation: false

Secure Deployment

Personal/Local Use

Minimal configuration:

server:
  transport: "stdio"  # No network exposure
  log_level: "info"

pool:
  min_engines: 1
  max_engines: 2

security:
  blocked_functions_enabled: true
  max_upload_size_mb: 100

Deployment:

  • Connect via Claude Desktop, VS Code, Cursor, or other MCP client
  • No authentication required (implicit via client connection)

Team/Shared Server

Recommended configuration:

server:
  transport: "sse"
  host: "127.0.0.1"        # Listen only on localhost
  port: 8765
  log_level: "info"

pool:
  min_engines: 2
  max_engines: 8
  health_check_interval: 30

execution:
  sync_timeout: 30
  workspace_isolation: true

security:
  blocked_functions_enabled: true
  max_upload_size_mb: 200
  require_proxy_auth: true  # MUST be true

sessions:
  max_sessions: 20
  session_timeout: 1800     # 30 minutes
  job_retention_seconds: 86400

monitoring:
  enabled: true             # Monitor for abuse
  sample_interval: 10

Deployment steps:

  1. Install the server on a Linux/macOS machine (not Windows for file permissions)
  2. Set up reverse proxy (nginx, Caddy, Traefik)
  3. Configure proxy authentication (LDAP, OAuth, JWT, mTLS)
  4. Configure TLS/SSL on the proxy
  5. Use require_proxy_auth: true in config
  6. Enable monitoring to detect suspicious activity
  7. Regularly review error logs and blocked function attempts

Example Caddy reverse proxy with JWT:

matlab.example.com {
    encode gzip
    
    # Validate JWT token
    jwt {
        algorithms HS256
        trusted_keys {
            key1 file /etc/caddy/jwt-key.txt
        }
    }
    
    # Reverse proxy to local server
    reverse_proxy 127.0.0.1:8765 {
        header_up X-User-ID {http.auth.user}
        header_up X-Forwarded-For {http.request.remote}
    }
}

Production Deployment

Hardened configuration:

server:
  name: "matlab-mcp-prod"
  transport: "sse"
  host: "127.0.0.1"        # Never expose directly
  port: 8765
  log_level: "warn"         # Reduce noise

pool:
  min_engines: 4
  max_engines: 16
  queue_max_size: 100
  health_check_interval: 20

execution:
  sync_timeout: 20          # Stricter timeout
  max_execution_time: 3600  # 1 hour max
  workspace_isolation: true

security:
  blocked_functions_enabled: true
  blocked_functions:
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
    - "web"
    - "load"                # Add custom restrictions
    - "save"
    - "diary"
  max_upload_size_mb: 50    # Stricter limit
  require_proxy_auth: true  # MANDATORY

sessions:
  max_sessions: 10          # Small pool
  session_timeout: 900      # 15 minutes
  job_retention_seconds: 86400

monitoring:
  enabled: true
  sample_interval: 5        # Frequent sampling
  store_events: true        # Persistent logging

Additional hardening:

  1. Network isolation: Run server in a restricted network segment
  2. TLS/mTLS: Require client certificates in addition to auth
  3. Rate limiting: Add to reverse proxy (e.g., 10 requests/sec per user)
  4. Audit logging: Store all requests and responses (via monitoring events)
  5. Resource quotas: Limit jobs per user, total concurrent jobs
  6. IP whitelisting: Restrict proxy access to known IP ranges
  7. Regular updates: Keep MATLAB engine and Python dependencies current
  8. Intrusion detection: Monitor for repeated blocked function attempts
  9. Secrets management: Store auth keys in a vault (HashiCorp Vault, AWS Secrets Manager)

Example audit logging (parse monitoring events):

# Retrieve event log
from matlab_mcp.monitoring.store import MetricsStore

store = MetricsStore("/path/to/metrics.db")
events = await store.get_events(event_type="blocked_function", limit=1000)
for event in events:
    if event["details"]["function"] == "system":
        print(f"ALERT: Blocked system() call at {event['timestamp']}")
        # Send to SIEM system

Vulnerability Reporting

How to Report Security Issues

Do NOT open a public GitHub issue for security vulnerabilities.

Instead, please email the maintainers with:

  • Title: Description of the vulnerability
  • Description: Detailed explanation of the issue
  • Reproduction steps: How to trigger the vulnerability
  • Impact: What could an attacker do?
  • Suggested fix: Optional, but helpful

Email: (Check the GitHub repository's SECURITY.md file for contact information, or file a private GitHub security advisory at https://github.com/HanSur94/matlab-mcp-server-python/security/advisories/new)

Security Update Process

  1. Vulnerability is reported privately
  2. Maintainers assess severity and impact
  3. A fix is developed and tested
  4. A security release is published with detailed notes
  5. Users are notified via GitHub Releases

Known Limitations

  • Stdio transport: Only safe for single-user, local use. No built-in encryption
  • Function blocklist: Cannot catch all injection attacks (always review user-provided code)
  • Async jobs: Results are stored in temp files; ensure proper file permissions
  • MATLAB version: Only tested with MATLAB R2020b+

Data Flow & Isolation

graph TD
    A["Agent 1<br/>Session: abc123"] -->|Code| B["Security Validator"]
    C["Agent 2<br/>Session: xyz789"] -->|Code| B
    B -->|Check Blocklist| D{Safe?}
    D -->|No| E["❌ Reject<br/>Error Response"]
    D -->|Yes| F["Job Executor"]
    F -->|Engine 1<br/>Workspace: /tmp/abc123| G["MATLAB Engine 1"]
    F -->|Engine 2<br/>Workspace: /tmp/xyz789| H["MATLAB Engine 2"]
    G -->|clear all| I["Isolated State"]
    H -->|clear all| J["Isolated State"]
    I -->|Result| K["Agent 1<br/>Only sees own results"]
    J -->|Result| L["Agent 2<br/>Only sees own results"]
Loading

Key principles:

  1. Code validation happens before execution — blocklist prevents dangerous operations
  2. Each session has isolated temp directory — files don't leak between users
  3. Workspace is cleared between jobs — variables don't persist
  4. MATLAB path is restored — custom paths don't affect other sessions
  5. File handles are closed — no state leaks across sessions

Testing Security

The test suite includes security validation tests in tests/test_security.py:

def test_blocks_system_call():
    validator = SecurityValidator(config)
    result = validator.validate("system('rm -rf /')")
    assert not result.is_valid
    assert "system" in result.blocked_functions

def test_allows_safe_code():
    validator = SecurityValidator(config)
    result = validator.validate("x = [1, 2, 3]; y = mean(x);")
    assert result.is_valid
    assert result.blocked_functions == []

def test_ignores_string_literals():
    validator = SecurityValidator(config)
    result = validator.validate("msg = 'system is great'; disp(msg);")
    assert result.is_valid  # "system" inside string is ignored

Run tests with:

pytest tests/test_security.py -v

Additional Resources

Clone this wiki locally