🛂 fix: Validate types Query Param in People Picker Access Middleware#12276
Merged
Conversation
checkPeoplePickerAccess only inspected `req.query.type` (singular), allowing callers to bypass type-specific permission checks by using the `types` (plural) parameter accepted by the controller. Now both `type` and `types` are collected and each requested principal type is validated against the caller's role permissions.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR closes an authorization gap in the people picker access middleware by ensuring permission checks apply to both the legacy type query param and the controller-supported types param, preventing bypass of type-specific permissions.
Changes:
- Update
checkPeoplePickerAccessto collect requested principal types from bothtypeandtypesand enforce permissions for each requested type. - Adjust “mixed search” handling to key off “no valid requested types” rather than only “no
type”. - Add test coverage for
typesas comma-separated strings, arrays, and combinedtype+typesinputs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| api/server/middleware/checkPeoplePickerAccess.js | Enforces per-type permission checks across both type and types query params. |
| api/server/middleware/checkPeoplePickerAccess.spec.js | Adds regression tests to ensure types cannot bypass type-specific permission checks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…-case tests - Hoist VALID_PRINCIPAL_TYPES to module-level Set to avoid per-request allocation - Include both `type` and `types` in error log for debuggability - Restore detailed JSDoc documenting per-type permission requirements - Add missing .json() assertion on partial-denial test - Add edge-case tests: all-invalid types, empty string types, PrincipalType.PUBLIC
The stale type used `type` (singular) but the controller and all callers use `types` (plural array). Aligns with PrincipalSearchParams in types/queries.ts.
2f8e51c to
9bb209a
Compare
types query param in people picker access middlewaretypes Query Param in People Picker Access Middleware
jcbartle
pushed a commit
to jcbartle/LibreChat
that referenced
this pull request
May 11, 2026
danny-avila#12276) * 🛂 fix: Validate `types` query param in people picker access middleware checkPeoplePickerAccess only inspected `req.query.type` (singular), allowing callers to bypass type-specific permission checks by using the `types` (plural) parameter accepted by the controller. Now both `type` and `types` are collected and each requested principal type is validated against the caller's role permissions. * 🛂 refactor: Hoist valid types constant, improve logging, and add edge-case tests - Hoist VALID_PRINCIPAL_TYPES to module-level Set to avoid per-request allocation - Include both `type` and `types` in error log for debuggability - Restore detailed JSDoc documenting per-type permission requirements - Add missing .json() assertion on partial-denial test - Add edge-case tests: all-invalid types, empty string types, PrincipalType.PUBLIC * 🏷️ fix: Align TPrincipalSearchParams with actual controller API The stale type used `type` (singular) but the controller and all callers use `types` (plural array). Aligns with PrincipalSearchParams in types/queries.ts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixed an authorization bypass in
checkPeoplePickerAccesswhere the middleware only inspectedreq.query.type(singular) while the controller exclusively consumedreq.query.types(plural). An authenticated caller could send?types=groupto skip type-specific permission enforcement entirely, receiving principal data their role was not permitted to access.type-only check with a unified collection step that reads bothtypeandtypesfromreq.queryand validates every collected principal type against the caller's role permissions before callingnext().!typetorequestedTypes.size === 0so it correctly triggers when neither parameter contributes a recognized type.VALID_PRINCIPAL_TYPESto a module-levelSetconstant, eliminating a per-requestObject.keys()allocation that previously rebuilt the same three-element structure on every call.catchblock error log to include bothtypeandtypesquery values, replacing the previous entry that always showedtype = undefinedfortypes-only requests.user→ VIEW_USERS,group→ VIEW_GROUPS,role→ VIEW_ROLES, no type → any of the above), which was lost in the initial fix commit.TPrincipalSearchParamsinaccessPermissions.tsfrom the staletype?: PrincipalType(singular) totypes?: Array<PrincipalType.USER | PrincipalType.GROUP | PrincipalType.ROLE>, aligning the shared type with the actual controller API.TPrincipalSearchResponseinaccessPermissions.tsfrom the staletype?:(singular) totypes?: Array<...> | null, wherenullaccurately represents the controller settingtypeFilters = nullwhen all provided type values are invalid, distinct fromundefinedwhen the parameter is absent entirely.types-param bypass scenario, comma-separated multi-type strings, array-formattypes(Expressqsparsing), combinedtype+typesparams, all-invalid type values falling through to mixed search, empty-stringtypeswith no permissions,PrincipalType.PUBLICtreated as mixed search, and a missing.json()assertion on an existing partial-denial test.Change Type
Testing
Verified with Jest unit tests in
api/server/middleware/checkPeoplePickerAccess.spec.js. All 20 tests pass.Key scenarios exercised:
?types=groupwith VIEW_GROUPS=false → 403 (the original bypass, now blocked)?types=user,rolewith VIEW_ROLES=false → 403 onrole?types=user,groupwith both VIEW_USERS=true and VIEW_GROUPS=true → 200?types[]=group&types[]=role(array form) with VIEW_GROUPS=false → 403?type=user&types=groupwith VIEW_GROUPS=false → 403?types=foobar→ mixed-search fallback,next()called when user has any VIEW permission?types=(empty string) with all permissions false → 403?types=public→ mixed-search fallback,next()called (PUBLIC absent from valid types)Test Configuration
api/workspace:npx jest checkPeoplePickerAccessChecklist