diff --git a/workspace-server/internal/handlers/mcp_test.go b/workspace-server/internal/handlers/mcp_test.go index dbad430a..ecb0c12a 100644 --- a/workspace-server/internal/handlers/mcp_test.go +++ b/workspace-server/internal/handlers/mcp_test.go @@ -9,6 +9,7 @@ import ( "net/http" "net/http/httptest" "os" + "strings" "testing" "errors" @@ -536,7 +537,12 @@ func TestMCPHandler_CommitMemory_CleanContent_PassesThrough(t *testing.T) { // tools/call — recall_memory // ───────────────────────────────────────────────────────────────────────────── -func TestMCPHandler_RecallMemory_GlobalScope_Blocked(t *testing.T) { +// TestMCPHandler_RecallMemory_GlobalScope_ScrubsInternalError verifies C3 is +// enforced and the OFFSEC-001 scrub contract is applied. The tool returns a +// descriptive error mentioning GLOBAL scope; dispatchRPC must replace it with +// a constant "tool call failed" message so internal implementation details +// never reach the caller. +func TestMCPHandler_RecallMemory_GlobalScope_ScrubsInternalError(t *testing.T) { h, mock := newMCPHandler(t) // No DB expectations — handler must abort before touching the DB. @@ -556,7 +562,20 @@ func TestMCPHandler_RecallMemory_GlobalScope_Blocked(t *testing.T) { var resp mcpResponse json.Unmarshal(w.Body.Bytes(), &resp) if resp.Error == nil { - t.Error("expected JSON-RPC error for GLOBAL scope recall, got nil") + t.Fatal("expected JSON-RPC error for GLOBAL scope recall, got nil") + } + // OFFSEC-001 scrub contract: error code is -32000 (server error). + if resp.Error.Code != -32000 { + t.Errorf("expected error code -32000, got %d", resp.Error.Code) + } + // Message must be the constant scrubbed string — no "GLOBAL" or bridge + // implementation details leaked to the caller. + if resp.Error.Message != "tool call failed" { + t.Errorf("error message should be constant 'tool call failed', got: %q", resp.Error.Message) + } + // Defence-in-depth: the original error body must not appear in the response. + if strings.Contains(resp.Error.Message, "GLOBAL") || strings.Contains(resp.Error.Message, "bridge") { + t.Error("scrubbed error message must not contain original error keywords") } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("unexpected DB calls on GLOBAL scope block: %v", err)