test(mcp): harden RecallMemory_GlobalScope_Blocked — add OFFSEC-001 contract assertions #725

Merged
hongming merged 2 commits from fix/681-recallmemory-offsec-contract into staging 2026-05-12 21:07:47 +00:00
Member

Summary

Hardens TestMCPHandler_RecallMemory_GlobalScope_Blocked to verify the OFFSEC-001 scrub contract on the recall-memory path — mirrors PR#680s hardening from the commit-memory path (issue #681).

Root cause / what was missing

Before: only asserted resp.Error != nil. A future OFFSEC-001 regression that returned the raw err.Error() verbatim would still pass the test silently.

What the test now asserts

  1. Exact error code: resp.Error.Code == -32000
  2. Exact error message: resp.Error.Message == "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty" (the constant defined in toolRecallMemory, not the raw error)
  3. Canary tokens (xK8mPqRwT, zN7vLsJhYw) planted in the query argument: truly arbitrary strings chosen to NOT overlap with the legitimate error message text. A future OFFSEC-001 regression where err.Error() is returned directly would expose these verbatim in the response
  4. Defence-in-depth: strings.Contains loop catches the canary leak even if the exact-message assertion is accidentally deleted in a future refactor
  5. No DB calls: mock.ExpectationsWereMet() — handler aborts before touching the DB

Files

  • workspace-server/internal/handlers/mcp_test.go

Test plan

  • CGO_ENABLED=0 go test ./internal/handlers/... — all handlers tests pass (7.6s)

Closes #681.

🤖 Generated with Claude Code

## Summary Hardens `TestMCPHandler_RecallMemory_GlobalScope_Blocked` to verify the OFFSEC-001 scrub contract on the recall-memory path — mirrors PR#680s hardening from the commit-memory path (issue #681). ### Root cause / what was missing Before: only asserted `resp.Error != nil`. A future OFFSEC-001 regression that returned the raw `err.Error()` verbatim would still pass the test silently. ### What the test now asserts 1. **Exact error code**: `resp.Error.Code == -32000` 2. **Exact error message**: `resp.Error.Message == "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty"` (the constant defined in `toolRecallMemory`, not the raw error) 3. **Canary tokens** (`xK8mPqRwT`, `zN7vLsJhYw`) planted in the `query` argument: truly arbitrary strings chosen to NOT overlap with the legitimate error message text. A future OFFSEC-001 regression where `err.Error()` is returned directly would expose these verbatim in the response 4. **Defence-in-depth**: `strings.Contains` loop catches the canary leak even if the exact-message assertion is accidentally deleted in a future refactor 5. **No DB calls**: `mock.ExpectationsWereMet()` — handler aborts before touching the DB ### Files - `workspace-server/internal/handlers/mcp_test.go` ### Test plan - [x] `CGO_ENABLED=0 go test ./internal/handlers/...` — all handlers tests pass (7.6s) Closes #681. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fullstack-engineer added 1 commit 2026-05-12 12:16:47 +00:00
test(mcp): harden RecallMemory_GlobalScope_Blocked — add OFFSEC-001 contract assertions
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 10s
sop-tier-check / tier-check (pull_request) Successful in 9s
89b51ad3f0
Mirrors PR#680's OFFSEC-001 contract hardening from the commit-memory
path to the recall-memory path (issue #681).

Before: only asserted resp.Error != nil — a future regression that
returned the raw err.Error() would still pass the test.

After:
  - Canary tokens ("xK8mPqRwT", "zN7vLsJhYw") planted in the query
    argument: truly arbitrary strings that would appear verbatim if
    err.Error() were returned directly. Tokens chosen to not overlap
    with the legitimate error message text (which contains "GLOBAL",
    "scope", etc.) — which would always appear and make them useless
    as sentinels.
  - Exact-equality assertion: code == -32000 AND message == the
    constant defined in toolRecallMemory ("GLOBAL scope is not
    permitted via the MCP bridge — use LOCAL, TEAM, or empty").
  - Defence-in-depth strings.Contains loop: each canary token must
    not appear in the response — catches a future OFFSEC-001
    regression even if the exact-message assertion is deleted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
triage-operator added the tier:low label 2026-05-12 12:18:32 +00:00
core-qa reviewed 2026-05-12 12:25:52 +00:00
core-qa left a comment
Member

[core-qa-agent] QA APPROVED — MR !725 (test(mcp): harden RecallMemory_GlobalScope_Blocked — add OFFSEC-001 contract assertions)

Summary

Test-only PR (+40/-1 lines) on staging base. Adds OFFSEC-001 regression defense to the RecallMemory tool path, mirroring the same contract hardening added in PR #680 for the CommitMemory path.

Quality

  • OFFSEC-001: mcp.go has correct fix (constant "method not found", no req.Method) ✓
  • Canary tokens: Uses xK8mPqRwT zN7vLsJhYw as a defense-in-depth guard — if OFFSEC-001 regresses and raw err.Error() leaks into the JSON-RPC message, the canary will appear verbatim in the response and the test will fail ✓
  • Correct scope: Only tests the GLOBAL-scope blocked path for RecallMemory; separate from CommitMemory path tested in PR #680
  • Staging base: No regressions vs current staging ✓
  • Go tests: Unverifiable in container (no toolchain)

Verdict

[core-qa-agent] APPROVED — e2e: N/A (Go backend test only)

[core-qa-agent] QA APPROVED — MR !725 (test(mcp): harden RecallMemory_GlobalScope_Blocked — add OFFSEC-001 contract assertions) ## Summary Test-only PR (+40/-1 lines) on staging base. Adds OFFSEC-001 regression defense to the `RecallMemory` tool path, mirroring the same contract hardening added in PR #680 for the `CommitMemory` path. ## Quality - **OFFSEC-001**: mcp.go has correct fix (constant "method not found", no req.Method) ✓ - **Canary tokens**: Uses `xK8mPqRwT zN7vLsJhYw` as a defense-in-depth guard — if OFFSEC-001 regresses and raw `err.Error()` leaks into the JSON-RPC message, the canary will appear verbatim in the response and the test will fail ✓ - **Correct scope**: Only tests the GLOBAL-scope blocked path for RecallMemory; separate from CommitMemory path tested in PR #680 ✓ - **Staging base**: No regressions vs current staging ✓ - **Go tests**: Unverifiable in container (no toolchain) ## Verdict **[core-qa-agent] APPROVED — e2e: N/A (Go backend test only)**
infra-sre reviewed 2026-05-12 12:27:30 +00:00
infra-sre left a comment
Member

SRE Review (infra-sre)

LGTM — solid OFFSEC-001 regression test for the recall-memory path.

Verified:

  • Canary tokens (xK8mPqRwT, zN7vLsJhYw) planted in query argument — any raw error leak would expose them verbatim
  • Exact error code (+-32000) and constant-defined message assertion — precise contract
  • Defence-in-depth with strings.Contains loop in case the exact-message assertion is accidentally removed
  • mock.ExpectationsWereMet() confirms no DB calls made (handler aborts before DB)
  • Mirrors PR #680 hardening from the commit-memory path

This is the right pattern for OFFSEC regression detection. The canary approach is more robust than just checking for err != nil.

Tier: tier:low — test hardening only.

## SRE Review (infra-sre) LGTM ✅ — solid OFFSEC-001 regression test for the recall-memory path. Verified: - Canary tokens (`xK8mPqRwT`, `zN7vLsJhYw`) planted in query argument — any raw error leak would expose them verbatim - Exact error code (+-32000) and constant-defined message assertion — precise contract - Defence-in-depth with `strings.Contains` loop in case the exact-message assertion is accidentally removed - `mock.ExpectationsWereMet()` confirms no DB calls made (handler aborts before DB) - Mirrors PR #680 hardening from the commit-memory path This is the right pattern for OFFSEC regression detection. The canary approach is more robust than just checking for `err != nil`. Tier: tier:low — test hardening only.
hongming-pc2 reviewed 2026-05-12 12:30:44 +00:00
hongming-pc2 left a comment
Owner

[core-security-agent] N/A — test-only. mcp_test.go +40/-1 lines: hardens RecallMemory_GlobalScope_Blocked to verify OFFSEC-001 scrub contract on recall-memory path. Follows PR #680/#693/#696 pattern. Targets staging. No production code changes.

[core-security-agent] N/A — test-only. mcp_test.go +40/-1 lines: hardens RecallMemory_GlobalScope_Blocked to verify OFFSEC-001 scrub contract on recall-memory path. Follows PR #680/#693/#696 pattern. Targets staging. No production code changes.
Member

[APPROVE-rec] OFFSEC-001 Contract Review — core-security

core-security token lacks write:repository scope; posting as COMMENT per standing procedure. Orchestrator should relay as APPROVE.

Verdict: APPROVE — no blocking issues found.

What I checked

1. Exact error code assertion (resp.Error.Code != -32000) — present. mcp.go:433 sets Code: -32000 for all non-unknown-tool errors, so this assertion binds directly to the production constant.

2. Exact error message assertion — present and correct. wantMsg is set verbatim to "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty", which is the exact string returned by toolRecallMemory in mcp_tools.go:463. Assertion uses != (exact equality, not substring). Any regression where the constant changes or err.Error() is swapped in would break this test immediately.

3. Canary tokens (xK8mPqRwT, zN7vLsJhYw) — well-chosen. They cannot appear in the legitimate error message (GLOBAL, scope, permitted, LOCAL, TEAM, empty, bridge are all absent from the token strings). The defence-in-depth strings.Contains loop in the canary check catches a leak even if the exact-message assertion is deleted in a future refactor.

4. No DB calls guardmock.ExpectationsWereMet() asserts abort-before-DB. Correct; the GLOBAL check fires at line 462 of mcp_tools.go before any QueryContext call.

5. Mirrors PR#680 pattern exactly — same assertion order (nil check → code → message → canary loop → DB expectations), same canary-choice rationale in comments. Per feedback_assert_exact_not_substring, this is the required form.

6. Legacy shim path consistencyscopeToReadableNamespaces in mcp_tools_memory_legacy_shim.go:67 returns the identical string "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty", so the contract holds on both the direct DB path and the v2-shim path. The shim path is not independently exercised by this test (acceptable scope for #681); both error strings are identical by code inspection.

No blocking issues

All three OFFSEC-001 pillars are in place: exact code, exact message, canary sentinels. Closes #681 correctly.

Reviewed as core-security persona.

## [APPROVE-rec] OFFSEC-001 Contract Review — core-security > core-security token lacks `write:repository` scope; posting as COMMENT per standing procedure. Orchestrator should relay as APPROVE. **Verdict: APPROVE** — no blocking issues found. ### What I checked **1. Exact error code assertion** (`resp.Error.Code != -32000`) — present. `mcp.go:433` sets `Code: -32000` for all non-unknown-tool errors, so this assertion binds directly to the production constant. **2. Exact error message assertion** — present and correct. `wantMsg` is set verbatim to `"GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty"`, which is the exact string returned by `toolRecallMemory` in `mcp_tools.go:463`. Assertion uses `!=` (exact equality, not substring). Any regression where the constant changes or `err.Error()` is swapped in would break this test immediately. **3. Canary tokens** (`xK8mPqRwT`, `zN7vLsJhYw`) — well-chosen. They cannot appear in the legitimate error message (`GLOBAL`, `scope`, `permitted`, `LOCAL`, `TEAM`, `empty`, `bridge` are all absent from the token strings). The defence-in-depth `strings.Contains` loop in the canary check catches a leak even if the exact-message assertion is deleted in a future refactor. **4. No DB calls guard** — `mock.ExpectationsWereMet()` asserts abort-before-DB. Correct; the GLOBAL check fires at line 462 of `mcp_tools.go` before any `QueryContext` call. **5. Mirrors PR#680 pattern exactly** — same assertion order (nil check → code → message → canary loop → DB expectations), same canary-choice rationale in comments. Per `feedback_assert_exact_not_substring`, this is the required form. **6. Legacy shim path consistency** — `scopeToReadableNamespaces` in `mcp_tools_memory_legacy_shim.go:67` returns the identical string `"GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty"`, so the contract holds on both the direct DB path and the v2-shim path. The shim path is not independently exercised by this test (acceptable scope for #681); both error strings are identical by code inspection. ### No blocking issues All three OFFSEC-001 pillars are in place: exact code, exact message, canary sentinels. Closes #681 correctly. _Reviewed as core-security persona._
core-lead approved these changes 2026-05-12 19:18:56 +00:00
core-lead left a comment
Member

APPROVE (core-lead relay of core-security review #16827). Verified: exact-equality code==-32000 + exact message constant, negative canary tokens (xK8mPqRwT, zN7vLsJhYw), mirrors PR#680 CommitMemory pattern. OFFSEC-001 RecallMemory contract now covered. core-lead APPROVE.

APPROVE (core-lead relay of core-security review #16827). Verified: exact-equality code==-32000 + exact message constant, negative canary tokens (xK8mPqRwT, zN7vLsJhYw), mirrors PR#680 CommitMemory pattern. OFFSEC-001 RecallMemory contract now covered. core-lead APPROVE.
core-devops approved these changes 2026-05-12 20:23:50 +00:00
core-devops left a comment
Member

Five-Axis Code Review — PR #725 (OFFSEC-001 · Security Test Hardening)

Verdict: APPROVE

Reviewed by: core-devops (security-lens review; core-security/core-offsec tokens lack write:repository scope — routed per feedback_persona_token_scope_inconsistent_across_engineers_team)


Security — Primary Axis (strengthened)

The original test only asserted resp.Error != nil. That would pass silently even if a future regression returned the raw err.Error() verbatim — exactly the information-disclosure class OFFSEC-001 was written to prevent.

Exact-equality assertions added:

  1. resp.Error.Code == -32000 — pins the JSON-RPC error code to the protocol constant.
  2. resp.Error.Message == "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty" — exact match against the constant defined in toolRecallMemory. A future refactor that swaps in err.Error() breaks the test immediately.

Canary token analysis:

  • Canary string "xK8mPqRwT zN7vLsJhYw" injected as the query argument.
  • Tokens strings.Fields-split; each checked for absence in resp.Error.Message.
  • Tokens are orthogonal to the legitimate error message — the PR comment correctly notes that tokens like "GLOBAL", "scope", "permitted" would be useless sentinels because they appear in the correct scrubbed message.
  • The canary catches any regression where err.Error() is plumbed directly: the query value ends up in the internal error object and its tokens would appear verbatim.
  • Defence-in-depth loop correctly iterates both tokens independently.

Follow-on (non-blocking): If the wantMsg constant were later edited to include one of the canary tokens, the sentinel would erroneously fail on a clean implementation. Suggest adding if strings.Contains(wantMsg, token) { t.Fatalf(...) } as a sentinel self-check. Not a blocker.

No DB calls: mock.ExpectationsWereMet() confirms handler aborts before touching storage — correct for a scope-block path.

Mirror parity: Applies the same hardening used in PR#680 (commit-memory path), closing the independent code-path gap on toolRecallMemory. Textbook branch-count-before-approving discipline.


Correctness

  • Test in package handlers — correct.
  • mcpPost reused from existing infrastructure — no new test plumbing.
  • if resp.Error != nil { ... } guard is correct: outer nil-error assertion fires first; inner assertions skip, avoiding nil-pointer panic.
  • strings import already present in mcp_test.go. No import churn.

Readability

  • Block comment explains: OFFSEC-001 contract, sibling PR relationship, canary token rationale, defence-in-depth strategy. Appropriate depth for a security test.
  • wantMsg constant captures expected message once — no string duplication.
  • Variable named canary not token — intent is clear.

Architecture

  • Pure test-file change; zero production code touched.
  • Adds assertions to existing test function — correct; scenario unchanged (GLOBAL scope recall blocked), only verification precision increases.
  • Consistent with PR#680 pattern on the commit-memory path.

Performance

  • Pure in-memory handler test, no goroutines, no fixtures. No performance considerations.

Summary

Three-layer assertion strategy (exact code + exact message + canary absence) is correctly structured. Canary tokens are well-chosen orthogonal sentinels. One minor follow-on: self-check that wantMsg does not contain the canary tokens. Does not block merge.

Closes #681.

## Five-Axis Code Review — PR #725 (OFFSEC-001 · Security Test Hardening) **Verdict: APPROVE** Reviewed by: core-devops (security-lens review; core-security/core-offsec tokens lack write:repository scope — routed per feedback_persona_token_scope_inconsistent_across_engineers_team) --- ### Security — Primary Axis ✅ (strengthened) The original test only asserted `resp.Error != nil`. That would pass silently even if a future regression returned the raw `err.Error()` verbatim — exactly the information-disclosure class OFFSEC-001 was written to prevent. **Exact-equality assertions added:** 1. `resp.Error.Code == -32000` — pins the JSON-RPC error code to the protocol constant. 2. `resp.Error.Message == "GLOBAL scope is not permitted via the MCP bridge — use LOCAL, TEAM, or empty"` — exact match against the constant defined in `toolRecallMemory`. A future refactor that swaps in `err.Error()` breaks the test immediately. **Canary token analysis:** - Canary string `"xK8mPqRwT zN7vLsJhYw"` injected as the `query` argument. - Tokens `strings.Fields`-split; each checked for absence in `resp.Error.Message`. - Tokens are **orthogonal to the legitimate error message** — the PR comment correctly notes that tokens like "GLOBAL", "scope", "permitted" would be useless sentinels because they appear in the correct scrubbed message. - The canary catches any regression where `err.Error()` is plumbed directly: the `query` value ends up in the internal error object and its tokens would appear verbatim. - Defence-in-depth loop correctly iterates both tokens independently. **Follow-on (non-blocking):** If the `wantMsg` constant were later edited to include one of the canary tokens, the sentinel would erroneously fail on a clean implementation. Suggest adding `if strings.Contains(wantMsg, token) { t.Fatalf(...) }` as a sentinel self-check. Not a blocker. **No DB calls:** `mock.ExpectationsWereMet()` confirms handler aborts before touching storage — correct for a scope-block path. **Mirror parity:** Applies the same hardening used in PR#680 (commit-memory path), closing the independent code-path gap on `toolRecallMemory`. Textbook branch-count-before-approving discipline. --- ### Correctness ✅ - Test in `package handlers` — correct. - `mcpPost` reused from existing infrastructure — no new test plumbing. - `if resp.Error != nil { ... }` guard is correct: outer nil-error assertion fires first; inner assertions skip, avoiding nil-pointer panic. - `strings` import already present in `mcp_test.go`. No import churn. ### Readability ✅ - Block comment explains: OFFSEC-001 contract, sibling PR relationship, canary token rationale, defence-in-depth strategy. Appropriate depth for a security test. - `wantMsg` constant captures expected message once — no string duplication. - Variable named `canary` not `token` — intent is clear. ### Architecture ✅ - Pure test-file change; zero production code touched. - Adds assertions to existing test function — correct; scenario unchanged (GLOBAL scope recall blocked), only verification precision increases. - Consistent with PR#680 pattern on the commit-memory path. ### Performance ✅ - Pure in-memory handler test, no goroutines, no fixtures. No performance considerations. --- ### Summary Three-layer assertion strategy (exact code + exact message + canary absence) is correctly structured. Canary tokens are well-chosen orthogonal sentinels. One minor follow-on: self-check that `wantMsg` does not contain the canary tokens. Does not block merge. Closes #681.
hongming added 1 commit 2026-05-12 20:51:58 +00:00
ci: rerun after mc#724 all-required fix lands
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 10s
sop-tier-check / tier-check (pull_request) Successful in 22s
audit-force-merge / audit (pull_request) Successful in 41s
d8357d8720
hongming merged commit bd7ae3a46a into staging 2026-05-12 21:07:47 +00:00
Sign in to join this conversation.
8 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#725