thoka
(Thomas Kalka)
22 أبريل 2026، 7:47م
1
أثناء التحقيق في https://meta.discourse.org/t/private-topics-plugin/268646/109، اكتشفت أن الإشارة إلى مستخدم في فئة مقيدة لا يتم الإبلاغ عنها عندما يحتوي اسم المستخدم على أحرف كبيرة.
عند الإشارة إلى @SomeUser، يطلب المحرر
/composer/mentions.json?names[]=SomeUser&topic_id=10728
في النتيجة، يُعاد اسم المستخدم بحروف صغيرة، دون تعيين user_reasons.
الاستعلام عن اسم المستخدم بحروف صغيرة يُرجع "user_reasons": {"someuser":"category"}.
إذا استخدمت أحرفًا صغيرة لأسماء المستخدمين في المحرر، فسيتم عرض تحذيرات للأشخاص الذين لا يملكون الصلاحيات الكافية.
إذا استخدمت الإكمال التلقائي الذي يوفره المحرر، فسيتم استبدال أسماء المستخدمين المكتوبة بحروف صغيرة بأسماء بحروف كبيرة، وبالتالي لن يتم الإبلاغ عنها.
3 إعجابات
RGJ
(Richard - Communiteq)
22 أبريل 2026، 9:26م
2
اكتشاف رائع @thoka !
المشكلة هنا
users تُرجع {"username_lower" => كائن مستخدم }
ومع ذلك، إذا لم يتم تحويل name إلى أحرف صغيرة، فإن users[name] غير موجود.
الحل:
if user = users[name.downcase]
...
elsif group = groups[name.downcase]
...
أو الأفضل: قم بتحويل جميع الأسماء إلى أحرف صغيرة في بداية الدالة، لأن هناك العديد من المشكلات فيها. فبينما تتعامل groups بشكل جيد مع .where("lower(name) IN (?)", @names.map(&:downcase))، فإن الدوال مثل visible_group_ids_for_allowed_check، وtopic_allowed_group_ids، وmentionable_group_ids، وmembers_visible_group_ids تستخدم جميعها where(name: @names)، مما يُدخل مشكلات تتعلق بحساسية الأحرف أيضًا.
3 إعجابات
الإصلاح الصحيح هو
main ← fix/unicode-username-lookups
closed 02:15AM - 14 Apr 26 UTC
Leverages Rails' built-in `normalizes` feature to handle username
normalization … consistently throughout the codebase. When you call
`User.where(username_lower: value)` or `find_by(username_lower: value)`,
ActiveRecord now automatically normalizes the input value.
Key changes:
- Adds `normalizes :username` (unicode normalize) and
`normalizes :username_lower` (unicode normalize + downcase) to User
- Adds `normalizes :email` (strip + downcase) to UserEmail
- Removes manual `.downcase` and `.map(&:downcase)` calls before AR queries
- Adds `User#matches_username?` method for comparing usernames
- Simplifies `filter_by_username` and `filter_by_username_or_email` scopes
to use `ILIKE ANY(ARRAY[?])` instead of branching on array vs single value
- Updates `filter_by_username` to normalize input for ILIKE patterns
- Updates `find_by_username` to rely on AR normalization
- Fixes a SQL injection vulnerability in search user ordering
- Uses before_save callback for username_lower assignment to ensure it
runs even when validation is skipped (e.g., finish installation flow)
- Adds shoulda matcher tests for username normalizations
The `normalize_username` class method is kept for cases where AR can't help:
raw SQL queries, ILIKE patterns, and direct comparisons.
Ref - https://meta.discourse.org/t/393646
لكنه تغيير كبير جدًا لأكون مرتاحًا لدمجه في هذه المرحلة
بدلاً من ذلك، سأقوم بإصلاح كل “نقطة نهاية” على حدة لتبسيط المراجعة وتقليل المخاطر.
إليك الخطوة الأولى
main ← fix-composer-mention-case-sensitivity
opened 08:30AM - 23 Apr 26 UTC
Mentioning `@SomeUser` in a topic the mentioned user can't see (restricted categ… ory, PM they aren't invited to, topic they've muted) silently skipped the "cannot see this mention" warning popup whenever the typed name contained any uppercase letter. Same for mixed-case group names. Mention validation itself worked because of an existing client-side `.toLowerCase()` workaround, so the bug was easy to miss — mentions stayed `<a class="mention">` but the user got no signal that the mentioned account wouldn't actually be notified.
Server side, `ComposerController#mentions` builds its `users` lookup keyed by `username_lower` and `groups` keyed by the case-preserved DB `name`, then iterated `@names.each { |n| users[n] || groups[n] }` without normalizing — so any uppercase character missed both hashes and `user_reasons`/`group_reasons` came back empty. Four downstream group helpers (`mentionable_group_ids`, `members_visible_group_ids`, `topic_allowed_group_ids`, `visible_group_ids_for_allowed_check`) used `where(name: @names)` which is case-sensitive in PostgreSQL, and the `SiteSetting.here_mention` membership test compared raw strings.
Client side, `link-mentions.js` cached `foundUsers` / `userReasons` / `foundGroups` / `groupReasons` by the case as typed, and the prosemirror `mention.js` warning lookup did `response.users.includes(name)` plus `response.user_reasons[name]` with the original case — both of which the server only ever returned in the casing it had on hand.
Normalize the controller's `@names` and `@allowed_names` once at the top of the action, switch all four group helpers to `LOWER(name) IN (?)`, lower-case the response keys, and lower-case client caches and lookups end to end. Also extract the inline notified-member query into `already_notified_member_count` and tighten the request specs to cover users, mentionable groups, group reasons in PMs, and `allowed_names` (both user and group branches) with mixed-case input.
https://meta.discourse.org/t/401292
إعجابَين (2)