{"id":"GHSA-wrr4-782v-jhwh","title":"neotoma has tenant isolation gap in relationship query endpoints","summary":"neotoma has tenant isolation gap in relationship query endpoints","severity":"low","cwe":["CWE-201"],"vendor":"neotoma","product":"neotoma","ecosystem":"npm","affected":["neotoma >= 0.13.0, < 0.14.0"],"patched":["neotoma 0.14.0"],"published":"2026-06-25","updated":"2026-06-25","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-wrr4-782v-jhwh","references":[{"url":"https://github.com/markmhendrickson/neotoma/security/advisories/GHSA-wrr4-782v-jhwh"},{"url":"https://github.com/markmhendrickson/neotoma/issues/365"},{"url":"https://github.com/markmhendrickson/neotoma/issues/366"},{"url":"https://github.com/markmhendrickson/neotoma/issues/372"},{"url":"https://github.com/advisories/GHSA-wrr4-782v-jhwh"}],"tags":["ghsa","npm"],"ingestedAt":"2026-06-26T16:43:14.243Z","slug":"GHSA-wrr4-782v-jhwh","body":"## Overview\n\n## Summary\n\nThe `/list_relationships` and `/retrieve_graph_neighborhood` endpoints call `getAuthenticatedUserId` (confirming a valid session exists) but do not pass the resolved user ID into the Supabase query as an `.eq(\"user_id\", userId)` filter. As a result, queries return rows from all users rather than scoping to the authenticated caller's data.\n\n## Affected code\n\n**`/list_relationships`** (`src/actions.ts`):\n- Calls `getAuthenticatedUserId` but does not apply `.eq(\"user_id\", userId)` to the relationships query\n- Uses `.or()` string interpolation for entity ID matching without input validation\n\n**`/retrieve_graph_neighborhood`** (`src/actions.ts`):\n- Same pattern: auth resolved, user ID not applied to query filter\n\n## Affected versions\n\nv0.13.0\n\n## Prerequisites\n\n1. A valid authentication token for the Neotoma instance (attacker must have a legitimate account on the same instance)\n2. A known entity ID belonging to another user (~96 bits of entropy — brute-force not practical)\n\nAn unauthenticated caller is rejected at the auth middleware layer. The gap requires a second user account on the instance.\n\n## Impact\n\nAn authenticated user with a known cross-user entity ID can retrieve relationship edges and graph neighborhood data belonging to another user. No write capability is exposed.\n\n## Severity\n\nLow under current conditions — no multi-tenant deployments exist. Escalates to Medium the moment two or more user accounts share an instance.\n\n## Remediation\n\n1. Add `.eq(\"user_id\", userId)` to all Supabase queries in both handlers\n2. Validate entity ID inputs with `isNeotomaEntityId` before query construction\n3. Replace `.or()` string interpolation with separate scoped `.eq()` calls\n\nFix tracked in #365 (list_relationships) and #366 (retrieve_graph_neighborhood). Gate gap tracked in #372.\n\n## Affected packages\n\n- `neotoma >= 0.13.0, < 0.14.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `neotoma 0.14.0`","depth":"sunlit","depthScore":14,"depthScoreParts":{"impact":13.8,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}