GitHub Advisoryadvisory · Go
GHSA-78h3-63c4-5fqc WeKnora has Command Injection in MCP stdio test
CRITICAL
Official page ↗
- Aliases
- CVE-2026-22688
- Ecosystem
- Go
- Packages
- github.com/Tencent/WeKnora
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Weaknesses
- CWE-77
- Reviewed
- 2026-01-09
Affected packages
• Go github.com/Tencent/WeKnora: < 0.2.5 → fixed in 0.2.5
Description
Vulnerability Description
---
Vulnerability Overview
This issue is a command injection vulnerability (CWE-78) that allows authenticated users to inject stdio_config.command/args into MCP stdio settings, causing the server to execute subprocesses using these injected values.
The root causes are as follows:
• Missing Security Filtering: When transport_type=stdio, there is no validation on stdio_config.command/args, such as allowlisting, enforcing fixed paths/binaries, or blocking dangerous options.
• Functional Flaw (Trust Boundary Violation): The command/args stored as "service configuration data" are directly used in the /test execution flow and connected to execution sinks without validation.
• Lack of Authorization Control: This functionality effectively allows "process execution on the server" (an administrative operation), yet no administrator-only permission checks are implemented in the code (accessible with Bearer authentication only).
Vulnerable Code
1. API Route Registration (path where endpoints are created)
****https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/router/router.go#L85-L110
https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/router/router.go#L371-L390
```go
// 认证中间件
r.Use(middleware.Auth(params.TenantService, params.UserService, params.Config))
// 添加OpenTelemetry追踪中间件
r.Use(middleware.TracingMiddleware())
// 需要认证的API路由
v1 := r.Group("/api/v1")
{
RegisterAuthRoutes(v1, params.AuthHandler)
RegisterTenantRoutes(v1, params.TenantHandler)
RegisterKnowledgeBaseRoutes(v1, params.KBHandler)
RegisterKnowledgeTagRoutes(v1, params.TagHandler)
RegisterKnowledgeRoutes(v1, params.KnowledgeHandler)
RegisterFAQRoutes(v1, params.FAQHandler)
RegisterChunkRoutes(v1, params.ChunkHandler)
RegisterSessionRoutes(v1, params.SessionHandler)
RegisterChatRoutes(v1, params.SessionHandler)
RegisterMessageRoutes(v1, params.MessageHandler)
RegisterModelRoutes(v1, params.ModelHandler)
RegisterEvaluationRoutes(v1, params.EvaluationHandler)
RegisterInitializationRoutes(v1, params.InitializationHandler)
RegisterSystemRoutes(v1, params.SystemHandler)
RegisterMCPServiceRoutes(v1, params.MCPServiceHandler)
RegisterWebSearchRoutes(v1, params.WebSearchHandler)
}
```
```go
func RegisterMCPServiceRoutes(r *gin.RouterGroup, handler *handler.MCPServiceHandler) {
mcpServices := r.Group("/mcp-services")
{
// Create MCP service
mcpServices.POST("", handler.CreateMCPService)
// List MCP services
mcpServices.GET("", handler.ListMCPServices)
// Get MCP service by ID
mcpServices.GET("/:id", handler.GetMCPService)
// Update MCP service
mcpServices.PUT("/:id", handler.UpdateMCPService)
// Delete MCP service
mcpServices.DELETE("/:id", handler.DeleteMCPService)
// Test MCP service connection
mcpServices.POST("/:id/test", handler.TestMCPService)
// Get MCP service tools
mcpServices.GET("/:id/tools", handler.GetMCPServiceTools)
// Get MCP service resources
mcpServices.GET("/:id/resources", handler.GetMCPServiceResources)
}
```
2. User input (JSON) → types.MCPService binding (POST /api/v1/mcp-services)
**https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/handler/mcp_service.go#L40-L55
```go
var service types.MCPService
if err := c.ShouldBindJSON(&service); err != nil {
logger.Error(ctx, "Failed to parse MCP service request", err)
c.Error(errors.NewBadRequestError(err.Error()))
return
}
tenantID := c.GetUint64(types.TenantIDContextKey.String())
if tenantID == 0 {
logger.Error(ctx, "Tenant ID is empty")
c.Error(errors.NewBadRequestError("Tenant ID cannot be empty"))
return
}
service.TenantID = tenantID
if err := h.mcpServiceService.CreateMCPService(ctx, &service); err != nil {
```
3. Taint propagation (storage): The bound service object is stored directly in the database without sanitization.
**https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/application/repository/mcp_service.go#L23-L25
```go
func (r *mcpServiceRepository) Create(ctx context.Context, service *types.MCPService) error {
return r.db.WithContext(ctx).Create(service).Error
}
```
4. Sink execution: /test endpoint loads the service from the database → executes TestMCPService
https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/handler/mcp_service.go#L323-L325
https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/application/service/mcp_service.go#L238-L264
```go
logger.Infof(ctx, "Testing MCP service: %s", secutils.SanitizeForLog(serviceID))
result, err := h.mcpServiceService.TestMCPService(ctx, tenantID, serviceID)
```
```go
service, err := s.mcpServiceRepo.GetByID(ctx, tenantID, id)
if err != nil {
return nil, fmt.Errorf("failed to get MCP service: %w", err)
}
if service == nil {
return nil, fmt.Errorf("MCP service not found")
}
// Create temporary client for testing
config := &mcp.ClientConfig{
Service: service,
}
client, err := mcp.NewMCPClient(config)
if err != nil {
return &types.MCPTestResult{
Success: false,
Message: fmt.Sprintf("Failed to create client: %v", err),
}, nil
}
// Connect
testCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := client.Connect(testCtx); err != nil {
return &types.MCPTestResult{
```
5. Ultimate sink (subprocess execution): The command/args values from stdio configuration are directly used in the subprocess execution path.
**https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/mcp/client.go#L120-L137
https://github.com/Tencent/WeKnora/blob/6b7558c5592828380939af18240a4cef67a2cbfc/internal/mcp/client.go#L158-L160
```go
case types.MCPTransportStdio:
if config.Service.StdioConfig == nil {
return nil, fmt.Errorf("stdio_config is required for stdio transport")
}
// Convert env vars map to []string format (KEY=value)
envVars := make([]string, 0, len(config.Service.EnvVars))
for key, value := range config.Service.EnvVars {
envVars = append(envVars, fmt.Sprintf("%s=%s", key, value))
}
// Create stdio client with options
// NewStdioMCPClientWithOptions(command string, env []string, args []string, opts ...transport.StdioOption)
mcpClient, err = client.NewStdioMCPClientWithOptions(
config.Service.StdioConfig.Command,
envVars,
config.Service.StdioConfig.Args,
)
```
```go
if err := c.client.Start(ctx); err != nil {
return fmt.Errorf("failed to start client: %w", err)
}
```
PoC
---
PoC Description
• Obtain an authentication token.
• Create an MCP service with transport_type=stdio, injecting the command to execute into stdio_config.command/args.
• Call the /test endpoint to trigger the Connect() → Start() execution flow, confirming command execution on the server via side effects (e.g., file creation).
PoC
• Container state verification (pre-exploitation)
```bash
docker exec -it WeKnora-app /bin/bash
cd /tmp/; ls -l
```
• Authenticate via /api/v1/auth/login to obtain a Bearer token for API calls.**
…
Related
Alias · CVE (1)
- CVE-2026-22688 WeKnora is an LLM-powered framework designed for deep document understanding and semantic retrieval. vulnerability
Weakness · CWE (1)
- CWE-77 Improper Neutralization of Special Elements used in a Command weakness
References
- https://github.com/Tencent/WeKnora/security/advisories/GHSA-78h3-63c4-5fqc web
- https://nvd.nist.gov/vuln/detail/CVE-2026-22688 advisory
- https://github.com/Tencent/WeKnora/commit/f7900a5e9a18c99d25cec9589ead9e4e59ce04bb web
- https://github.com/Tencent/WeKnora package
- https://pkg.go.dev/vuln/GO-2026-4292 web