12 KiB
MCP Server Security Audit Report
Audit Date: 2026-02-08 Auditor: Claude Security Engineer Scope: /mnt/ollama/git/agent-guardrails-template/mcp-server/ Language: Go
Executive Summary
This security audit analyzed the MCP server codebase for common security vulnerabilities. Overall, the codebase demonstrates strong security practices with proper parameterized queries, input validation, and security headers. No Critical vulnerabilities were identified. Several medium and low-priority issues were found that should be addressed to improve the security posture.
Risk Summary
| Severity | Count | Status |
|---|---|---|
| Critical | 0 | - |
| High | 0 | - |
| Medium | 4 | Needs attention |
| Low | 5 | Recommended fixes |
| Info | 2 | Best practices |
Detailed Findings
MEDIUM SEVERITY
M-001: Insecure Hash Function for API Key Logging
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go
- Line: 127-132
- Severity: Medium
Description:
The hashAPIKey function uses SHA-256 truncated to 8 characters for logging API key hashes. While SHA-256 is cryptographically strong, truncating to 8 hex characters (32 bits) provides insufficient collision resistance for an attacker attempting to identify valid API keys from logs.
Code:
func hashAPIKey(key string) string {
var h [32]byte
h = sha256.Sum256([]byte(key))
return hex.EncodeToString(h[:8]) // Only 32 bits of entropy
}
Impact: An attacker with log access could potentially brute-force the truncated hash to identify which API keys are in use.
Recommended Fix: Increase the truncation length to at least 16 bytes (128 bits) or use a purpose-built key identifier:
func hashAPIKey(key string) string {
var h [32]byte
h = sha256.Sum256([]byte(key))
return hex.EncodeToString(h[:16]) // 64 bits of entropy
}
M-002: Information Disclosure in Error Messages
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go
- Lines: 72, 239, 402, 422, 445, 507, 527
- Severity: Medium
Description: Multiple handlers return raw error messages from the database layer directly to API consumers, potentially exposing internal implementation details.
Code Examples:
// Line 72
return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()})
// Line 239
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
// Line 402
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
Impact: Attackers could use internal error messages to understand database schema, file paths, or system architecture.
Recommended Fix: Log detailed errors internally but return generic messages to clients:
slog.Error("Database error", "error", err, "handler", "createProject")
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": "An internal error occurred",
})
M-003: Missing Rate Limiting on SSE Endpoint
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/mcp/server.go
- Line: 336-346
- Severity: Medium
Description:
The SSE endpoint at /mcp/v1/sse does not implement rate limiting, allowing an attacker to create unlimited sessions and potentially exhaust server resources.
Code:
// SSE endpoint - no rate limiting applied
s.echo.GET("/mcp/v1/sse", s.handleSSE)
Impact: Resource exhaustion through uncontrolled session creation, potentially leading to denial of service.
Recommended Fix: Add rate limiting middleware specifically for the SSE endpoint:
// Add rate limiter for SSE connections
sseLimiter := tollbooth.NewLimiter(10, nil) // 10 connections per minute
s.echo.GET("/mcp/v1/sse", tollbooth.LimitFuncHandler(sseLimiter, s.handleSSE))
M-004: Missing Input Validation on Project Creation
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go
- Line: 395-406
- Severity: Medium
Description:
The createProject handler binds request body directly to the model without calling the Validate() method, potentially allowing invalid data to reach the database.
Code:
func (s *Server) createProject(c echo.Context) error {
var proj models.Project
if err := c.Bind(&proj); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
}
// Missing: if err := proj.Validate(); err != nil { ... }
if err := s.projStore.Create(c.Request().Context(), &proj); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusCreated, proj)
}
Impact: Invalid project data (empty names, invalid slugs) could be stored in the database.
Recommended Fix: Add validation before database operations:
if err := proj.Validate(); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
LOW SEVERITY
L-001: Missing Request Size Limit on MCP Message Endpoint
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/mcp/server.go
- Line: 333
- Severity: Low
Description: While body limit is set to 1MB, there's no validation that specific message types don't exceed reasonable size limits for their content.
Recommended Fix: Add content-length validation for specific endpoints.
L-002: Potential Race Condition in Cache Invalidation
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/cache/redis.go
- Line: 162-183
- Severity: Low
Description:
The InvalidateOnRuleChange function uses a timeout context for pipeline operations but doesn't verify the pipeline execution result before proceeding to pattern-based deletion.
Recommended Fix: Check pipeline result for partial failures:
cmders, err := pipe.Exec(ctx)
if err != nil {
return fmt.Errorf("pipeline failed: %w", err)
}
for _, cmder := range cmders {
if cmder.Err() != nil {
slog.Warn("Cache deletion partial failure", "error", cmder.Err())
}
}
L-003: Weak CORS Configuration in Development Mode
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go
- Line: 88-102
- Severity: Low
Description:
In non-production mode, CORS allows any localhost origin (http://localhost:*), which could be exploited by malicious local applications.
Code:
if s.cfg.ProductionMode {
corsOrigins = []string{"http://localhost:8081", "https://localhost:8081"}
} else {
corsOrigins = []string{"http://localhost:*", "https://localhost:*"} // Too permissive
}
Recommended Fix: Require explicit origin configuration even in development mode.
L-004: Missing Context Cancellation Check in Session Cleanup
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/mcp/server.go
- Line: 835-848
- Severity: Low
Description: The session cleanup goroutine doesn't check for context cancellation during the ticker loop, potentially delaying shutdown.
Recommended Fix:
func (s *MCPServer) sessionCleanup() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// ... cleanup logic
case <-s.ctx.Done(): // Add context cancellation
return
}
}
}
L-005: Missing Secure Flag in Security Headers
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go
- Line: 246-270
- Severity: Low
Description:
The security headers middleware doesn't set the Strict-Transport-Security (HSTS) header, which is recommended for HTTPS deployments.
Recommended Fix: Add HSTS header when TLS is enabled:
if s.cfg.TLSEnabled {
c.Response().Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
}
INFORMATIONAL
I-001: Comprehensive ReDoS Protection
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/validation/safe_regex.go
- Severity: Info
Description: The codebase implements excellent protection against Regular Expression Denial of Service (ReDoS) attacks using timeout-based regex execution with panic recovery.
Code:
func SafeRegex(pattern string, input string, timeout time.Duration) (bool, error) {
// Uses goroutine with timeout to prevent catastrophic backtracking
select {
case result := <-resultChan:
return result, nil
case <-time.After(timeout):
return false, fmt.Errorf("regex timeout after %v - possible ReDoS attack", timeout)
}
}
Status: Good security practice implemented.
I-002: Proper Parameterized Queries
- File: /mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/*.go
- Severity: Info
Description:
All database queries use parameterized statements with $N placeholders, effectively preventing SQL injection attacks.
Example:
rows, err := s.db.QueryContext(ctx, `
SELECT id, name, slug FROM projects WHERE slug = $1
`, slug)
Status: Good security practice implemented.
Positive Security Findings
- No SQL Injection: All database queries use proper parameterization
- No Hardcoded Secrets: All credentials loaded from environment variables
- Secure Session Generation: Uses
crypto/randfor session IDs (mcp/server.go:822-831) - Constant-Time Comparison: API keys compared using
subtle.ConstantTimeCompare(middleware.go:47-50) - ReDoS Protection: Regex validation with timeout protection (safe_regex.go)
- Security Headers: Comprehensive CSP and security headers implemented
- Input Validation: UUID parsing and slug validation present
- Audit Logging: Comprehensive audit trail for security events
- Rate Limiting: Distributed rate limiting implemented
- Secrets Scanning: Content scanned for secrets before storage
Recommendations Summary
Immediate Actions (Medium Priority)
- Fix API key hash truncation (M-001)
- Sanitize error messages returned to clients (M-002)
- Add rate limiting to SSE endpoint (M-003)
- Add input validation to project creation (M-004)
Short-term Actions (Low Priority)
- Add HSTS header for TLS deployments
- Improve CORS configuration in development
- Add context cancellation to background goroutines
- Enhance pipeline error handling in cache operations
Security Best Practices Already Implemented
- Parameterized SQL queries (no SQL injection risk)
- Environment-based secret management
- Cryptographically secure random generation
- Constant-time credential comparison
- ReDoS protection
- Comprehensive security headers
Appendix: Files Audited
Core Files
/mnt/ollama/git/agent-guardrails-template/mcp-server/cmd/server/main.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/handlers.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/middleware.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/server.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/web/errors.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/mcp/server.go
Database Layer
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/postgres.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/projects.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/documents.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/rules.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/database/failures.go
Supporting Components
/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/security/secrets_scanner.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/config/config.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/cache/redis.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/validation/safe_regex.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/circuitbreaker/breaker.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/models/project.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/models/rule.go/mnt/ollama/git/agent-guardrails-template/mcp-server/internal/audit/logger.go
Report Generated: 2026-02-08 Next Review Recommended: After addressing Medium priority findings