From fe3c9ee4fd7181848d03b2f08e6fdbd68e5a3fff Mon Sep 17 00:00:00 2001 From: Molecule AI Core-BE Date: Tue, 12 May 2026 07:34:23 +0000 Subject: [PATCH 01/10] test(handlers/mcp): correct RecallMemory_GlobalScope to expect descriptive error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligns with PR #669's fix to mcp.go: the descriptive GLOBAL scope error ("GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty") now propagates to the caller. The OFFSEC-001 scrub applies only to "unknown tool:" errors (to avoid leaking tool names); permission/usage errors are returned verbatim. Test name updated to reflect actual behavior. Branch: fix/681-recall-memory-offsec-scrub (PR #693) Co-Authored-By: Claude Opus 4.7 --- .../internal/handlers/mcp_test.go | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/workspace-server/internal/handlers/mcp_test.go b/workspace-server/internal/handlers/mcp_test.go index d306fa14..409c4d4e 100644 --- a/workspace-server/internal/handlers/mcp_test.go +++ b/workspace-server/internal/handlers/mcp_test.go @@ -548,7 +548,13 @@ func TestMCPHandler_CommitMemory_CleanContent_PassesThrough(t *testing.T) { // tools/call — recall_memory // ───────────────────────────────────────────────────────────────────────────── -func TestMCPHandler_RecallMemory_GlobalScope_Blocked(t *testing.T) { +// TestMCPHandler_RecallMemory_GlobalScope_ReturnsDescriptiveError verifies C3 +// (GLOBAL scope blocked on MCP bridge) is enforced and the error message +// propagates to the caller. Unlike "unknown tool:" errors (OFFSEC-001), which +// are scrubbed to a constant "tool call failed" to avoid leaking tool names, +// permission/usage errors like "GLOBAL scope is not permitted" are returned +// verbatim so callers (including tests) can assert on permission messages. +func TestMCPHandler_RecallMemory_GlobalScope_ReturnsDescriptiveError(t *testing.T) { h, mock := newMCPHandler(t) // No DB expectations — handler must abort before touching the DB. @@ -568,7 +574,17 @@ 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") + } + // Error code is -32000 (server error). + if resp.Error.Code != -32000 { + t.Errorf("expected error code -32000, got %d", resp.Error.Code) + } + // Descriptive error message propagates to the caller. The OFFSEC-001 scrub + // applies only to "unknown tool:" errors (to avoid leaking tool names). + want := "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty" + if resp.Error.Message != want { + t.Errorf("error message: got %q, want %q", resp.Error.Message, want) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("unexpected DB calls on GLOBAL scope block: %v", err) -- 2.45.2 From cc89f453727bfba90ded77a5987f40a79f369f40 Mon Sep 17 00:00:00 2001 From: core-devops Date: Tue, 12 May 2026 20:52:17 +0000 Subject: [PATCH 02/10] ci: rerun after mc#724 all-required fix lands -- 2.45.2 From 050d7ee14a7910e6922d3bd374b1d7284dc8dfd8 Mon Sep 17 00:00:00 2001 From: claude-ceo-assistant Date: Tue, 12 May 2026 21:15:55 +0000 Subject: [PATCH 03/10] ci: rerun after concurrency-block clear -- 2.45.2 From bd4ede1d0e4cede841e4772bfa6be683c6e63ff9 Mon Sep 17 00:00:00 2001 From: claude-ceo-assistant Date: Tue, 12 May 2026 21:24:45 +0000 Subject: [PATCH 04/10] ci: clean-slate rerun -- 2.45.2 From 5a474fa1d4c9029d136e1602e592498b4feaf87f Mon Sep 17 00:00:00 2001 From: claude-ceo-assistant Date: Tue, 12 May 2026 21:30:16 +0000 Subject: [PATCH 05/10] ci: post-restart rerun -- 2.45.2 From e07aa747d3410831dff69212b718f8455a8dc212 Mon Sep 17 00:00:00 2001 From: core-lead Date: Tue, 12 May 2026 21:35:01 +0000 Subject: [PATCH 06/10] ci: clean-slate rerun v2 -- 2.45.2 From 5c4b96aac897633e1628688cd546ad0ea9275ec8 Mon Sep 17 00:00:00 2001 From: core-lead Date: Tue, 12 May 2026 21:44:30 +0000 Subject: [PATCH 07/10] ci: global-zombie-purge rerun -- 2.45.2 From 74608da6081b9c538fab14aebf3575e405c4001f Mon Sep 17 00:00:00 2001 From: core-lead Date: Tue, 12 May 2026 21:48:22 +0000 Subject: [PATCH 08/10] ci: post-full-purge rerun -- 2.45.2 From afb328cf397f0e2d014a0519dca42883afc7c265 Mon Sep 17 00:00:00 2001 From: core-devops Date: Tue, 12 May 2026 22:07:20 +0000 Subject: [PATCH 09/10] ci: post-purge rerun -- 2.45.2 From 23e408379d0a5ec097945ff69bb700b4d8fe7d60 Mon Sep 17 00:00:00 2001 From: Molecule AI Core-Security Date: Tue, 12 May 2026 18:11:07 -0700 Subject: [PATCH 10/10] fix(test/mcp): align RecallMemory_GlobalScope with OFFSEC-001 scrub contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was asserting that the client-visible error.message equals the descriptive internal reason ("GLOBAL scope is not permitted via the MCP bridge"). After PR#680 and PR#772 enforced the OFFSEC-001 scrub contract across all tool-dispatch failure paths, mcp.go returns the constant "tool call failed" to callers — not the internal detail. Update the test to: - Rename to ..._Blocked_ScrubsInternalError (consistent with CommitMemory) - Assert error.message == "tool call failed" (OFFSEC-001 positive) - Add negative assertions (no internal tokens leak to client) - Use proper json.Unmarshal error check - Merge origin/main (PR#691 lint-required-context-exists-in-bp) Co-Authored-By: Claude Sonnet 4.6 --- .../internal/handlers/mcp_test.go | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/workspace-server/internal/handlers/mcp_test.go b/workspace-server/internal/handlers/mcp_test.go index b8487020..125eb725 100644 --- a/workspace-server/internal/handlers/mcp_test.go +++ b/workspace-server/internal/handlers/mcp_test.go @@ -608,13 +608,13 @@ func TestMCPHandler_CommitMemory_CleanContent_PassesThrough(t *testing.T) { // tools/call — recall_memory // ───────────────────────────────────────────────────────────────────────────── -// TestMCPHandler_RecallMemory_GlobalScope_ReturnsDescriptiveError verifies C3 -// (GLOBAL scope blocked on MCP bridge) is enforced and the error message -// propagates to the caller. Unlike "unknown tool:" errors (OFFSEC-001), which -// are scrubbed to a constant "tool call failed" to avoid leaking tool names, -// permission/usage errors like "GLOBAL scope is not permitted" are returned -// verbatim so callers (including tests) can assert on permission messages. -func TestMCPHandler_RecallMemory_GlobalScope_ReturnsDescriptiveError(t *testing.T) { +// TestMCPHandler_RecallMemory_GlobalScope_Blocked_ScrubsInternalError verifies +// C3 (GLOBAL scope blocked on MCP bridge) is enforced and that the OFFSEC-001 +// scrub contract applies: the client-visible error.message is the constant +// "tool call failed", NOT the descriptive internal reason. The internal reason +// ("GLOBAL scope is not permitted via the MCP bridge") is logged server-side +// but must never reach the wire. +func TestMCPHandler_RecallMemory_GlobalScope_Blocked_ScrubsInternalError(t *testing.T) { h, mock := newMCPHandler(t) // No DB expectations — handler must abort before touching the DB. @@ -632,20 +632,38 @@ func TestMCPHandler_RecallMemory_GlobalScope_ReturnsDescriptiveError(t *testing. }) var resp mcpResponse - json.Unmarshal(w.Body.Bytes(), &resp) + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatalf("response is not valid JSON: %v", err) + } + // (1) C3: an error must be reported. if resp.Error == nil { t.Fatal("expected JSON-RPC error for GLOBAL scope recall, got nil") } - // Error code is -32000 (server error). + // (2) OFFSEC-001 positive assertions — exact equality on the scrubbed + // constants so any change (re-leak of err.Error(), code mutation) trips + // the test. if resp.Error.Code != -32000 { - t.Errorf("expected error code -32000, got %d", resp.Error.Code) + t.Errorf("error code should be -32000 (Server error / dispatch-failure), got: %d", resp.Error.Code) } - // Descriptive error message propagates to the caller. The OFFSEC-001 scrub - // applies only to "unknown tool:" errors (to avoid leaking tool names). - want := "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty" - if resp.Error.Message != want { - t.Errorf("error message: got %q, want %q", resp.Error.Message, want) + if resp.Error.Message != "tool call failed" { + t.Errorf("error message should be the OFFSEC-001 constant %q, got: %q", "tool call failed", resp.Error.Message) } + // (3) OFFSEC-001 negative assertions — the internal reason must NOT appear + // in the client-visible message. + leakedTokens := []string{ + "GLOBAL", // scope name + "scope", // policy lexicon + "permitted", // policy verb + "bridge", // internal architecture term + "LOCAL", // alternative scope name + "TEAM", // alternative scope name + } + for _, tok := range leakedTokens { + if bytes.Contains([]byte(resp.Error.Message), []byte(tok)) { + t.Errorf("OFFSEC-001 scrub regression: client-visible error.message leaks internal token %q (got: %q)", tok, resp.Error.Message) + } + } + // (4) C3 invariant preserved: handler must short-circuit before any DB call. if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("unexpected DB calls on GLOBAL scope block: %v", err) } -- 2.45.2