# Input Validation & Output Sanitization

## Overview

ContextForge provides comprehensive input validation and output sanitization to protect against common security vulnerabilities including:

- **Path Traversal**: Prevents access to files outside allowed directories
- **Command Injection**: Blocks shell metacharacters in parameters
- **SQL Injection**: Validates and escapes SQL parameters
- **XSS Attacks**: Sanitizes output to remove dangerous content
- **Control Character Injection**: Removes ANSI escape sequences and control characters

## Configuration

### Environment Variables

```bash
# Enable experimental validation (default: false)
EXPERIMENTAL_VALIDATE_IO=true

# Deprecated global validation middleware (default: false)
VALIDATION_MIDDLEWARE_ENABLED=true

# Strict mode - reject on violations (default: true)
VALIDATION_STRICT=true

# Sanitize output responses (default: true)
SANITIZE_OUTPUT=true

# Allowed root paths for resource access (JSON array or comma-separated)
ALLOWED_ROOTS='["/srv/data", "/var/app"]'

# Maximum path depth (default: 10)
MAX_PATH_DEPTH=10

# Maximum parameter length (default: 10000)
MAX_PARAM_LENGTH=10000

# Dangerous patterns (regex, JSON array)
DANGEROUS_PATTERNS='["[;&|`$(){}\\[\\]<>]", "\\.\\.[\\\/]", "[\\x00-\\x1f\\x7f-\\x9f]"]'
```

### Roll-out Phases

The validation feature supports a phased roll-out approach:

#### Phase 0: Feature Flag (Off by Default)
```bash
EXPERIMENTAL_VALIDATE_IO=false  # Disabled in production
```

#### Phase 1: Log-Only Mode (Dev/Staging)
```bash
EXPERIMENTAL_VALIDATE_IO=true
VALIDATION_STRICT=false  # Warn only, don't block
```

#### Phase 2: Enforce in Staging
```bash
EXPERIMENTAL_VALIDATE_IO=true
VALIDATION_STRICT=true  # Block violations
```

#### Phase 3: Production Deployment
```bash
EXPERIMENTAL_VALIDATE_IO=true
VALIDATION_STRICT=true
SANITIZE_OUTPUT=true
```

## Validation Rules

### Path Traversal Defense

**Scenario**: Reject resource path traversal

```python
# Configuration
MCP_GW_ROOT="/srv/data"

# Attack attempt
uri = "/srv/data/../../secret.txt"

# Result: 400 "invalid_path: Path traversal detected"
# No files outside "/srv/data" are accessed
```

**Implementation**:

- Normalizes paths using `Path.resolve()`
- Checks for `..` sequences
- Validates against `ALLOWED_ROOTS`
- Enforces `MAX_PATH_DEPTH` limit

### Command Injection Prevention

**Scenario**: Prevent command injection via filename

```python
# Tool that shells out with filename parameter
filename = "bobbytables.jpg; cat /etc/passwd"

# Strict mode: Rejects with 422 "validation_failed"
# Non-strict mode: Escapes value using shlex.quote()
```

**Protected Patterns**:

- Shell metacharacters: `; & | \` $ ( ) { } [ ] < >`
- Command chaining: `&&`, `||`, `;`
- Pipe operators: `|`
- Backticks and command substitution

### SQL Injection Prevention

**Scenario**: Validate SQL parameters

```python
# Dangerous input
param = "'; DROP TABLE users; --"

# Strict mode: Rejects with 422 "validation_failed"
# Non-strict mode: Escapes quotes
```

**Protected Patterns**:

- Quote characters: `'`, `"`
- SQL comments: `--`, `/* */`
- SQL keywords: `UNION`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `DROP`

### Output Sanitization

**Scenario**: Sanitize tool output

```python
# Tool returns text with control characters
output = "Result: \x1b[31mError\x1b[0m\x00"

# Sanitized output
clean = "Result: Error"

# Control chars removed, Content-Type verified
```

**Sanitization Rules**:

- Removes C0 control characters (0x00-0x1F) except newlines/tabs
- Removes ANSI escape sequences
- Removes C1 control characters (0x7F-0x9F)
- Preserves `\n` (newline) and `\t` (tab)
- Verifies Content-Type matches payload

### Tool Description Content Validation

**Scenario**: MCP server tools whose descriptions contain Markdown syntax fail to register

**Blocked patterns**: `> `, `< `, `&&`, `||`, `$(`

These shell metacharacters are blocked by default in `ToolCreate` and `ToolUpdate` to reduce injection risk from externally-sourced tool metadata. However, they are common in Markdown content (e.g. `> blockquote`, `< input`). Note: single pipes (`|`), semicolons (`;`), and backticks (`` ` ``) are explicitly allowed as they commonly appear in natural-language documentation and Markdown tables.

**Control variables**:

| Variable | Default | Effect |
|----------|---------|--------|
| `TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED` | `true` | Master switch — set to `false` to disable all pattern checks |
| `TOOL_DESCRIPTION_FORBIDDEN_PATTERNS` | `["&&", "||", "$(", "> ", "< "]` | Override the blocked substrings (JSON array) |
| `VALIDATION_STRICT` | `true` | When `false`, matched patterns log a warning instead of rejecting |

**Option 1 — Customize the pattern list** (recommended):

```bash
# Keep injection checks but allow pipe and redirect syntax in descriptions
TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", "||", "$("]
```

**Option 2 — Disable pattern checks entirely**:

```bash
TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED=false
```

**Option 3 — Warn-only mode** (all patterns checked but never rejected):

```bash
VALIDATION_STRICT=false
```

When `VALIDATION_STRICT=false`:
- The forbidden-pattern check in `ToolCreate.validate_description` and `ToolUpdate.validate_description` logs a warning and proceeds instead of raising a validation error.
- All other sanitization (XSS, length truncation) still runs.

!!! note "Upgrade note (v0.9.0 → v1.0.0.Beta2)"
    The forbidden-pattern check was introduced in v1.0.0.Beta2. If you are upgrading from v0.9.0 and your MCP servers expose tools with Markdown descriptions, set `VALIDATION_STRICT=false` or customize `TOOL_DESCRIPTION_FORBIDDEN_PATTERNS` to allow the specific characters your tools use.

### JSON Schema Validation

**Scenario**: Validate tool and prompt schemas during registration

```python
# Tool registration with invalid schema
tool = {
    "name": "invalid_tool",
    "inputSchema": {
        "type": "object",
        "properties": {"arg": {"type": "unknown_type"}} # Invalid type
    }
}

# Strict mode (Default): Rejects with 400 Bad Request
# Non-strict mode: Logs warning but accepts registration
```

**Control variable**: `JSON_SCHEMA_VALIDATION_STRICT` (default: `true`)

**Validation Rules**:
- Enforces valid JSON Schema 2020-12 (default)
- Validates structural integrity of `inputSchema` for tools
- Validates `arguments` schema for tool prompts
- prevents registration of broken tools that would fail at runtime

## API Usage

### SecurityValidator Class

```python
from mcpgateway.common.validators import SecurityValidator

# Validate shell parameters
safe_param = SecurityValidator.validate_shell_parameter("filename.txt")

# Validate paths
safe_path = SecurityValidator.validate_path("/srv/data/file.txt", ["/srv/data"])

# Validate SQL parameters
safe_sql = SecurityValidator.validate_sql_parameter("user_input")

# Sanitize output
clean_text = SecurityValidator.sanitize_text("Text\x1b[31mwith\x1b[0mcolors")

# Sanitize JSON responses
clean_data = SecurityValidator.sanitize_json_response({
    "message": "Hello\x1bWorld",
    "items": ["test\x00", "clean"]
})
```

### ValidationMiddleware

!!! warning "Deprecated as of 2026-06-11; sunsets on 2026-07-07"
    `ValidationMiddleware` is deprecated. Prefer endpoint-level Pydantic
    validation, `SecurityValidator` helpers, and protocol-specific validation
    middleware. See [Deprecations](../deprecations.md).

The middleware automatically validates all incoming requests when enabled:

```python
# In main.py
from mcpgateway.middleware.validation_middleware import ValidationMiddleware

if settings.validation_middleware_enabled:
    app.add_middleware(ValidationMiddleware)
```

## Testing

### Unit Tests

```python
import pytest
from mcpgateway.common.validators import SecurityValidator

def test_path_traversal_blocked():
    """Test path traversal is blocked."""
    with pytest.raises(ValueError, match="Path traversal"):
        SecurityValidator.validate_path("../../../etc/passwd")

def test_command_injection_blocked():
    """Test command injection is blocked."""
    with pytest.raises(ValueError, match="shell metacharacters"):
        SecurityValidator.validate_shell_parameter("file; rm -rf /")

def test_output_sanitization():
    """Test output sanitization removes control chars."""
    result = SecurityValidator.sanitize_text("Hello\x1b[31mWorld\x00")
    assert result == "HelloWorld"
```

### Integration Tests

```bash
# Test path traversal protection
curl -X POST http://localhost:4444/resources \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"uri": "../../../etc/passwd"}'
# Expected: 400 Bad Request

# Test command injection protection
curl -X POST http://localhost:4444/tools/call \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "convert", "arguments": {"file": "test; cat /etc/passwd"}}'
# Expected: 422 Validation Failed (strict mode)
```

## Metrics & Monitoring

### Log Messages

```
[VALIDATION] Input validation failed: Parameter contains shell metacharacters
[VALIDATION] Path traversal detected: ../../../etc/passwd
[SECURITY] Path validation failed: Path outside allowed roots
```

### Prometheus Metrics

```
# Validation failures
mcpgateway_validation_failures_total{type="path_traversal"} 5
mcpgateway_validation_failures_total{type="command_injection"} 2
mcpgateway_validation_failures_total{type="sql_injection"} 1

# Sanitization operations
mcpgateway_sanitization_operations_total{type="output"} 1234
```

## Security Best Practices

### 1. Enable Endpoint and Protocol Validation in Production

Use endpoint-level Pydantic validation and protocol-specific validation in production.
Keep the deprecated global validation middleware disabled unless an existing deployment depends on it:

```bash
EXPERIMENTAL_VALIDATE_IO=true
VALIDATION_MIDDLEWARE_ENABLED=false
VALIDATION_STRICT=true
SANITIZE_OUTPUT=true
```

### 2. Configure Allowed Roots

Restrict resource access to specific directories:

```bash
ALLOWED_ROOTS='["/srv/data", "/var/app/uploads"]'
```

### 3. Use Strict Mode

Enable strict mode to reject invalid input:

```bash
VALIDATION_STRICT=true
```

### 4. Monitor Validation Failures

Set up alerts for validation failures:

```yaml
# Prometheus alert
- alert: HighValidationFailureRate
  expr: rate(mcpgateway_validation_failures_total[5m]) > 10
  annotations:
    summary: "High rate of validation failures detected"
```

### 5. Regular Security Audits

Review validation logs regularly:

```bash
# Check for validation failures
grep "VALIDATION" /var/log/mcpgateway.log | tail -100

# Check for security warnings
grep "SECURITY" /var/log/mcpgateway.log | tail -100
```

## Troubleshooting

### Issue: Legitimate Paths Blocked

**Symptom**: Valid file paths are rejected

**Solution**: Add path to `ALLOWED_ROOTS`:

```bash
ALLOWED_ROOTS='["/srv/data", "/var/app", "/opt/resources"]'
```

### Issue: MCP Server Tools Fail to Register with "unsafe characters" Error

**Symptom**:
```
All N tools failed validation. First error: Validation failed for tool 'X':
[{'type': 'value_error', 'loc': ('description',),
  'msg': "Value error, Description contains unsafe characters: '> '", ...}]
```

**Cause**: The tool's `description` field contains a shell metacharacter (`> `, `< `, `&&`, `||`, `$(`) that is blocked by default. This is common with Markdown-formatted descriptions. Note: single pipes (`|`), semicolons (`;`), and backticks (`` ` ``) are allowed as they commonly appear in natural-language documentation.

**Solution** (pick one):

```bash
# Option 1: Customize the pattern list to allow specific characters
TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", "||", "$("]

# Option 2: Disable pattern checks entirely
TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED=false

# Option 3: Warn-only mode (log but don't reject)
VALIDATION_STRICT=false
```

**Note**: `EXPERIMENTAL_VALIDATE_IO=false` and `JSON_SCHEMA_VALIDATION_STRICT=false` do **not** affect this check — only `VALIDATION_STRICT` and `TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED` do.

### Issue: Tool Parameters Escaped

**Symptom**: Tool receives escaped parameters

**Solution**: Disable strict mode for specific tools or use non-strict mode:

```bash
VALIDATION_STRICT=false  # Escape instead of reject
```

### Issue: Output Appears Corrupted

**Symptom**: Output missing formatting

**Solution**: Control characters were sanitized. This is expected behavior for security.

## Upstream Spec Proposal

### Validation Clause

> Servers MUST treat all inbound values as untrusted and validate them against JSON Schema or allow-lists.

### Path-Safety Clause

> Resource paths MUST resolve inside configured roots; otherwise reject with 400 status.

### Dangerous-Sink Clause

> Parameters passed to shells/SQL MUST be escaped or rejected to prevent injection attacks.

### Output-Sanitization Clause

> Before emission, servers SHOULD strip control chars and MUST ensure MIME correctness.

## References

- [OWASP Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)
- [OWASP Command Injection](https://owasp.org/www-community/attacks/Command_Injection)
- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection)
- [CWE-22: Path Traversal](https://cwe.mitre.org/data/definitions/22.html)
- [CWE-78: OS Command Injection](https://cwe.mitre.org/data/definitions/78.html)
- [CWE-89: SQL Injection](https://cwe.mitre.org/data/definitions/89.html)
