GHSA-WRR4-782V-JHWH
Vulnerability from github – Published: 2026-06-25 17:46 – Updated: 2026-06-25 17:46Summary
The /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.
Affected code
/list_relationships (src/actions.ts):
- Calls getAuthenticatedUserId but does not apply .eq("user_id", userId) to the relationships query
- Uses .or() string interpolation for entity ID matching without input validation
/retrieve_graph_neighborhood (src/actions.ts):
- Same pattern: auth resolved, user ID not applied to query filter
Affected versions
v0.13.0
Prerequisites
- A valid authentication token for the Neotoma instance (attacker must have a legitimate account on the same instance)
- A known entity ID belonging to another user (~96 bits of entropy — brute-force not practical)
An unauthenticated caller is rejected at the auth middleware layer. The gap requires a second user account on the instance.
Impact
An 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.
Severity
Low under current conditions — no multi-tenant deployments exist. Escalates to Medium the moment two or more user accounts share an instance.
Remediation
- Add
.eq("user_id", userId)to all Supabase queries in both handlers - Validate entity ID inputs with
isNeotomaEntityIdbefore query construction - Replace
.or()string interpolation with separate scoped.eq()calls
Fix tracked in #365 (list_relationships) and #366 (retrieve_graph_neighborhood). Gate gap tracked in #372.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "neotoma"
},
"ranges": [
{
"events": [
{
"introduced": "0.13.0"
},
{
"fixed": "0.14.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-201"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-25T17:46:49Z",
"nvd_published_at": null,
"severity": "LOW"
},
"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\u0027s 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 \u2014 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 \u2014 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.",
"id": "GHSA-wrr4-782v-jhwh",
"modified": "2026-06-25T17:46:49Z",
"published": "2026-06-25T17:46:49Z",
"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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"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",
"type": "CVSS_V4"
}
],
"summary": "neotoma has tenant isolation gap in relationship query endpoints"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.