fix(registry,discovery,mcp): prevent cross-tenant peer metadata leak (#1953) #1956

Closed
agent-pm wants to merge 1 commits from fix/cross-tenant-isolation-1953 into main
Member

Fixes #1953.

Three code paths used parent_id IS NULL to identify siblings of a root-level workspace. Because every org root has parent_id IS NULL, this predicate matched ALL org roots across ALL tenants, leaking peer metadata and allowing cross-tenant A2A delegation.

Changes

  • registry/access.go: Remove the root-level sibling fast-path in CanCommunicate. Two distinct workspaces with parent_id = NULL are in different orgs and must not communicate.
  • discovery.go: Skip the sibling query entirely when the caller is an org root (parent_id IS NULL). Each org has exactly one root.
  • mcp_tools.go: Same fix for the MCP list_peers tool path.

Test updates

  • TestCanCommunicate_RootSiblingsTestCanCommunicate_Denied_RootCrossTenant
  • Tests that mocked root-root communication now use parent-child or shared-parent relationships so they continue to test allowed paths.

Verification

All 695 workspace-server tests pass with -race.


🤖 Generated with Claude Code

Fixes #1953. Three code paths used `parent_id IS NULL` to identify siblings of a root-level workspace. Because every org root has `parent_id IS NULL`, this predicate matched ALL org roots across ALL tenants, leaking peer metadata and allowing cross-tenant A2A delegation. ### Changes - **registry/access.go**: Remove the root-level sibling fast-path in `CanCommunicate`. Two distinct workspaces with `parent_id = NULL` are in different orgs and must not communicate. - **discovery.go**: Skip the sibling query entirely when the caller is an org root (`parent_id IS NULL`). Each org has exactly one root. - **mcp_tools.go**: Same fix for the MCP `list_peers` tool path. ### Test updates - `TestCanCommunicate_RootSiblings` → `TestCanCommunicate_Denied_RootCrossTenant` - Tests that mocked root-root communication now use parent-child or shared-parent relationships so they continue to test allowed paths. ### Verification All 695 workspace-server tests pass with `-race`. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
agent-pm added 2 commits 2026-05-27 16:02:49 +00:00
docs: fix stale molecule-monorepo references (#1837)
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 46s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 1m24s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m12s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Successful in 8m4s
qa-review / approved (pull_request) Refired via /qa-recheck by unknown
security-review / approved (pull_request) Refired via /security-recheck; security-review failed
gate-check-v3 / gate-check (pull_request) Successful in 15s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 8s
sop-tier-check / tier-check (pull_request_review) Successful in 6s
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
CI / Python Lint & Test (pull_request) Successful in 2s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 5s
CI / Detect changes (pull_request) Successful in 5s
E2E API Smoke Test / detect-changes (pull_request) Successful in 9s
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 11s
E2E Chat / detect-changes (pull_request) Successful in 14s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 14s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 9s
Harness Replays / detect-changes (pull_request) Successful in 5s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 5s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 4s
qa-review / approved (pull_request_target) Successful in 3s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
sop-checklist / review-refire (pull_request_target) Has been skipped
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 3s
security-review / approved (pull_request_target) Successful in 3s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 1s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 1s
CI / Platform (Go) (pull_request) Successful in 6s
Harness Replays / Harness Replays (pull_request) Successful in 1s
gate-check-v3 / gate-check (pull_request_target) Successful in 15s
sop-tier-check / tier-check (pull_request_target) Successful in 9s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 6s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 10s
E2E Chat / E2E Chat (pull_request) Successful in 11s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 56s
CI / Canvas (Next.js) (pull_request) Successful in 6m18s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Successful in 7m23s
audit-force-merge / audit (pull_request_target) Successful in 4s
2a04e9bec1
Replace remaining user-facing references to the old repo name
molecule-monorepo with molecule-core in clone instructions,
documentation links, path examples, and source links.

Affected files:
- README.md (clone commands in Quick Start)
- docs/quickstart.md (clone commands in one-command and manual paths)
- docs/architecture/molecule-technical-doc.md (repo links)
- docs/development/local-development.md (path example)
- docs/infra/workspace-terminal.md (factually incorrect rename claim)
- docs/integrations/opencode.md (task example)
- docs/internal-content-policy.md (repo name and path references)
- canvas/src/app/pricing/page.tsx (source code link)
- .env.example (repo name in comment)
- tools/check-template-parity.sh (path example in comment)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(registry,discovery,mcp): prevent cross-tenant peer metadata leak (#1953)
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 11s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 13s
CI / Detect changes (pull_request) Successful in 13s
CI / Python Lint & Test (pull_request) Successful in 9s
E2E API Smoke Test / detect-changes (pull_request) Successful in 19s
E2E Chat / detect-changes (pull_request) Successful in 15s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 55s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 48s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 14s
Harness Replays / detect-changes (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 9s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
gate-check-v3 / gate-check (pull_request) Successful in 13s
qa-review / approved (pull_request) Failing after 4s
security-review / approved (pull_request) Failing after 4s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 3s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 5s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m15s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Successful in 6m22s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 14s
E2E Chat / E2E Chat (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 13s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Failing after 1m44s
Harness Replays / Harness Replays (pull_request) Successful in 11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Failing after 2m18s
CI / Platform (Go) (pull_request) Successful in 6m54s
CI / Canvas (Next.js) (pull_request) Successful in 7m20s
CI / all-required (pull_request) Successful in 33m31s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
audit-force-merge / audit (pull_request) Waiting to run
c8e19bbdcd
Three code paths used `parent_id IS NULL` to identify 'siblings' of a
root-level workspace. Because every org root has `parent_id IS NULL`,
this predicate matched ALL org roots across ALL tenants, leaking peer
metadata and allowing cross-tenant A2A delegation.

Fixes:
- registry/access.go: Remove the root-level sibling fast-path in
  `CanCommunicate`. Two distinct workspaces with `parent_id = NULL`
  are in different orgs and must not communicate.
- discovery.go: Skip the sibling query entirely when the caller is an
  org root (parent_id IS NULL). Each org has exactly one root.
- mcp_tools.go: Same fix for the MCP `list_peers` tool path.

Tests updated to reflect the new security boundary:
- TestCanCommunicate_RootSiblings → TestCanCommunicate_Denied_RootCrossTenant
- Tests that mocked root-root communication now use parent-child or
  shared-parent relationships so they continue to test allowed paths.

All 695 workspace-server tests pass with -race.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Author
Member

Note: PR #1954 (core-be) already fixes the three leaking paths (discovery.Peers, mcp_tools.toolListPeers, a2a_proxy) via org_scope.go CTE scoping. This PR (#1956) closes the residual weakness filed as #1955registry.CanCommunicate itself still treats any two org-roots as siblings.\n\nThis PR is smaller and can land independently of #1954, or be merged after #1954 to close the full class. Either order is safe.

Note: PR #1954 (core-be) already fixes the three leaking paths (discovery.Peers, mcp_tools.toolListPeers, a2a_proxy) via `org_scope.go` CTE scoping. This PR (#1956) closes the **residual weakness** filed as #1955 — `registry.CanCommunicate` itself still treats any two org-roots as siblings.\n\nThis PR is smaller and can land independently of #1954, or be merged after #1954 to close the full class. Either order is safe.
agent-pm closed this pull request 2026-05-27 17:46:36 +00:00
Author
Member

Closing as superseded by #1954 (canonical cross-tenant fix with broader coverage: org_scope.go, a2a_proxy.go hardening, E2E test update, integration tests).

Closing as superseded by #1954 (canonical cross-tenant fix with broader coverage: org_scope.go, a2a_proxy.go hardening, E2E test update, integration tests).
Some required checks failed
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 11s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 13s
CI / Detect changes (pull_request) Successful in 13s
CI / Python Lint & Test (pull_request) Successful in 9s
E2E API Smoke Test / detect-changes (pull_request) Successful in 19s
E2E Chat / detect-changes (pull_request) Successful in 15s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 10s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Successful in 55s
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Successful in 48s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 14s
Harness Replays / detect-changes (pull_request) Successful in 9s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 9s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
gate-check-v3 / gate-check (pull_request) Successful in 13s
qa-review / approved (pull_request) Failing after 4s
security-review / approved (pull_request) Failing after 4s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 3s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 5s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
E2E Staging External Runtime / E2E Staging External Runtime (pull_request) Successful in 5m15s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Successful in 6m22s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 14s
E2E Chat / E2E Chat (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 13s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Failing after 1m44s
Required
Details
Harness Replays / Harness Replays (pull_request) Successful in 11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Failing after 2m18s
Required
Details
CI / Platform (Go) (pull_request) Successful in 6m54s
CI / Canvas (Next.js) (pull_request) Successful in 7m20s
CI / all-required (pull_request) Successful in 33m31s
Required
Details
CI / Canvas Deploy Reminder (pull_request) Has been skipped
audit-force-merge / audit (pull_request) Waiting to run

Pull request closed

Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#1956