feat: finalize dc dual-channel security rollout

This commit is contained in:
kory
2026-10-02 13:23:25 +08:00
parent a5620cb9c4
commit 192969d7f6
11 changed files with 1964 additions and 85 deletions
+50 -2
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
@@ -24,6 +25,9 @@ func (m *mockBackend) ReadFile(ctx context.Context, clientIP, device, path strin
func (m *mockBackend) WriteFile(ctx context.Context, clientIP, device, path, content string) (any, error) {
return map[string]any{"device": device, "path": path, "bytes": len(content)}, nil
}
func (m *mockBackend) EditBlock(ctx context.Context, clientIP, device, path, oldText, newText string) (any, error) {
return map[string]any{"device": device, "path": path, "bytes_written": len(newText)}, nil
}
func TestMCPInitializeAndListTools(t *testing.T) {
srv := NewServer(&mockBackend{}, nil)
@@ -75,8 +79,8 @@ func TestMCPInitializeAndListTools(t *testing.T) {
t.Fatalf("expected map result")
}
tools, ok := resMap["tools"].([]any)
if !ok || len(tools) != 6 {
t.Fatalf("expected 6 tools, got %d", len(tools))
if !ok || len(tools) != 11 {
t.Fatalf("expected 11 tools, got %d", len(tools))
}
}
@@ -110,3 +114,47 @@ func TestMCPCallTool(t *testing.T) {
t.Fatalf("expected stdout in result, got %s", contentStr)
}
}
type gatedMockBackend struct {
mockBackend
deniedAction string
}
func (g *gatedMockBackend) AuthorizeMCP(device, action, clientIP string) error {
if action == g.deniedAction {
return fmt.Errorf("denied %s", action)
}
return nil
}
func (g *gatedMockBackend) ListAgentMCP(ctx context.Context, device string) (any, error) {
return map[string]any{"device": device}, nil
}
func (g *gatedMockBackend) CallAgentMCP(ctx context.Context, clientIP, device, server, tool string, args any) (any, error) {
return map[string]any{"device": device, "server": server, "tool": tool}, nil
}
func TestMCPAgentCallUsesSecurityGate(t *testing.T) {
backend := &gatedMockBackend{deniedAction: "mcp"}
srv := NewServer(backend, nil)
params := json.RawMessage("{\"name\":\"call_agent_mcp\",\"arguments\":{\"device\":\"node1\",\"server\":\"s\",\"tool\":\"t\"}}")
req := JSONRPCRequest{JSONRPC: "2.0", ID: 10, Method: "tools/call", Params: params}
resp := srv.HandleDirectRequest(context.Background(), req)
result, ok := resp.Result.(CallToolResult)
if !ok || !result.IsError || !strings.Contains(result.Content[0].Text, "denied mcp") {
t.Fatalf("expected MCP denial, got %#v", resp)
}
}
func TestMCPListAgentUsesReadGate(t *testing.T) {
backend := &gatedMockBackend{deniedAction: "read"}
srv := NewServer(backend, nil)
params := json.RawMessage("{\"name\":\"list_agent_mcp\",\"arguments\":{\"device\":\"node1\"}}")
req := JSONRPCRequest{JSONRPC: "2.0", ID: 11, Method: "tools/call", Params: params}
resp := srv.HandleDirectRequest(context.Background(), req)
result, ok := resp.Result.(CallToolResult)
if !ok || !result.IsError || !strings.Contains(result.Content[0].Text, "denied read") {
t.Fatalf("expected MCP list denial, got %#v", resp)
}
}