{"id":"GHSA-wrr4-782v-jhwh","summary":"neotoma has tenant isolation gap in relationship query endpoints","details":"## 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.","modified":"2026-06-25T18:00:08.021100609Z","published":"2026-06-25T17:46:49Z","database_specific":{"cwe_ids":["CWE-201"],"severity":"LOW","github_reviewed":true,"github_reviewed_at":"2026-06-25T17:46:49Z","nvd_published_at":null},"references":[{"type":"WEB","url":"https://github.com/markmhendrickson/neotoma/security/advisories/GHSA-wrr4-782v-jhwh"},{"type":"WEB","url":"https://github.com/markmhendrickson/neotoma/issues/365"},{"type":"WEB","url":"https://github.com/markmhendrickson/neotoma/issues/366"},{"type":"WEB","url":"https://github.com/markmhendrickson/neotoma/issues/372"},{"type":"PACKAGE","url":"https://github.com/markmhendrickson/neotoma"}],"affected":[{"package":{"name":"neotoma","ecosystem":"npm","purl":"pkg:npm/neotoma"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0.13.0"},{"fixed":"0.14.0"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-wrr4-782v-jhwh/GHSA-wrr4-782v-jhwh.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:U"}]}