- Shipped
- September 23, 2026 at 1:16 AM UTC
- Author
- Kamo
- Commit
- 6488bd4
RagSearchController read orgId/memberId/memberType from the JSON body on every path, including requests authenticated by a *** session rather than X-Internal-Auth. A signed-in member with only VIEW_KB_ARTICLES could put a different org's id, or a different member's id, in the body and search that org's knowledge base or that member's private notes straight through Qdrant's filters — the session proved who was asking but never constrained what they could ask for. /reindex/{orgId} and /status/{orgId} had the same shape one level up: the path orgId was trusted once the caller held MANAGE_KB_SETTINGS in *some* org, without checking it was theirs. Making it worse, SessionHelper.hasRight returned true for a session with an empty rights list and again for one with no rights field at all, so the right checks guarding all three endpoints were skippable by the same missing field. The rule now: on the session path, org/member/memberType are derived from the session only **************** added to SessionHelper), and reindex/status additionally require the path orgId to equal the session's own org. hasRight fails closed, matching KBService's. The X-Internal-Auth path is unchanged — AIService has no session of its own and must still name who it is searching for in the body. Also switched the X-Internal-Auth secret comparison from String.equals to MessageDigest.isEqual so a timing difference can't leak the secret one byte at a time. Tests: SessionHelperRightsTest pins the fail-closed cases (mirrors KBService's suite). RagSearchControllerAuthzTest pins the body-trust bug and the reindex/status org check, and separately proves the internal-auth path still trusts the body (AIService's only way to call this). Each was verified red against the pre-fix code before this commit.
