BACK TO SEARCH
SUSE/suse-ai-upcritical

A comprehensive platform for managing and proxying Model Context Protocol (MCP) servers, providing scalable AI service orchestration across multiple microservices.

SUSE AI Universal Proxy is a comprehensive MCP proxy system that aggregates, manages, and orchestrates multiple MCP servers. It provides a unified end...

purpose: SUSE AI Universal Proxy is a comprehensive MCP prothreat: network exposed
Go · 28 · May 21, 2026 · May 21, 2026 · GITHUB ↗
RISK SCORE
0/ 100 risk
low findings+5
high findings+75
medium findings+60
capped at100
VULNERABILITY ANALYSIS · 8 findings in 8 blocks3 HIGH · 4 MEDIUM
HIGH1 finding
pkg/proxy/src/main.py:54
54@app.tool()
55def get_server_info() -> dict:
56    """Get server information"""
57    auth_required = transport == "http"
58    return {
59        "name": server_name,
60        "version": "1.0.0",
61        "description": "Example MCP server",
62        "auth_required": auth_required,
63        "auth_method": "Bearer token" if auth_required else None,
64        "token_format": "Authorization: Bearer <token>" if auth_required else None,
65        "expected_token": AUTH_TOKEN if auth_required else None,
66        "supported_protocols": ["2024-11-05"]
67    }

// Network-exposed MCP server; tool is callable by any authenticated or unauthenticated client.

EXPLAINThe get_server_info tool returns the expected authentication token in the 'expected_token' field. This is not the intended purpose of a server info endpoint and exposes credentials.
IMPACTAny user or LLM can retrieve the authentication token, bypassing intended access controls.
FIXRemove the 'expected_token' field from the response. Server info should not include secrets.
HIGH1 finding
pkg/proxy/src/main.py:11
11AUTH_TOKEN = os.getenv("MCP_AUTH_TOKEN", "mcp-example-token-12345")

// Network-exposed MCP server; token is retrievable via get_server_info tool.

EXPLAINThe default authentication token is hardcoded and also exposed via the get_server_info tool (line 65: 'expected_token': AUTH_TOKEN). This allows any client to retrieve the token and authenticate.
IMPACTAn attacker can obtain the authentication token and gain unauthorized access to the MCP server.
FIXRemove the default token and do not expose the token via get_server_info. Use a randomly generated token set via environment variable only.
HIGH1 finding
pkg/proxy/src/main.py:40
40app = FastMCP(server_name, auth=token_verifier) if token_verifier else FastMCP(server_name)

// Network-exposed MCP server; no authentication enforced.

EXPLAINThe FastMCP server is created with auth=token_verifier only when transport is 'http'. However, the token_verifier is set only if transport == 'http' (line 14). For stdio transport, no auth is applied. But even for HTTP, the auth is optional and the token is hardcoded and exposed. Additionally, the server runs on 0.0.0.0:8000 in examples/registry/server.py without any authentication.
IMPACTUnauthenticated access to MCP tools, including the ability to call add, multiply, and get_server_info.
FIXEnforce authentication for all transports. Remove hardcoded tokens and use secure token generation.
MEDIUM1 finding
internal/handlers/mcp_auth_handler.go:37
37func (h *MCPAuthHandler) GetClientToken(c *gin.Context) {
38	name := c.Param("name")
39
40	// Get adapter
41	adapter, err := h.store.Get(c.Request.Context(), name)
42	if err != nil || adapter == nil {
43		c.JSON(http.StatusNotFound, gin.H{"error": "Adapter not found"})
44		return
45	}
46
47	// Check if auth integration is available
48	if h.authIntegration == nil {
49		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Authentication integration not available"})
50		return
51	}
52
53	// Get client token
54	tokenResponse, err := h.authIntegration.GetClientToken(*adapter)
55	if err != nil {
56		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
57		return
58	}
59
60	c.JSON(http.StatusOK, tokenResponse)
61}
internal/handlers/mcp_auth_handler.go:37

// Network-exposed proxy; endpoint is accessible without authentication.

EXPLAINThe GetClientToken endpoint does not require authentication. Any user can retrieve client tokens for any adapter, including bearer tokens and API keys stored in the adapter configuration.
IMPACTAn attacker can retrieve authentication credentials for all adapters, gaining unauthorized access to backend MCP servers.
FIXRequire authentication on all admin/management endpoints. Use proper authorization checks.
MEDIUM1 finding
internal/service/mcp_auth_integration.go:29
29func (mais *MCPAuthIntegrationService) GetClientToken(adapter models.AdapterResource) (*ClientTokenResponse, error) {
30	log.Printf("MCPAuthIntegrationService: Getting client token for adapter %s", adapter.Name)
31
32	if adapter.Authentication == nil || !adapter.Authentication.Required {
33		return &ClientTokenResponse{
34			Token:     "",
35			Type:      "none",
36			ExpiresAt: time.Time{},
37			Message:   "No authentication required",
38		}, nil
39	}
40
41	switch adapter.Authentication.Type {
42	case "bearer":
43		return mais.getBearerToken(adapter)
44	case "oauth":
45		return mais.getOAuthToken(adapter)
46	case "basic":
47		return mais.getBasicAuth(adapter)
48	case "apikey":
49		return mais.getAPIKey(adapter)
50	default:
51		return nil, fmt.Errorf("unsupported authentication type: %s", adapter.Authentication.Type)
52	}
53}
internal/service/mcp_auth_integration.go:29

// Network-exposed proxy; endpoint may be accessible without authentication.

EXPLAINThe GetClientToken function returns the actual token (bearer, basic credentials, API key) in the response. This exposes sensitive credentials to anyone who can call this endpoint.
IMPACTExposure of authentication credentials for backend MCP servers, allowing unauthorized access.
FIXDo not return actual tokens in responses. Return a confirmation that token is configured, or use masked values.
MEDIUM1 finding
internal/handlers/unified_mcp.go:242
242func (h *UnifiedMCPHandler) resolveAdapterURL(adapter models.AdapterResource) string {
243	targetURL := adapter.URL
244	if targetURL == "" {
245		return ""
246	}
247
248	if strings.Contains(targetURL, "/api/v1/adapters/") {
249		parts := strings.SplitN(targetURL, "/api/v1/adapters/", 2)
250		if len(parts) == 2 {
251			newURL := "http://127.0.0.1:8911/api/v1/adapters/" + parts[1]
252			logging.ProxyLogger.Info("UnifiedMCP: Internal loopback redirected: %s -> %s", targetURL, newURL)
253			return newURL
254		}
255	}
256	return targetURL
257}
internal/handlers/unified_mcp.go:242

// Network-exposed proxy; adapter URLs may be user-controllable.

EXPLAINThe adapter URL is taken directly from the adapter resource without validation. An attacker who can register or modify an adapter can set the URL to an arbitrary internal or external endpoint, causing the proxy to make requests to that URL.
IMPACTSSRF allowing an attacker to probe internal networks, access cloud metadata, or interact with other services.
FIXValidate adapter URLs against a whitelist of allowed hosts and schemes. Restrict to HTTPS and known internal domains.
MEDIUM1 finding
pkg/mcp/tool_discovery.go:31
31func (s *MCPToolDiscoveryService) DiscoverTools(ctx context.Context, serverURL string, auth *models.AdapterAuthConfig) ([]models.MCPTool, error) {
32	log.Printf("MCPToolDiscovery: Discovering tools from server: %s", serverURL)
33
34	// Validate input parameters
35	if serverURL == "" {
36		return nil, fmt.Errorf("server URL cannot be empty")
37	}
38
39	// Create tools/list request
40	request := map[string]interface{}{
41		"jsonrpc": "2.0",
42		"id":      1,
43		"method":  "tools/list",
44		"params":  map[string]interface{}{},
45	}
46
47	requestBody, err := json.Marshal(request)
48	if err != nil {
49		return nil, fmt.Errorf("failed to marshal request: %w", err)
50	}
51
52	// Create HTTP request with timeout
53	req, err := http.NewRequestWithContext(ctx, "POST", serverURL, bytes.NewReader(requestBody))
pkg/mcp/tool_discovery.go:31

// Network-exposed proxy; serverURL comes from adapter configuration which may be user-controllable.

EXPLAINThe serverURL parameter is only checked for empty string, but not validated against allowed hosts or schemes. An attacker who can control the serverURL (e.g., via adapter configuration) could make the proxy send requests to arbitrary internal or external URLs.
IMPACTServer-Side Request Forgery (SSRF) allowing an attacker to probe internal networks, access cloud metadata endpoints, or interact with other internal services.
FIXValidate serverURL against a whitelist of allowed hosts and schemes. Restrict to HTTPS and known internal domains.
LOW1 finding
pkg/proxy/src/main.py:38
38os.environ['FASTMCP_CSRF_PROTECTION'] = 'false'

// Network-exposed MCP server; relevant if accessed from web browsers.

EXPLAINCSRF protection is explicitly disabled. This makes the HTTP endpoint vulnerable to cross-site request forgery attacks if accessed from a browser context.
IMPACTAn attacker could trick a user's browser into making unauthorized MCP requests.
FIXEnable CSRF protection or ensure the endpoint is not accessible from browsers (e.g., use proper CORS headers).
5/21/2026
Findings are produced by automated LLM analysis and may include false positives or miss issues. Verify independently before acting.