Summary
filterNodesByChannelPermission in nodeEnhancer.ts is called from server.ts:1004 without passing the nodesSourceId parameter. This causes the permission filter to perform a union across all sources rather than scoping to the requested source, creating a cross-source permission leak for guest users.
Affected Files
server.ts:1004 — call site where nodesSourceId is extracted but not forwarded to filterNodesByChannelPermission
nodeEnhancer.ts:160 — filterNodesByChannelPermission implementation where sourceId === undefined triggers a global permission union
Root Cause
In server.ts around line 1004, nodesSourceId is correctly extracted to scope which nodes are fetched. However, when filterNodesByChannelPermission is invoked, nodesSourceId is not passed as an argument.
Inside filterNodesByChannelPermission (nodeEnhancer.ts:160), when sourceId is undefined, the call to getUserPermissionSetAsync falls back to a union of permissions across all configured sources. This means the permission check is evaluated against the broadest possible permission set rather than the specific source the nodes belong to.
Impact
A guest user who holds channel 0 read permission on source A will pass the permission filter for channel 0 nodes fetched from source B, even if they have been granted zero permissions on source B.
- The
sourceId correctly controls which nodes are fetched (source-scoped fetch).
- But the permission filter uses a global union, so the security boundary is not enforced at the filter layer.
- Guests can view nodes from sources they have no explicit permission for, as long as the channel index overlaps with a source they do have access to.
Reproduction
- Configure two sources (A and B) with channel 0 on each.
- Create a guest user with channel 0 read permission on source A only.
- Request nodes scoped to source B.
- Observe that channel 0 nodes from source B pass the permission filter.
Fix
Pass nodesSourceId (or the equivalent source identifier) through to filterNodesByChannelPermission at the server.ts:1004 call site, and ensure getUserPermissionSetAsync inside nodeEnhancer.ts:160 scopes its lookup to that specific source rather than performing a cross-source union when a sourceId is provided.
Related
This is the same class of bug as #3712 (sourceId scoping gaps). The fetch path was corrected but the permission filter path was not updated to match.
Authored by NodeZero 0️⃣
Summary
filterNodesByChannelPermissioninnodeEnhancer.tsis called fromserver.ts:1004without passing thenodesSourceIdparameter. This causes the permission filter to perform a union across all sources rather than scoping to the requested source, creating a cross-source permission leak for guest users.Affected Files
server.ts:1004— call site wherenodesSourceIdis extracted but not forwarded tofilterNodesByChannelPermissionnodeEnhancer.ts:160—filterNodesByChannelPermissionimplementation wheresourceId === undefinedtriggers a global permission unionRoot Cause
In
server.tsaround line 1004,nodesSourceIdis correctly extracted to scope which nodes are fetched. However, whenfilterNodesByChannelPermissionis invoked,nodesSourceIdis not passed as an argument.Inside
filterNodesByChannelPermission(nodeEnhancer.ts:160), whensourceIdisundefined, the call togetUserPermissionSetAsyncfalls back to a union of permissions across all configured sources. This means the permission check is evaluated against the broadest possible permission set rather than the specific source the nodes belong to.Impact
A guest user who holds channel 0 read permission on source A will pass the permission filter for channel 0 nodes fetched from source B, even if they have been granted zero permissions on source B.
sourceIdcorrectly controls which nodes are fetched (source-scoped fetch).Reproduction
Fix
Pass
nodesSourceId(or the equivalent source identifier) through tofilterNodesByChannelPermissionat theserver.ts:1004call site, and ensuregetUserPermissionSetAsyncinsidenodeEnhancer.ts:160scopes its lookup to that specific source rather than performing a cross-source union when a sourceId is provided.Related
This is the same class of bug as #3712 (sourceId scoping gaps). The fetch path was corrected but the permission filter path was not updated to match.
Authored by NodeZero 0️⃣