claw-code/.guardrails/docs/security/SECURITY_AUDIT_API.md

18 KiB

API Security Audit Report

Repository: guardrail-mcp Component: mcp-server/internal/web/ Audit Date: 2026-02-08 Auditor: Security Engineer

Executive Summary

This audit covers the web layer of the guardrail-mcp server, focusing on authentication, authorization, rate limiting, CORS, input validation, and security headers. 3 Critical, 5 High, 4 Medium, and 3 Low severity issues were identified.

Risk Overview

Severity Count Immediate Action Required
Critical 3 Yes
High 5 Yes
Medium 4 Recommended
Low 3 Scheduled

Critical Issues

1. AUTH-BYPASS-001: Path Traversal in Authentication Bypass

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go:21-28

Issue: The authentication middleware uses c.Path() to check if routes should be skipped, but c.Path() returns the route pattern (e.g., /health/live), not the actual request path. A request to /health/live/../../../api/sensitive would match the /health/live pattern, bypassing authentication.

// VULNERABLE CODE
path := c.Path()  // Returns route pattern, not actual URL
if path == "/health/live" || path == "/health/ready" || path == "/metrics" {
    return next(c)  // Bypasses auth incorrectly
}

Severity: Critical CVSS: 9.1 (Critical) Impact: Complete authentication bypass, unauthorized API access Remediation:

path := c.Request().URL.Path  // Use actual request path
// Normalize path to prevent traversal
path = filepath.Clean(path)

// Use exact matching with proper path handling
if path == "/health/live" || path == "/health/ready" || path == "/metrics" {
    return next(c)
}

2. RATE-001: Rate Limiting Bypass via IP Spoofing

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go:105-108

Issue: When API key hash is not available, the middleware falls back to c.RealIP() for rate limiting. Echo's RealIP() extracts IP from X-Forwarded-For/X-Real-IP headers without validation, allowing attackers to spoof different IPs and bypass rate limits.

// VULNERABLE CODE
keyHash, ok := c.Get("api_key_hash").(string)
if !ok {
    keyHash = c.RealIP()  // Easily spoofed via headers
}

Severity: Critical CVSS: 8.2 (High) Impact: Rate limit bypass, potential DoS Remediation:

// Extract IP from trusted source only
func getClientIP(c echo.Context, trustProxy bool) string {
    if !trustProxy {
        // Use direct connection IP
        ip := c.Request().RemoteAddr
        host, _, err := net.SplitHostPort(ip)
        if err == nil {
            return host
        }
        return ip
    }
    // Only trust proxy headers if behind verified load balancer
    return c.RealIP()
}

3. CORS-001: Overly Permissive CORS in Non-Production

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go:87-102

Issue: In non-production mode, CORS allows http://localhost:* and https://localhost:*, which permits any localhost port. This allows malicious websites running on localhost to make authenticated cross-origin requests.

// VULNERABLE CODE - allows any localhost port
corsOrigins = []string{"http://localhost:*", "https://localhost:*"}

Severity: Critical CVSS: 7.5 (High) Impact: CSRF-style attacks from malicious localhost services Remediation:

// Use specific allowed origins only
corsOrigins = s.cfg.CORSAllowedOrigins
// Validate origins against allowlist
allowedOrigins := map[string]bool{
    "http://localhost:3000": true,
    "http://localhost:8081": true,
    // Add specific allowed origins
}

High Severity Issues

4. INPUT-001: Insufficient Input Validation on PATCH Endpoints

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go:283-343

Issue: The patchRule handler accepts arbitrary fields in the request body without strict validation. While the struct defines expected fields, Echo's Bind() method will ignore unknown fields, potentially allowing injection of unexpected data.

Severity: High Impact: Mass assignment vulnerability, data corruption Remediation:

// Use strict validation
var req struct {
    Enabled  *bool   `json:"enabled,omitempty" validate:"omitempty"`
    Name     *string `json:"name,omitempty" validate:"omitempty,max=255"`
    Message  *string `json:"message,omitempty" validate:"omitempty,max=1000"`
    Pattern  *string `json:"pattern,omitempty" validate:"omitempty,max=500"`
    Severity *string `json:"severity,omitempty" validate:"omitempty,oneof=low medium high critical"`
}

// Add strict decoding to reject unknown fields
decoder := json.NewDecoder(c.Request().Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(&req); err != nil {
    return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid fields in request"})
}

5. INPUT-002: Missing Content-Type Validation

Files: All handlers in /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go

Issue: No Content-Type validation on requests. Endpoints accepting JSON don't verify Content-Type: application/json, allowing CSRF attacks through HTML forms or content type confusion attacks.

Severity: High Impact: CSRF attacks, content type confusion Remediation:

// Add middleware for content type validation
func RequireContentType(contentTypes ...string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if c.Request().Method == http.MethodGet || c.Request().Method == http.MethodDelete {
                return next(c)
            }
            contentType := c.Request().Header.Get("Content-Type")
            for _, allowed := range contentTypes {
                if strings.HasPrefix(contentType, allowed) {
                    return next(c)
                }
            }
            return echo.NewHTTPError(http.StatusUnsupportedMediaType, "invalid content type")
        }
    }
}

6. AUTH-002: Weak IDE Endpoint Authorization Logic

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go:59-62

Issue: The endpoint restriction logic has a logical error. It checks if path starts with /ide and requires ide key type, but mcp key type is also allowed, making the restriction ineffective.

// INEFFECTIVE CODE
if strings.HasPrefix(path, "/ide") && keyType != "ide" && keyType != "mcp" {
    return echo.NewHTTPError(http.StatusForbidden, "IDE API key required")
}

Severity: High Impact: Authorization bypass, privilege escalation Remediation:

// Proper endpoint authorization
switch {
case strings.HasPrefix(path, "/ide"):
    if keyType != "ide" {
        return echo.NewHTTPError(http.StatusForbidden, "IDE API key required")
    }
case strings.HasPrefix(path, "/api/admin"):
    if keyType != "admin" {  // Add admin key type
        return echo.NewHTTPError(http.StatusForbidden, "admin key required")
    }
}

7. SEC-001: Missing HSTS Header

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go:246-270

Issue: The security headers middleware doesn't include Strict-Transport-Security (HSTS), allowing downgrade attacks when TLS is enabled.

Severity: High Impact: SSL/TLS downgrade attacks, MITM Remediation:

func securityHeadersMiddleware() echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            // Add HSTS when TLS is enabled
            if c.Request().TLS != nil {
                c.Response().Header().Set(
                    "Strict-Transport-Security",
                    "max-age=31536000; includeSubDomains; preload",
                )
            }
            // ... existing headers ...
            return next(c)
        }
    }
}

8. ERROR-001: Verbose Error Messages in Production

Files: Multiple handlers in /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go

Issue: Error messages expose internal details (e.g., database errors, file paths) in production. Example: return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})

Severity: High Impact: Information disclosure, system fingerprinting Remediation:

func handleError(c echo.Context, err error, publicMsg string, isProduction bool) error {
    slog.Error("Request failed", "error", err, "path", c.Path())
    if isProduction {
        return c.JSON(http.StatusInternalServerError, map[string]string{
            "error": publicMsg,
            "request_id": c.Response().Header().Get("X-Request-ID"),
        })
    }
    return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}

Medium Severity Issues

9. INPUT-003: Insufficient Query Parameter Validation

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go:116-140

Issue: Search query parameter q is accepted without length limits or sanitization, potentially enabling ReDoS (Regular Expression Denial of Service) if regex is used in search.

Severity: Medium Impact: ReDoS, resource exhaustion Remediation:

const maxQueryLength = 200

func (s *Server) searchDocuments(c echo.Context) error {
    query := c.QueryParam("q")
    if query == "" {
        return c.JSON(http.StatusBadRequest, map[string]string{"error": "query required"})
    }
    if len(query) > maxQueryLength {
        return c.JSON(http.StatusBadRequest, map[string]string{
            "error": "query too long",
            "max_length": strconv.Itoa(maxQueryLength),
        })
    }
    // Sanitize query to prevent regex injection
    query = sanitizeSearchQuery(query)
    // ... rest of handler
}

10. RATE-002: No Burst Protection for Rate Limiting

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/cache/redis.go:282-298

Issue: The rate limiter uses a simple counter without burst bucket or proper sliding window implementation. It resets at window boundaries, allowing burst attacks at minute boundaries.

Severity: Medium Impact: Burst attacks at window boundaries Remediation:

// Implement token bucket or proper sliding window
func (dl *DistributedRateLimiter) Allow(ctx context.Context, key string, limit int, burst int) bool {
    now := time.Now().UnixMilli()
    bucketKey := fmt.Sprintf("ratelimit:bucket:%s", key)

    lua := `
        local key = KEYS[1]
        local now = tonumber(ARGV[1])
        local limit = tonumber(ARGV[2])
        local burst = tonumber(ARGV[3])
        local window = tonumber(ARGV[4])

        local bucket = redis.call('hmget', key, 'tokens', 'last_update')
        local tokens = tonumber(bucket[1]) or burst
        local last_update = tonumber(bucket[2]) or now

        local delta = math.max(0, now - last_update)
        tokens = math.min(burst, tokens + (delta * limit / window))

        if tokens < 1 then
            return 0
        end

        tokens = tokens - 1
        redis.call('hset', key, 'tokens', tokens, 'last_update', now)
        redis.call('pexpire', key, window)
        return 1
    `

    result, err := dl.redis.Eval(ctx, lua, []string{bucketKey}, now, limit, burst, 60000).Int()
    if err != nil {
        slog.Error("Rate limiter error", "error", err)
        return false  // Fail closed
    }
    return result == 1
}

11. SEC-002: Version Endpoint Information Disclosure

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go:273-280

Issue: The /version endpoint exposes version information without authentication, enabling attackers to identify vulnerable versions.

Severity: Medium Impact: System fingerprinting, targeted attacks Remediation:

// Require authentication for version endpoint
// Or return minimal information
func (s *Server) versionInfo(c echo.Context) error {
    // Only return version to authenticated requests
    if c.Get("api_key_type") == nil {
        return echo.NewHTTPError(http.StatusUnauthorized, "authentication required")
    }
    // ... existing code ...
}

12. SEC-003: Missing Request Size Validation for Query Parameters

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go:109-110

Issue: While body size is limited to 10MB, there's no validation on URL length or query string size, potentially enabling HTTP request smuggling or buffer overflow attacks.

Severity: Medium Impact: HTTP request smuggling, DoS Remediation:

// Add URL length validation middleware
func URLLengthLimit(maxLength int) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            if len(c.Request().URL.String()) > maxLength {
                return echo.NewHTTPError(http.StatusRequestURITooLong, "URL too long")
            }
            return next(c)
        }
    }
}

// Use in setupMiddleware
s.echo.Use(URLLengthLimit(4096))

Low Severity Issues

13. SEC-004: X-XSS-Protection Deprecated Header

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go:264

Issue: The X-XSS-Protection header is deprecated and can introduce vulnerabilities in older browsers. CSP is the modern replacement.

Severity: Low Remediation:

// Remove X-XSS-Protection header
// CSP already provides better protection

14. LOG-001: Sensitive Information in Logs (API Key Hash)

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go:66-72

Issue: API key hashes are logged, which could enable offline cracking attempts if logs are compromised.

Severity: Low Impact: Information disclosure (requires log access) Remediation:

// Don't log key hashes, only use for rate limiting internally
// Or use HMAC with server secret for log identifiers

15. SEC-005: Race Condition in Cache Invalidation

File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go:334-336

Issue: The patchRule handler doesn't use the same error handling pattern as other handlers for cache invalidation, potentially masking failures.

Severity: Low Impact: Inconsistent error handling, stale cache Remediation: Standardize cache invalidation error handling across all handlers.


Security Strengths

The following security controls are well-implemented:

  1. Constant-Time API Key Comparison (middleware.go:47-50): Uses subtle.ConstantTimeCompare to prevent timing attacks
  2. Secret Scanning (handlers.go:90-96): Documents are scanned for secrets before saving
  3. CSP Implementation (server.go:251-261): Comprehensive Content Security Policy
  4. Panic Recovery (server.go:213-244): Proper panic recovery with stack trace logging
  5. Request Timeout (server.go:104-107): Configurable request timeouts
  6. Body Size Limit (server.go:110): 10MB body limit prevents large payload attacks
  7. Audit Logging (multiple files): Comprehensive audit logging for changes
  8. Health Check Security (server.go:296-313): Health endpoints don't expose component details

Remediation Priority

Immediate (Critical & High - Next 7 Days)

  1. Fix AUTH-BYPASS-001: Use actual request path instead of route pattern
  2. Fix RATE-001: Validate proxy headers before using RealIP()
  3. Fix CORS-001: Remove wildcard localhost origins
  4. Fix AUTH-002: Correct IDE endpoint authorization logic
  5. Fix INPUT-001: Add strict PATCH validation
  6. Fix SEC-001: Add HSTS header for TLS connections

Short-term (High & Medium - Next 30 Days)

  1. Fix INPUT-002: Add Content-Type validation middleware
  2. Fix ERROR-001: Implement production-safe error handling
  3. Fix INPUT-003: Add query parameter length limits
  4. Fix RATE-002: Implement proper token bucket rate limiting

Long-term (Medium & Low - Next 90 Days)

  1. Fix SEC-002: Protect version endpoint
  2. Fix SEC-003: Add URL length validation
  3. Fix SEC-004: Remove deprecated headers
  4. Fix LOG-001: Reduce sensitive information in logs
  5. Fix SEC-005: Standardize cache invalidation handling

Testing Recommendations

  1. Authentication Bypass Testing:

    curl "http://localhost:8080/health/live/../../../api/rules" -H "Authorization: Bearer invalid"
    
  2. Rate Limit Bypass Testing:

    for i in {1..1100}; do
      curl -H "X-Forwarded-For: 1.2.3.$i" http://localhost:8080/api/rules
    done
    
  3. CORS Testing:

    curl -H "Origin: http://localhost:9999" -H "Authorization: Bearer $KEY" \
         -X OPTIONS http://localhost:8080/api/rules -v
    
  4. Content-Type Testing:

    curl -X POST http://localhost:8080/api/rules \
         -H "Content-Type: text/plain" \
         -d "malicious data"
    

Compliance Mapping

Issue OWASP Top 10 2021 NIST 800-53 CIS Controls
AUTH-BYPASS-001 A01:2021-Broken Access Control AC-3 6.1
RATE-001 A07:2021-Identification AC-17 13.1
CORS-001 A07:2021-Identification AC-4 13.1
INPUT-001 A03:2021-Injection SI-10 3.5
SEC-001 A02:2021-Cryptographic Failures SC-8 14.4

Appendix: File Locations

File Lines Purpose
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go 1-133 Authentication & rate limiting
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go 1-712 API endpoint handlers
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go 1-321 Server setup & middleware
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/config/config.go 1-401 Configuration & validation
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/cache/redis.go 1-356 Rate limiting implementation