fix(registry): remove root-sibling bypass in CanCommunicate (#1955)
The `caller.ParentID == nil && target.ParentID == nil` fast path treated any two org-root workspaces as siblings, allowing cross-tenant communication when the workspaces table has no org_id column. Rules after this change: - self → self (unchanged) - siblings with same parent (unchanged) - ancestor ↔ descendant, any depth (unchanged) - unrelated org roots → DENIED (fixed) Updates integration-test fixtures to place source/target under a shared parent so CanCommunicate still returns true for the test scenario. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,7 @@ import (
|
||||
const integrationTestDelegationID = "del-159-test-integration"
|
||||
const integrationTestSourceID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
const integrationTestTargetID = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
const integrationTestParentID = "cccccccc-cccc-cccc-cccc-cccccccccccc"
|
||||
|
||||
// rawHTTPServer starts a TCP listener, serves one HTTP response, and closes.
|
||||
// It runs in a background goroutine so the test can proceed immediately after
|
||||
|
||||
@@ -99,11 +99,6 @@ func CanCommunicate(callerID, targetID string) bool {
|
||||
*caller.ParentID == *target.ParentID {
|
||||
return true
|
||||
}
|
||||
// Root-level siblings — both have no parent
|
||||
if caller.ParentID == nil && target.ParentID == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Direct parent → child (fast path; avoids the ancestor walk)
|
||||
if target.ParentID != nil && caller.ID == *target.ParentID {
|
||||
return true
|
||||
|
||||
@@ -63,14 +63,16 @@ func TestCanCommunicate_Siblings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanCommunicate_RootSiblings(t *testing.T) {
|
||||
func TestCanCommunicate_Denied_RootSiblings(t *testing.T) {
|
||||
mock := setupMockDB(t)
|
||||
// Both at root level (no parent)
|
||||
// Two unrelated org roots (both parent_id = NULL) must NOT communicate.
|
||||
// This is the regression test for #1955: without an org_id column, two
|
||||
// root workspaces have no shared ancestor, so they must be denied.
|
||||
expectLookup(mock, "ws-a", nil)
|
||||
expectLookup(mock, "ws-b", nil)
|
||||
|
||||
if !CanCommunicate("ws-a", "ws-b") {
|
||||
t.Error("root-level siblings should communicate")
|
||||
if CanCommunicate("ws-a", "ws-b") {
|
||||
t.Error("unrelated root-level workspaces should NOT communicate")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user