regression: 2fa inline errors for API calls - #40678
Conversation
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
|
WalkthroughRefactors 2FA handling to an error-driven callback pattern: centralizes 2FA error predicates, adds a promise-based client handler (challenge2fa) that opens TwoFactorModal, and updates RestClient to invoke the handler on parsed HTTP 400 errors and retry with x-2fa headers. ChangesTwo-Factor Authentication Challenge Flow Refactor
Sequence DiagramsequenceDiagram
participant Client
participant RestClient
participant Errors
participant Challenge2fa
participant TwoFactorModal
Client->>RestClient: send(request)
RestClient->>RestClient: receives HTTP 400
RestClient->>Errors: parse JSON error
RestClient->>Challenge2fa: challenge2fa({ error })
Challenge2fa->>TwoFactorModal: open modal with handlers
TwoFactorModal->>Challenge2fa: onConfirm(code)
Challenge2fa->>Challenge2fa: hash code if password
Challenge2fa-->>RestClient: resolve code promise
RestClient->>RestClient: retry request with x-2fa-code/x-2fa-method
RestClient->>Challenge2fa: resolveChallenge()
RestClient-->>Client: response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/meteor/client/lib/2fa/utils.ts (1)
3-9: 💤 Low valueConsider re-exporting
isTotpMaxAttemptsErrorfrom@rocket.chat/api-clientas well.Since
isTotpInvalidErrorandisTotpRequiredErrorare now re-exported from@rocket.chat/api-client, andisTotpMaxAttemptsErroris also exported from the same package (seepackages/api-client/src/errors.tsline 35 and theexport * from './errors'in the package index), the local implementation here is now duplicative.Suggested change
import type { MeteorErrorLike } from './types'; -export { isTotpInvalidError, isTotpRequiredError } from '`@rocket.chat/api-client`'; - -export const isTotpMaxAttemptsError = ( - error: unknown, -): error is MeteorErrorLike & ({ error: 'totp-max-attempts' } | { errorType: 'totp-max-attempts' }) => - (error as { error?: unknown } | undefined)?.error === 'totp-max-attempts' || - (error as { errorType?: unknown } | undefined)?.errorType === 'totp-max-attempts'; +export { isTotpInvalidError, isTotpRequiredError, isTotpMaxAttemptsError } from '`@rocket.chat/api-client`';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/lib/2fa/utils.ts` around lines 3 - 9, The file defines a local isTotpMaxAttemptsError but other errors (isTotpInvalidError, isTotpRequiredError) are already re-exported from `@rocket.chat/api-client`; remove the local isTotpMaxAttemptsError implementation and replace it with a re-export from the same package so all three come from `@rocket.chat/api-client` (match the existing export style used for isTotpInvalidError and isTotpRequiredError); ensure you update exports in apps/meteor/client/lib/2fa/utils.ts to export isTotpMaxAttemptsError from `@rocket.chat/api-client` and delete the local type-guard implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/api-client/src/errors.ts`:
- Around line 16-30: isTotpError currently promises a full
TwoFactorError<TErrorType> (including details.method) but only checks top-level
error/errorType fields; update the predicate so its type matches the runtime
checks instead of claiming details exist — e.g., change the return type of
isTotpError to assert only that the value has error === TErrorType or errorType
=== TErrorType (a narrower type such as a union of objects with just error or
errorType) and do not claim TwoFactorError<TErrorType> (or require
TwoFactorMethod); update references to isTotpError and any call sites that rely
on details.method to first run hasRequiredTwoFactorMethod or narrow the type
before accessing details.method.
---
Nitpick comments:
In `@apps/meteor/client/lib/2fa/utils.ts`:
- Around line 3-9: The file defines a local isTotpMaxAttemptsError but other
errors (isTotpInvalidError, isTotpRequiredError) are already re-exported from
`@rocket.chat/api-client`; remove the local isTotpMaxAttemptsError implementation
and replace it with a re-export from the same package so all three come from
`@rocket.chat/api-client` (match the existing export style used for
isTotpInvalidError and isTotpRequiredError); ensure you update exports in
apps/meteor/client/lib/2fa/utils.ts to export isTotpMaxAttemptsError from
`@rocket.chat/api-client` and delete the local type-guard implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 89516109-87ed-414a-96c8-2cad9c14c724
📒 Files selected for processing (7)
apps/meteor/app/utils/client/lib/RestApiClient.tsapps/meteor/client/lib/2fa/challenge2fa.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/utils.tspackages/api-client/src/RestClientInterface.tspackages/api-client/src/errors.tspackages/api-client/src/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 📦 Build Packages
- GitHub Check: cubic · AI code reviewer
- GitHub Check: CodeQL-Build
- GitHub Check: Hacktron Security Check
- GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)
**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation
Files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/app/utils/client/lib/RestApiClient.tspackages/api-client/src/RestClientInterface.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/index.tspackages/api-client/src/errors.ts
🧠 Learnings (5)
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.
Applied to files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.
Applied to files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.
Applied to files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/app/utils/client/lib/RestApiClient.tspackages/api-client/src/RestClientInterface.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/index.tspackages/api-client/src/errors.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.
Applied to files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/app/utils/client/lib/RestApiClient.tspackages/api-client/src/RestClientInterface.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/index.tspackages/api-client/src/errors.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.
Applied to files:
apps/meteor/client/lib/2fa/utils.tsapps/meteor/app/utils/client/lib/RestApiClient.tspackages/api-client/src/RestClientInterface.tsapps/meteor/client/lib/2fa/process2faReturn.tsapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/index.tspackages/api-client/src/errors.ts
🧬 Code graph analysis (4)
apps/meteor/app/utils/client/lib/RestApiClient.ts (1)
apps/meteor/client/lib/2fa/challenge2fa.ts (1)
challenge2fa(89-161)
apps/meteor/client/lib/2fa/process2faReturn.ts (1)
apps/meteor/client/lib/2fa/types.ts (1)
MeteorErrorLike(4-9)
apps/meteor/client/lib/2fa/challenge2fa.ts (4)
packages/api-client/src/errors.ts (5)
TwoFactorMethod(3-3)isTotpMaxAttemptsError(36-36)isTotpRequiredError(32-32)isTotpInvalidError(34-34)hasRequiredTwoFactorMethod(40-48)apps/meteor/client/lib/user.ts (1)
getUser(13-17)apps/meteor/client/components/TwoFactorModal/TwoFactorModal.tsx (1)
TwoFactorModal(28-44)apps/meteor/client/lib/toast.ts (1)
dispatchToastMessage(22-25)
packages/api-client/src/index.ts (1)
apps/meteor/client/lib/2fa/challenge2fa.ts (1)
challenge2fa(89-161)
🔇 Additional comments (12)
packages/api-client/src/RestClientInterface.ts (1)
89-91: LGTM!packages/api-client/src/index.ts (3)
16-16: LGTM!Also applies to: 54-54
222-250: LGTM!
303-305: LGTM!apps/meteor/client/lib/2fa/challenge2fa.ts (4)
1-18: LGTM!
23-56: LGTM!
58-82: LGTM!
89-161: LGTM!apps/meteor/client/lib/2fa/process2faReturn.ts (2)
91-91: LGTM!
140-147: LGTM!apps/meteor/app/utils/client/lib/RestApiClient.ts (1)
4-4: LGTM!Also applies to: 31-31
packages/api-client/src/errors.ts (1)
3-15: LGTM!Also applies to: 32-37, 41-47
There was a problem hiding this comment.
3 issues found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/api-client/src/index.ts">
<violation number="1" location="packages/api-client/src/index.ts:222">
P1: Guard `error` before using `'details' in error`; this line can throw a TypeError for non-object 400 payloads.</violation>
</file>
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
| 'x-2fa-method': method2fa, | ||
| }, | ||
| }); | ||
| const method2fa = 'details' in error ? error.details.method : 'password'; |
There was a problem hiding this comment.
P1: Guard error before using 'details' in error; this line can throw a TypeError for non-object 400 payloads.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/api-client/src/index.ts, line 222:
<comment>Guard `error` before using `'details' in error`; this line can throw a TypeError for non-object 400 payloads.</comment>
<file context>
@@ -223,28 +219,35 @@ export class RestClient implements RestClientInterface {
- 'x-2fa-method': method2fa,
- },
- });
+ const method2fa = 'details' in error ? error.details.method : 'password';
+
+ if (!this.challenge2fa) {
</file context>
| const method2fa = 'details' in error ? error.details.method : 'password'; | |
| const method2fa = | |
| typeof error === 'object' && | |
| error !== null && | |
| 'details' in error && | |
| typeof (error as { details?: { method?: unknown } }).details?.method === 'string' | |
| ? ((error as { details: { method: 'totp' | 'email' | 'password' } }).details.method as 'totp' | 'email' | 'password') | |
| : 'password'; |
|
🟡 Medium: Automatic Email Verification Bypass during OAuth User Merge In the Steps to Reproduce
Tracegraph TD
subgraph SG0 ["./Rocket.Chat/apps/meteor/app/custom-oauth/server/custom_oauth_server.js"]
._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js{{"Initializes and configures custom OAuth service providers, handling identity fetching and user account linking."}}
end
style SG0 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG1 ["./Rocket.Chat/apps/meteor/app/dolphin/server/lib.ts"]
._Rocket.Chat_apps_meteor_app_dolphin_server_lib.ts["Defines the Dolphin OAuth integration setup and configuration."]
end
style SG1 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG2 ["./Rocket.Chat/apps/meteor/app/drupal/server/lib.ts"]
._Rocket.Chat_apps_meteor_app_drupal_server_lib.ts["Defines the Drupal OAuth integration setup."]
end
style SG2 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG3 ["./Rocket.Chat/apps/meteor/app/gitlab/server/lib.ts"]
._Rocket.Chat_apps_meteor_app_gitlab_server_lib.ts["Module-level initialization for Gitlab OAuth integration."]
end
style SG3 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG4 ["./Rocket.Chat/apps/meteor/app/nextcloud/server/lib.ts"]
._Rocket.Chat_apps_meteor_app_nextcloud_server_lib.ts["Configures Nextcloud OAuth integration."]
end
style SG4 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG5 ["./Rocket.Chat/apps/meteor/app/wordpress/server/lib.ts"]
._Rocket.Chat_apps_meteor_app_wordpress_server_lib.ts["Top-level module initialization for WordPress OAuth integration, including configuration setup and settings watching."]
end
style SG5 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG6 ["./Rocket.Chat/apps/meteor/client/lib/e2ee/logger.ts"]
._Rocket.Chat_apps_meteor_client_lib_e2ee_logger.ts["Initializes global logging configurations and defines the Logger and Span classes."]
end
style SG6 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG7 ["./Rocket.Chat/apps/meteor/server/lib/oauth/updateOAuthServices.ts"]
updateOAuthServices["Updates OAuth service configurations based on application settings."]
end
style SG7 fill:#2a2a2a,stroke:#444,color:#aaa
._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js --> ._Rocket.Chat_apps_meteor_client_lib_e2ee_logger.ts
._Rocket.Chat_apps_meteor_app_wordpress_server_lib.ts --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
updateOAuthServices --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
._Rocket.Chat_apps_meteor_app_dolphin_server_lib.ts --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
._Rocket.Chat_apps_meteor_app_drupal_server_lib.ts --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
._Rocket.Chat_apps_meteor_app_gitlab_server_lib.ts --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
._Rocket.Chat_apps_meteor_app_nextcloud_server_lib.ts --> ._Rocket.Chat_apps_meteor_app_custom-oauth_server_custom_oauth_server.js
Fix with AITriage: Reply |
|
🟡 Medium: Missing Re-authentication for 2FA Enablement The Steps to Reproduce
Tracegraph TD
subgraph SG0 ["./Rocket.Chat/apps/meteor/app/2fa/server/code/index.ts"]
getUserForCheck["getUserForCheck"]
end
style SG0 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG1 ["./Rocket.Chat/apps/meteor/app/2fa/server/functions/resetTOTP.ts"]
sendResetNotification["sendResetNotification"]
resetTOTP["Resets the TOTP configuration for a user, including notifying the user via email."]
end
style SG1 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG2 ["./Rocket.Chat/apps/meteor/app/api/server/ApiClass.ts"]
APIClass.success["APIClass.success"]
APIClass.failure["APIClass.failure"]
APIClass.forbidden["APIClass.forbidden"]
APIClass.this.parseJsonQuery["APIClass.this.parseJsonQuery"]
end
style SG2 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG3 ["./Rocket.Chat/apps/meteor/app/api/server/helpers/getPaginationItems.ts"]
getPaginationItems["Calculates and validates pagination offset and count parameters based on API settings."]
end
style SG3 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG4 ["./Rocket.Chat/apps/meteor/app/api/server/helpers/getUserFromParams.ts"]
getUserFromParams["Retrieves user record from provided request parameters (userId/username)."]
end
style SG4 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG5 ["./Rocket.Chat/apps/meteor/app/api/server/helpers/getUserInfo.ts"]
isVerifiedEmail["isVerifiedEmail"]
getUserPreferences["getUserPreferences"]
filterOutdatedVersionUpdateBanners["filterOutdatedVersionUpdateBanners"]
getUserCalendar["getUserCalendar"]
getUserInfo["Retrieves and filters user information, including preferences, calendar settings, and service status for API responses."]
end
style SG5 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG6 ["./Rocket.Chat/apps/meteor/app/api/server/helpers/isUserFromParams.ts"]
isUserFromParams["Determines if the user specified in params matches the logged-in user."]
end
style SG6 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG7 ["./Rocket.Chat/apps/meteor/app/api/server/lib/eraseTeam.ts"]
eraseTeamOnRelinquishRoomOwnerships["eraseTeamOnRelinquishRoomOwnerships"]
eraseRoomLooseValidation["eraseRoomLooseValidation"]
end
style SG7 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG8 ["./Rocket.Chat/apps/meteor/app/api/server/lib/getUploadFormData.ts"]
getUploadFormData["Extracts and validates form data from a multipart upload request."]
end
style SG8 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG9 ["./Rocket.Chat/apps/meteor/app/api/server/lib/isValidQuery.ts"]
._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts["Validates query objects against allowed attributes and operations."]
end
style SG9 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG10 ["./Rocket.Chat/apps/meteor/app/api/server/lib/users.ts"]
findUsersToAutocomplete["Finds users for autocomplete based on search term and conditions."]
getInclusiveFields["Extracts inclusive fields from a query object."]
getNonEmptyFields["Returns default fields if input fields are empty."]
getNonEmptyQuery["Returns a default query if input query is empty."]
findPaginatedUsersByStatus["Finds paginated users based on status and other criteria."]
end
style SG10 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG11 ["./Rocket.Chat/apps/meteor/app/api/server/v1/users.ts"]
get["get"]
action{{"action"}}
end
style SG11 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG12 ["./Rocket.Chat/apps/meteor/app/authentication/server/startup/index.js"]
Accounts.insertUserDoc["Accounts.insertUserDoc"]
end
style SG12 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG13 ["./Rocket.Chat/apps/meteor/app/authorization/server/functions/hasPermission.ts"]
hasPermissionAsync["Checks if a user has a specific permission within a given scope."]
end
style SG13 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG14 ["./Rocket.Chat/apps/meteor/app/authorization/server/index.ts"]
._Rocket.Chat_apps_meteor_app_authorization_server_index.ts["Exports authorization-related functions and registers server-side methods for permission management."]
end
style SG14 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG15 ["./Rocket.Chat/apps/meteor/app/file/server/file.server.ts"]
RocketChatFile.dataURIParse["RocketChatFile.dataURIParse"]
end
style SG15 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG16 ["./Rocket.Chat/apps/meteor/app/invites/server/functions/validateInviteToken.ts"]
validateInviteToken["validateInviteToken"]
end
style SG16 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG17 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts"]
addUserToDefaultChannels["addUserToDefaultChannels"]
end
style SG17 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG18 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/addUserToRoom.ts"]
addUserToRoom["addUserToRoom"]
end
style SG18 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG19 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/checkEmailAvailability.ts"]
checkEmailAvailability["Checks if an email address is available in the system."]
end
style SG19 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG20 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts"]
toRegExp["toRegExp"]
usernameIsBlocked["usernameIsBlocked"]
checkUsernameAvailabilityWithValidation["Checks username availability with validation checks against user state."]
checkUsernameAvailability["Checks if a username is available or blocked."]
end
style SG20 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG21 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/deleteRoom.ts"]
deleteRoom["deleteRoom"]
end
style SG21 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG22 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/deleteUser.ts"]
deleteUser["Deletes a user account, managing room ownership, message erasure, and associated data cleanup."]
end
style SG22 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG23 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts"]
getAvatarSuggestionForUser["Suggests and fetches avatar images for a user from various providers."]
end
style SG23 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG24 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getDefaultChannels.ts"]
getDefaultChannels["getDefaultChannels"]
end
style SG24 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG25 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getFullUserData.ts"]
getCustomFields["getCustomFields"]
getFields["getFields"]
findTargetUser["findTargetUser"]
getFullUserDataByUniqueSearchTerm["Retrieves full user data based on a unique search term, enforcing permission checks."]
end
style SG25 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG26 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts"]
shouldRemoveOrChangeOwner["Checks if any rooms require ownership changes or removal."]
getSubscribedRoomsForUserWithDetails["Retrieves rooms a user is subscribed to and determines ownership status for each."]
end
style SG26 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG27 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getUserSingleOwnedRooms.ts"]
getUserSingleOwnedRooms["Identifies rooms where the user is the sole owner."]
end
style SG27 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG28 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts"]
slug["slug"]
usernameIsAvailable["usernameIsAvailable"]
name["name"]
generateUsernameSuggestion["Generates a unique username suggestion based on user profile information."]
end
style SG28 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG29 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/joinDefaultChannels.ts"]
joinDefaultChannels["joinDefaultChannels"]
end
style SG29 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG30 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts"]
bulkTeamCleanup["bulkTeamCleanup"]
bulkRoomCleanUp["Performs bulk cleanup of rooms, including removing files, subscriptions, and messages."]
relinquishRoomOwnerships["Relinquishes room ownerships for a user, potentially reassigning ownership or removing rooms."]
end
style SG30 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG31 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveCustomFields.ts"]
saveCustomFields["Validates and saves custom user fields."]
end
style SG31 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG32 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts"]
getCustomFieldsMeta["getCustomFieldsMeta"]
saveCustomFieldsWithoutValidation["Saves custom user fields to the database without performing schema validation."]
end
style SG32 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG33 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveUser/index.ts"]
._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts["Exports functions for user saving and validation."]
end
style SG33 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG34 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveUser/sendUserEmail.ts"]
sendUserEmail["sendUserEmail"]
sendWelcomeEmail["Sends a welcome email to a newly created user."]
end
style SG34 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG35 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts"]
canEditExtension["Checks if a VoIP extension is available for assignment to a user."]
end
style SG35 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG36 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/saveUserIdentity.ts"]
saveUserIdentity["saveUserIdentity"]
updateUsernameReferences["updateUsernameReferences"]
end
style SG36 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG37 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/setRealName.ts"]
setRealName["setRealName"]
end
style SG37 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG38 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/setStatusText.ts"]
setStatusText["Updates a user's status text and broadcasts the change to other users."]
end
style SG38 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG39 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/setUserAvatar.ts"]
setAvatarFromServiceWithValidation["setAvatarFromServiceWithValidation"]
setUserAvatar["Sets a user's avatar from a data URI, URL, or raw buffer, performing validation and database updates."]
end
style SG39 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG40 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/setUsername.ts"]
isUserInFederatedRooms["isUserInFederatedRooms"]
setUsernameWithValidation["Sets a user's username after performing various validations and checks."]
setUsername["_setUsername"]
end
style SG40 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG41 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/updateGroupDMsName.ts"]
getFname["getFname"]
getName["getName"]
getUsersWhoAreInTheSameGroupDMsAs["getUsersWhoAreInTheSameGroupDMsAs"]
updateGroupDMsName["updateGroupDMsName"]
getMembers["getMembers"]
end
style SG41 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG42 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/validateCustomFields.js"]
validateCustomFields["Validates user-provided custom fields against configured metadata."]
end
style SG42 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG43 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/validateName.ts"]
validateName["validateName"]
end
style SG43 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG44 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/validateNameChars.ts"]
validateNameChars["Validates that a name string does not contain invalid characters."]
end
style SG44 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG45 ["./Rocket.Chat/apps/meteor/app/lib/server/functions/validateUsername.ts"]
validateUsername["Validates a username against configured regex patterns."]
end
style SG45 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG46 ["./Rocket.Chat/apps/meteor/app/lib/server/index.ts"]
._Rocket.Chat_apps_meteor_app_lib_server_index.ts["./Rocket.Chat/apps/meteor/app/lib/server/index.ts"]
end
style SG46 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG47 ["./Rocket.Chat/apps/meteor/app/lib/server/lib/notifyListener.ts"]
notifyOnRoomChangedById["Broadcasts a notification that a room has changed, identified by ID."]
notifyOnRoomChangedByUsernamesOrUids["notifyOnRoomChangedByUsernamesOrUids"]
notifyOnIntegrationChangedByUserId["notifyOnIntegrationChangedByUserId"]
notifyOnLivechatDepartmentAgentChanged["notifyOnLivechatDepartmentAgentChanged"]
notifyOnSettingChanged["Broadcasts a notification that a system setting has changed."]
notifyOnSettingChangedById["notifyOnSettingChangedById"]
notifyOnUserChange["Broadcasts a notification about a user change event."]
notifyOnUserChangeAsync["Executes a callback and broadcasts user change notifications based on the result."]
notifyOnSubscriptionChanged["notifyOnSubscriptionChanged"]
notifyOnSubscriptionChangedByRoomIdAndUserId["notifyOnSubscriptionChangedByRoomIdAndUserId"]
notifyOnSubscriptionChangedById["notifyOnSubscriptionChangedById"]
notifyOnSubscriptionChangedByUserPreferences["notifyOnSubscriptionChangedByUserPreferences"]
notifyOnSubscriptionChangedByRoomId["notifyOnSubscriptionChangedByRoomId"]
notifyOnSubscriptionChangedByAutoTranslateAndUserId["notifyOnSubscriptionChangedByAutoTranslateAndUserId"]
notifyOnSubscriptionChangedByUserIdAndRoomType["notifyOnSubscriptionChangedByUserIdAndRoomType"]
notifyOnSubscriptionChangedByNameAndRoomType["Broadcasts a notification that subscriptions matching a name and room type have changed."]
notifyOnSubscriptionChangedByUserId["notifyOnSubscriptionChangedByUserId"]
end
style SG47 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG48 ["./Rocket.Chat/apps/meteor/app/lib/server/methods/createToken.ts"]
generateAccessToken["Generates an access token for a user if the provided secret is valid."]
end
style SG48 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG49 ["./Rocket.Chat/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts"]
deleteUserOwnAccount["Deletes the current user's account after verifying their password."]
end
style SG49 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG50 ["./Rocket.Chat/apps/meteor/app/mailer/server/api.ts"]
replacekey["replacekey"]
translate["translate"]
replace["Replaces placeholders in a string with values from the provided data object."]
replaceEscaped["replaceEscaped"]
wrap["wrap"]
checkAddressFormat["Validates the format of one or more email addresses."]
sendNoWrap["Sends an email without applying standard layout wrapping, triggering pre-send events."]
send["send"]
end
style SG50 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG51 ["./Rocket.Chat/apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts"]
getDefaultSubscriptionPref["getDefaultSubscriptionPref"]
end
style SG51 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG52 ["./Rocket.Chat/apps/meteor/app/utils/lib/getURL.ts"]
getCloudUrl["getCloudUrl"]
getURL_2["_getURL"]
getURLWithoutSettings["getURLWithoutSettings"]
end
style SG52 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG53 ["./Rocket.Chat/apps/meteor/app/utils/lib/mimeTypes.ts"]
getMimeTypeFromFileName["getMimeTypeFromFileName"]
getMimeType["getMimeType"]
end
style SG53 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG54 ["./Rocket.Chat/apps/meteor/app/utils/server/functions/isSMTPConfigured.ts"]
isSMTPConfigured["Checks if SMTP configuration is set in environment or settings."]
end
style SG54 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG55 ["./Rocket.Chat/apps/meteor/app/utils/server/getURL.ts"]
getURL["Constructs a URL based on the provided path and configuration settings."]
end
style SG55 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG56 ["./Rocket.Chat/apps/meteor/app/utils/server/lib/getUserPreference.ts"]
getUserPreference["getUserPreference"]
end
style SG56 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG57 ["./Rocket.Chat/apps/meteor/client/meteor/overrides/userAndUsers.ts"]
Meteor.userId["Meteor.userId"]
end
style SG57 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG58 ["./Rocket.Chat/apps/meteor/client/meteor/user.ts"]
watchUserId["watchUserId"]
end
style SG58 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG59 ["./Rocket.Chat/apps/meteor/client/meteor/watch.ts"]
watch["watch"]
end
style SG59 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG60 ["./Rocket.Chat/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts"]
generatePersonalAccessTokenOfUser["Generates a new personal access token for a user and stores it in the database."]
end
style SG60 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG61 ["./Rocket.Chat/apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts"]
regeneratePersonalAccessTokenOfUser["Regenerates a user's personal access token by removing the old one and creating a new one."]
end
style SG61 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG62 ["./Rocket.Chat/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts"]
removePersonalAccessTokenOfUser["Removes a user's personal access token from the database."]
end
style SG62 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG63 ["./Rocket.Chat/apps/meteor/lib/roles/calculateRoomRolePriorityFromRoles.ts"]
calculateRoomRolePriorityFromRoles["calculateRoomRolePriorityFromRoles"]
end
style SG63 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG64 ["./Rocket.Chat/apps/meteor/lib/utils/isObject.ts"]
isObject["isObject"]
end
style SG64 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG65 ["./Rocket.Chat/apps/meteor/lib/utils/parseCSV.ts"]
parseCSV["Parses a comma-separated string into an array of trimmed items, optionally removing empty values."]
end
style SG65 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG66 ["./Rocket.Chat/apps/meteor/lib/utils/stringUtils.ts"]
makeString["makeString"]
defaultToWhiteSpace["defaultToWhiteSpace"]
trim["Trims whitespace or specified characters from both ends of a string."]
ltrim["Trims whitespace or specified characters from the left side of a string."]
rtrim["Trims whitespace or specified characters from the right side of a string."]
strLeft["strLeft"]
strRightBack["strRightBack"]
end
style SG66 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG67 ["./Rocket.Chat/apps/meteor/server/database/utils.ts"]
isExtendedSession["isExtendedSession"]
onceTransactionCommitedSuccessfully["onceTransactionCommitedSuccessfully"]
withError["withError"]
end
style SG67 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG68 ["./Rocket.Chat/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts"]
getSubscriptionAutotranslateDefaultConfig["getSubscriptionAutotranslateDefaultConfig"]
end
style SG68 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG69 ["./Rocket.Chat/apps/meteor/server/lib/isUserIdFederated.ts"]
isUserIdFederated["isUserIdFederated"]
end
style SG69 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG70 ["./Rocket.Chat/apps/meteor/server/lib/resetUserE2EKey.ts"]
sendResetNotification_2["sendResetNotification"]
resetUserE2EEncriptionKey["Resets a user's E2E encryption key, notifies the user, and forces a logout."]
end
style SG70 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG71 ["./Rocket.Chat/apps/meteor/server/lib/roles/addUserRoles.ts"]
addUserRolesAsync["Adds roles to a user, optionally scoped to a specific room, with validation."]
end
style SG71 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG72 ["./Rocket.Chat/apps/meteor/server/lib/roles/syncRoomRolePriority.ts"]
syncRoomRolePriorityForUserAndRoom["syncRoomRolePriorityForUserAndRoom"]
updateRolePriority["updateRolePriority"]
end
style SG72 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG73 ["./Rocket.Chat/apps/meteor/server/lib/roles/validateRoleList.ts"]
validateRoleList["Validates a list of role IDs against the database."]
end
style SG73 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG74 ["./Rocket.Chat/apps/meteor/server/lib/rooms/roomCoordinator.ts"]
RoomCoordinatorServer.allowMemberAction["RoomCoordinatorServer.allowMemberAction"]
RoomCoordinatorServer.getRoomDirectives["RoomCoordinatorServer.getRoomDirectives"]
end
style SG74 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG75 ["./Rocket.Chat/apps/meteor/server/methods/registerUser.ts"]
registerUser["Registers a new user account with provided credentials and optional metadata."]
end
style SG75 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG76 ["./Rocket.Chat/apps/meteor/server/methods/requestDataDownload.ts"]
requestDataDownload["Initiates a data export operation for a user."]
end
style SG76 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG77 ["./Rocket.Chat/apps/meteor/server/methods/resetAvatar.ts"]
resetAvatar["Resets a user's avatar, requiring appropriate permissions."]
userId["userId"]
end
style SG77 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG78 ["./Rocket.Chat/apps/meteor/server/methods/saveUserPreferences.ts"]
updateNotificationPreferences["updateNotificationPreferences"]
saveUserPreferences["Updates user preferences and propagates changes to notification settings."]
end
style SG78 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG79 ["./Rocket.Chat/apps/meteor/server/methods/sendConfirmationEmail.ts"]
sendConfirmationEmail["Sends a verification email to a user identified by their email address."]
end
style SG79 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG80 ["./Rocket.Chat/apps/meteor/server/methods/sendForgotPasswordEmail.ts"]
sendForgotPasswordEmail["Sends a password reset email to a user identified by their email address."]
end
style SG80 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG81 ["./Rocket.Chat/apps/meteor/server/methods/setUserActiveStatus.ts"]
executeSetUserActiveStatus["Executes the logic to change a user's active status, verifying permissions."]
setUserActiveStatus["setUserActiveStatus"]
end
style SG81 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG82 ["./Rocket.Chat/apps/meteor/server/services/authorization/service.ts"]
Authorization.hasPermission["Authorization.hasPermission"]
Authorization.all["Authorization.all"]
end
style SG82 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG83 ["./Rocket.Chat/apps/meteor/server/services/user/lib/getNewUserRoles.ts"]
getNewUserRoles["Determines the default roles for a new user based on settings."]
end
style SG83 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG84 ["./Rocket.Chat/ee/apps/account-service/src/lib/utils.ts"]
generateStampedLoginToken["Generates a stamped login token."]
hashLoginToken["Hashes a login token for storage."]
end
style SG84 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG85 ["./Rocket.Chat/packages/models/src/models/BaseRaw.ts"]
BaseRaw.doNotMixInclusionAndExclusionFields["BaseRaw.doNotMixInclusionAndExclusionFields"]
BaseRaw.ensureDefaultFields["BaseRaw.ensureDefaultFields"]
BaseRaw.find["BaseRaw.find"]
BaseRaw.updateOne["Updates a record in the collection and ensures the 'updatedAt' field is set."]
BaseRaw.updateMany["BaseRaw.updateMany"]
BaseRaw.deleteMany["BaseRaw.deleteMany"]
BaseRaw.setUpdatedAt["Internal helper to update the 'updatedAt' field on a record before persistence."]
end
style SG85 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG86 ["./Rocket.Chat/packages/models/src/models/LivechatDepartmentAgents.ts"]
LivechatDepartmentAgentsRaw.findByAgentId["LivechatDepartmentAgentsRaw.findByAgentId"]
LivechatDepartmentAgentsRaw.removeByAgentId["LivechatDepartmentAgentsRaw.removeByAgentId"]
LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId["LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId"]
end
style SG86 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG87 ["./Rocket.Chat/packages/sha256/src/binb2hex.ts"]
binb2hex["Converts a bit-packed number array to a hexadecimal string."]
end
style SG87 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG88 ["./Rocket.Chat/packages/sha256/src/core.ts"]
safeAdd["safeAdd"]
s["s"]
r["r"]
ch["ch"]
maj["maj"]
sigma0256["sigma0256"]
sigma1256["sigma1256"]
gamma0256["gamma0256"]
gamma1256["gamma1256"]
core["Core SHA-256 algorithm implementation."]
end
style SG88 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG89 ["./Rocket.Chat/packages/sha256/src/sha256.ts"]
SHA256["Main entry point for SHA-256 hashing."]
end
style SG89 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG90 ["./Rocket.Chat/packages/sha256/src/str2binb.ts"]
str2binb["Converts a string to a bit-packed number array for hashing."]
end
style SG90 fill:#2a2a2a,stroke:#444,color:#aaa
subgraph SG91 ["./Rocket.Chat/packages/sha256/src/utf8Encode.ts"]
utf8Encode["Encodes a string into UTF-8 format."]
end
style SG91 fill:#2a2a2a,stroke:#444,color:#aaa
action --> isSMTPConfigured
action --> resetTOTP
action --> getPaginationItems
action --> getUserFromParams
action --> isUserFromParams
action --> getUserInfo
action --> registerUser
action --> requestDataDownload
action --> executeSetUserActiveStatus
action --> saveUserPreferences
action --> sendForgotPasswordEmail
action --> sendConfirmationEmail
action --> resetAvatar
action --> getUploadFormData
action --> findUsersToAutocomplete
action --> findPaginatedUsersByStatus
action --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
action --> hasPermissionAsync
action --> resetUserE2EEncriptionKey
action --> validateNameChars
action --> sendWelcomeEmail
action --> ._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts
action --> canEditExtension
action --> setUsernameWithValidation
action --> saveCustomFields
action --> saveCustomFieldsWithoutValidation
action --> deleteUser
action --> generateUsernameSuggestion
action --> getAvatarSuggestionForUser
action --> generatePersonalAccessTokenOfUser
action --> regeneratePersonalAccessTokenOfUser
action --> removePersonalAccessTokenOfUser
action --> getFullUserDataByUniqueSearchTerm
action --> checkEmailAvailability
action --> checkUsernameAvailabilityWithValidation
action --> checkUsernameAvailability
action --> validateUsername
action --> setStatusText
action --> validateCustomFields
action --> setUserAvatar
action --> deleteUserOwnAccount
action --> notifyOnUserChange
action --> notifyOnUserChangeAsync
action --> hashLoginToken
action --> getUserForCheck
action --> APIClass.success
action --> APIClass.failure
action --> APIClass.forbidden
action --> APIClass.this.parseJsonQuery
action --> get
action --> generateAccessToken
resetTOTP --> sendResetNotification
resetTOTP --> isUserIdFederated
resetTOTP --> notifyOnUserChange
getUserInfo --> getURL
getUserInfo --> isVerifiedEmail
getUserInfo --> getUserPreferences
getUserInfo --> filterOutdatedVersionUpdateBanners
getUserInfo --> getUserCalendar
registerUser --> validateInviteToken
registerUser --> Accounts.insertUserDoc
registerUser --> registerUser
registerUser --> ._Rocket.Chat_apps_meteor_app_lib_server_index.ts
registerUser --> trim
registerUser --> generateStampedLoginToken
requestDataDownload --> requestDataDownload
executeSetUserActiveStatus --> setUserActiveStatus
executeSetUserActiveStatus --> hasPermissionAsync
saveUserPreferences --> updateNotificationPreferences
saveUserPreferences --> saveUserPreferences
saveUserPreferences --> Meteor.userId
saveUserPreferences --> notifyOnUserChange
saveUserPreferences --> notifyOnSubscriptionChangedByAutoTranslateAndUserId
saveUserPreferences --> notifyOnSubscriptionChangedByUserId
sendForgotPasswordEmail --> sendForgotPasswordEmail
resetAvatar --> resetAvatar
resetAvatar --> userId
resetAvatar --> hasPermissionAsync
getUploadFormData --> getMimeType
findUsersToAutocomplete --> hasPermissionAsync
findPaginatedUsersByStatus --> hasPermissionAsync
hasPermissionAsync --> Authorization.hasPermission
resetUserE2EEncriptionKey --> isUserIdFederated
resetUserE2EEncriptionKey --> sendResetNotification_2
resetUserE2EEncriptionKey --> notifyOnUserChange
resetUserE2EEncriptionKey --> notifyOnSubscriptionChangedByUserId
sendWelcomeEmail --> sendUserEmail
setUsernameWithValidation --> saveUserIdentity
setUsernameWithValidation --> isUserInFederatedRooms
setUsernameWithValidation --> checkUsernameAvailability
setUsernameWithValidation --> joinDefaultChannels
setUsernameWithValidation --> validateUsername
setUsernameWithValidation --> notifyOnUserChange
saveCustomFields --> saveCustomFieldsWithoutValidation
saveCustomFields --> validateCustomFields
saveCustomFields --> trim
saveCustomFieldsWithoutValidation --> getCustomFieldsMeta
saveCustomFieldsWithoutValidation --> onceTransactionCommitedSuccessfully
saveCustomFieldsWithoutValidation --> notifyOnSubscriptionChangedByUserIdAndRoomType
saveCustomFieldsWithoutValidation --> trim
deleteUser --> LivechatDepartmentAgentsRaw.findByAgentId
deleteUser --> LivechatDepartmentAgentsRaw.removeByAgentId
deleteUser --> getUserSingleOwnedRooms
deleteUser --> updateGroupDMsName
deleteUser --> shouldRemoveOrChangeOwner
deleteUser --> getSubscribedRoomsForUserWithDetails
deleteUser --> relinquishRoomOwnerships
deleteUser --> notifyOnRoomChangedById
deleteUser --> notifyOnIntegrationChangedByUserId
deleteUser --> notifyOnLivechatDepartmentAgentChanged
deleteUser --> notifyOnUserChange
generateUsernameSuggestion --> slug
generateUsernameSuggestion --> usernameIsAvailable
generateUsernameSuggestion --> name
generatePersonalAccessTokenOfUser --> hasPermissionAsync
generatePersonalAccessTokenOfUser --> hashLoginToken
regeneratePersonalAccessTokenOfUser --> hasPermissionAsync
regeneratePersonalAccessTokenOfUser --> generatePersonalAccessTokenOfUser
regeneratePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
removePersonalAccessTokenOfUser --> hasPermissionAsync
removePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
getFullUserDataByUniqueSearchTerm --> hasPermissionAsync
getFullUserDataByUniqueSearchTerm --> getFields
getFullUserDataByUniqueSearchTerm --> findTargetUser
checkUsernameAvailabilityWithValidation --> checkUsernameAvailability
checkUsernameAvailability --> toRegExp
checkUsernameAvailability --> usernameIsBlocked
checkUsernameAvailability --> validateName
setStatusText --> onceTransactionCommitedSuccessfully
validateCustomFields --> trim
setUserAvatar --> RocketChatFile.dataURIParse
setUserAvatar --> onceTransactionCommitedSuccessfully
deleteUserOwnAccount --> SHA256
deleteUserOwnAccount --> deleteUser
deleteUserOwnAccount --> deleteUserOwnAccount
deleteUserOwnAccount --> Meteor.userId
deleteUserOwnAccount --> trim
notifyOnUserChangeAsync --> notifyOnUserChange
APIClass.success --> isObject
APIClass.failure --> isObject
APIClass.this.parseJsonQuery --> APIClass.this.parseJsonQuery
get --> getURL
get --> getPaginationItems
get --> getUserFromParams
get --> getInclusiveFields
get --> getNonEmptyFields
get --> getNonEmptyQuery
get --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
get --> hasPermissionAsync
get --> APIClass.success
get --> APIClass.forbidden
get --> APIClass.this.parseJsonQuery
get --> get
generateAccessToken --> generateStampedLoginToken
sendResetNotification --> send
getURL --> getURLWithoutSettings
getUserPreferences --> getUserPreference
Accounts.insertUserDoc --> getNewUserRoles
Accounts.insertUserDoc --> addUserRolesAsync
Accounts.insertUserDoc --> getAvatarSuggestionForUser
Accounts.insertUserDoc --> joinDefaultChannels
Accounts.insertUserDoc --> setAvatarFromServiceWithValidation
Accounts.insertUserDoc --> notifyOnSettingChangedById
Accounts.insertUserDoc --> parseCSV
trim --> makeString
trim --> defaultToWhiteSpace
setUserActiveStatus --> executeSetUserActiveStatus
setUserActiveStatus --> Meteor.userId
updateNotificationPreferences --> notifyOnSubscriptionChangedByUserPreferences
Meteor.userId --> watchUserId
getMimeType --> getMimeTypeFromFileName
Authorization.hasPermission --> Authorization.all
sendResetNotification_2 --> send
sendUserEmail --> send
saveUserIdentity --> updateUsernameReferences
saveUserIdentity --> setUsername
saveUserIdentity --> onceTransactionCommitedSuccessfully
saveUserIdentity --> setRealName
saveUserIdentity --> validateName
joinDefaultChannels --> addUserToDefaultChannels
onceTransactionCommitedSuccessfully --> isExtendedSession
onceTransactionCommitedSuccessfully --> withError
LivechatDepartmentAgentsRaw.findByAgentId --> BaseRaw.find
LivechatDepartmentAgentsRaw.removeByAgentId --> BaseRaw.deleteMany
updateGroupDMsName --> getFname
updateGroupDMsName --> getName
updateGroupDMsName --> getUsersWhoAreInTheSameGroupDMsAs
updateGroupDMsName --> getMembers
updateGroupDMsName --> notifyOnSubscriptionChangedByRoomId
getSubscribedRoomsForUserWithDetails --> ._Rocket.Chat_apps_meteor_app_authorization_server_index.ts
relinquishRoomOwnerships --> addUserRolesAsync
relinquishRoomOwnerships --> bulkRoomCleanUp
name --> slug
getFields --> getCustomFields
SHA256 --> utf8Encode
SHA256 --> str2binb
SHA256 --> binb2hex
SHA256 --> core
send --> replace
send --> wrap
send --> sendNoWrap
getURLWithoutSettings --> getURL_2
getNewUserRoles --> parseCSV
addUserRolesAsync --> validateRoleList
addUserRolesAsync --> syncRoomRolePriorityForUserAndRoom
addUserRolesAsync --> notifyOnSubscriptionChangedByRoomIdAndUserId
setAvatarFromServiceWithValidation --> hasPermissionAsync
setAvatarFromServiceWithValidation --> setUserAvatar
defaultToWhiteSpace --> makeString
watchUserId --> watch
updateUsernameReferences --> LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId
updateUsernameReferences --> updateGroupDMsName
updateUsernameReferences --> notifyOnRoomChangedByUsernamesOrUids
updateUsernameReferences --> notifyOnSubscriptionChangedByNameAndRoomType
updateUsernameReferences --> notifyOnSubscriptionChangedByUserId
setUsername --> isUserInFederatedRooms
setUsername --> onceTransactionCommitedSuccessfully
setUsername --> getAvatarSuggestionForUser
setUsername --> checkUsernameAvailability
setUsername --> validateUsername
setUsername --> addUserToRoom
setUsername --> setUserAvatar
setRealName --> onceTransactionCommitedSuccessfully
addUserToDefaultChannels --> getDefaultSubscriptionPref
addUserToDefaultChannels --> getSubscriptionAutotranslateDefaultConfig
addUserToDefaultChannels --> getDefaultChannels
addUserToDefaultChannels --> notifyOnSubscriptionChangedById
BaseRaw.find --> BaseRaw.doNotMixInclusionAndExclusionFields
BaseRaw.find --> BaseRaw.find
BaseRaw.deleteMany --> BaseRaw.find
BaseRaw.deleteMany --> BaseRaw.updateOne
BaseRaw.deleteMany --> BaseRaw.deleteMany
bulkRoomCleanUp --> eraseRoomLooseValidation
bulkRoomCleanUp --> bulkTeamCleanup
bulkRoomCleanUp --> notifyOnSubscriptionChanged
core --> safeAdd
core --> ch
core --> maj
core --> sigma0256
core --> sigma1256
core --> gamma0256
core --> gamma1256
replace --> replacekey
replace --> translate
replace --> replace
replace --> strLeft
replace --> strRightBack
wrap --> replace
wrap --> replaceEscaped
sendNoWrap --> checkAddressFormat
sendNoWrap --> notifyOnSettingChanged
getURL_2 --> getCloudUrl
getURL_2 --> trim
getURL_2 --> ltrim
getURL_2 --> rtrim
syncRoomRolePriorityForUserAndRoom --> updateRolePriority
syncRoomRolePriorityForUserAndRoom --> calculateRoomRolePriorityFromRoles
LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId --> BaseRaw.updateMany
addUserToRoom --> RoomCoordinatorServer.allowMemberAction
addUserToRoom --> RoomCoordinatorServer.getRoomDirectives
addUserToRoom --> notifyOnRoomChangedById
addUserToRoom --> notifyOnSubscriptionChanged
BaseRaw.doNotMixInclusionAndExclusionFields --> BaseRaw.ensureDefaultFields
BaseRaw.updateOne --> BaseRaw.updateOne
BaseRaw.updateOne --> BaseRaw.setUpdatedAt
eraseRoomLooseValidation --> deleteRoom
bulkTeamCleanup --> eraseTeamOnRelinquishRoomOwnerships
sigma0256 --> s
sigma1256 --> s
gamma0256 --> s
gamma0256 --> r
gamma1256 --> s
gamma1256 --> r
replacekey --> replace
strLeft --> makeString
strRightBack --> makeString
replaceEscaped --> replace
getCloudUrl --> ltrim
getCloudUrl --> rtrim
ltrim --> makeString
ltrim --> defaultToWhiteSpace
rtrim --> makeString
rtrim --> defaultToWhiteSpace
updateRolePriority --> calculateRoomRolePriorityFromRoles
BaseRaw.updateMany --> BaseRaw.updateMany
BaseRaw.updateMany --> BaseRaw.setUpdatedAt
BaseRaw.setUpdatedAt --> BaseRaw.setUpdatedAt
deleteRoom --> notifyOnRoomChangedById
deleteRoom --> notifyOnSubscriptionChanged
eraseTeamOnRelinquishRoomOwnerships --> eraseRoomLooseValidation
Fix with AITriage: Reply |
There was a problem hiding this comment.
7 issues found across 7 files
| Severity | Count |
|---|---|
| 🔴 High | 4 |
| 🟡 Medium | 3 |
Comments Outside Diff (5)
🔴 High: Account Takeover via Unverified Email in Apple OAuth Login
Location: apps/meteor/app/apple/server/loginHandler.ts:32-35
The Apple OAuth login handler in loginHandler.ts contains a vulnerability where it trusts a client-provided email address if the Apple identity token does not contain one. Because the client-provided email field in the loginRequest is not cryptographically verified, an attacker can supply an arbitrary email address (e.g., a victim's email). The accounts_meld.js hook will then use this unverified email to look up an existing user and link the attacker's Apple service ID to the victim's account. This allows the attacker to log into the victim's account using their own Apple ID, leading to a full account takeover.
Steps to Reproduce
- The attacker initiates an Apple login using their own Apple ID, ensuring that the Apple
identityTokendoes not contain anemailclaim (e.g., by using an Apple ID that hasn't shared its email, or during a subsequent login where Apple omits the email). - The attacker intercepts the login request sent to the Rocket.Chat server (e.g., the DDP
loginmethod call). - The attacker modifies the
loginRequestpayload to include the victim's email address in theemailfield:{"identityToken": "<attacker_token>", "email": "victim@example.com", "fullName": {...}}. - The server processes the request. In
loginHandler.ts,serviceData.emailis empty, so it assignsserviceData.email = email(the victim's email). - The
accounts_meld.jshook looks up the user by the victim's email, finds the victim's account, and links the attacker's Apple ID to it (Users.setServiceId(user._id, serviceName, serviceData.id)). - The attacker is now logged into the victim's account and can access it at any time using their Apple ID.
🔴 High: Insecure Account Linking via OAuth Email Claim
Location: apps/meteor/server/configuration/accounts_meld.js:21-24
The configureAccounts function in accounts_meld.js automatically links external OAuth accounts to existing local accounts based on the serviceData.email claim. It does not independently verify the email address or consistently check if the OAuth provider has verified the email (except for meteor-developer). If an attacker authenticates via an OAuth provider that allows unverified emails (or a custom OAuth provider), they can link their OAuth account to a victim's local account and achieve account takeover. Additionally, if the victim's local account is unverified, the attacker's login will trigger a password reset on the victim's account, locking them out.
Steps to Reproduce
- Identify a target user's email address (e.g.,
victim@example.com). - Register an account on a supported OAuth provider (e.g., GitHub, GitLab, or a custom configured provider) using
victim@example.com. - Do not verify the email address on the OAuth provider if it allows unverified emails to be returned in the OAuth flow.
- Log into the Rocket.Chat instance using the OAuth provider.
- The
configureAccountslogic will extractserviceData.emailand link the OAuth account to the victim's local account. - The attacker now has access to the victim's account.
🔴 High: Missing JWT Audience Validation in Apple OAuth Login
Location: apps/meteor/app/apple/lib/handleIdentityToken.ts:30-49
The handleIdentityToken function validates the signature of the Apple identity token but fails to verify the aud (audience) claim. This allows an attacker to use a valid Apple identity token issued for a different application to authenticate against the Rocket.Chat instance. If an attacker possesses a token signed by Apple, the system will accept it as valid for the Rocket.Chat instance, potentially leading to unauthorized account access.
🔴 High: Arbitrary Username Spoofing via Dolphin OAuth Provider
Location: apps/meteor/app/dolphin/server/lib.ts:23
The DolphinOnCreateUser function, registered as a beforeCreateUser callback, directly assigns user.username using the NickName field from the external Dolphin OAuth identity provider without any validation or collision checks. An attacker with control over an account on the external Dolphin OAuth provider can set their NickName to a reserved or existing system username (e.g., 'admin'), allowing them to impersonate other users or claim privileged identities during the account creation process in Rocket.Chat.
Steps to Reproduce
- Configure a Rocket.Chat instance with the Dolphin OAuth provider enabled.
- Create or gain access to an account on the external Dolphin OAuth identity provider.
- Modify the account's profile on the Dolphin provider to set the
NickNamefield to a sensitive or reserved username (e.g., 'admin'). - Initiate the OAuth login flow on the Rocket.Chat instance using the Dolphin provider.
- Upon successful authentication, Rocket.Chat will create a new user account using the provided
NickNameas the systemusername. - Verify that the new user has been created with the 'admin' username.
🟡 Medium: Login CSRF via URL Query Parameter (resumeToken)
Location: apps/meteor/client/views/root/hooks/useLoginViaQuery.ts:10
The useLoginViaQuery hook automatically processes a resumeToken from the URL query parameters and calls loginWithToken without any user interaction or secondary validation. An attacker can craft a malicious link containing a valid resumeToken (e.g., obtained through other means or previously leaked) and trick a victim into clicking it. This results in the victim's browser session being authenticated as the user associated with that token, leading to session fixation or account switching. While the hook attempts to clean the URL after the login, the authentication has already occurred.
Steps to Reproduce
- Obtain a valid
resumeTokenfor a target user account (e.g., through log leakage, physical access, or other vulnerabilities). - Construct a URL for the Rocket.Chat instance:
https://example.rocket.chat/?resumeToken=VALID_TOKEN_HERE. - Send this link to a victim user and trick them into clicking it.
- When the victim clicks the link, their browser will load the application, the
useLoginViaQueryhook will execute, and theloginWithTokenfunction will authenticate the victim's session using the provided token. - The victim is now logged into the account associated with the token, potentially allowing the attacker to perform actions on behalf of the user or access their data.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/meteor/client/lib/2fa/challenge2fa.ts (1)
24-25: ⚡ Quick winRemove the inline implementation comments.
Please encode these constraints in the control flow or a follow-up issue instead of source comments. As per coding guidelines "Avoid code comments in the implementation".
Also applies to: 37-39
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/meteor/client/lib/2fa/challenge2fa.ts` around lines 24 - 25, Remove the inline implementation comments in apps/meteor/client/lib/2fa/challenge2fa.ts (the two comment blocks about queuing and modal deterrence around the challenge flow) and instead encode the constraints: implement or stub a simple queue/lock around the 2FA challenge invocation (e.g., a single-running-ticket or Promise-based lock used by the function that opens the modal) so concurrent challenges cannot overwrite each other, or create a follow-up issue referenced from the code (e.g., TODO with issue ID) if you cannot implement it now; ensure no explanatory implementation comments remain in the function that manages showing the modal and handling responses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/meteor/client/lib/2fa/challenge2fa.spec.tsx`:
- Around line 189-202: The test "rejects the code promise when the dialog is
closed via the X button" currently clicks the same Cancel button; update the
test to click the dialog's dismiss (close-icon) control instead so the X-button
path is exercised: after calling challenge2fa(...) and asserting the dialog is
present, target the close icon via an accessible query such as
screen.getByRole('button', { name: /close/i }) or screen.getByLabelText('Close')
(whichever matches the modal implementation) and click that, then assert the
code promise rejects with 'Two-factor_authentication_cancelled'; keep the rest
of the test and references to challenge2fa unchanged.
In `@apps/meteor/client/lib/2fa/challenge2fa.ts`:
- Around line 143-150: The current branch overwrites the stored
unresolvedChallenge (used by saveChallenge) which can leave the first caller's
code promise unresolved; modify the logic in challenge2fa.ts around
unresolvedChallenge, saveChallenge, wrapWithEndChallenge, resolveChallenge and
rejectChallenge so concurrent challenges are not lost — either implement a
simple FIFO queue of pending challenge tuples and enqueue the new {onConfirm,
rejectChallenge, onClose, rejectCode} when unresolvedChallenge exists, or
explicitly reuse/cancel the existing tuple by rejecting the prior
unresolvedChallenge (calling its rejectChallenge with a distinct cancellation
error) before replacing it; ensure returned promises map to the correct queued
tuple (do not overwrite the previously stored callbacks without
resolving/rejecting them).
---
Nitpick comments:
In `@apps/meteor/client/lib/2fa/challenge2fa.ts`:
- Around line 24-25: Remove the inline implementation comments in
apps/meteor/client/lib/2fa/challenge2fa.ts (the two comment blocks about queuing
and modal deterrence around the challenge flow) and instead encode the
constraints: implement or stub a simple queue/lock around the 2FA challenge
invocation (e.g., a single-running-ticket or Promise-based lock used by the
function that opens the modal) so concurrent challenges cannot overwrite each
other, or create a follow-up issue referenced from the code (e.g., TODO with
issue ID) if you cannot implement it now; ensure no explanatory implementation
comments remain in the function that manages showing the modal and handling
responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fff747f5-ecbc-424f-b729-1c5335ac387d
📒 Files selected for processing (3)
apps/meteor/client/lib/2fa/challenge2fa.spec.tsxapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/errors.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: 📦 Build Packages
- GitHub Check: cubic · AI code reviewer
- GitHub Check: CodeQL-Build
- GitHub Check: Hacktron Security Check
- GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)
**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation
Files:
apps/meteor/client/lib/2fa/challenge2fa.spec.tsxapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/errors.ts
🧠 Learnings (7)
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.spec.tsx
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.spec.tsx
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.spec.tsxapps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/errors.ts
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/errors.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.
Applied to files:
apps/meteor/client/lib/2fa/challenge2fa.tspackages/api-client/src/errors.ts
🧬 Code graph analysis (1)
apps/meteor/client/lib/2fa/challenge2fa.spec.tsx (1)
apps/meteor/client/lib/2fa/challenge2fa.ts (1)
challenge2fa(96-168)
🔇 Additional comments (1)
packages/api-client/src/errors.ts (1)
21-23: Make sure everytotp-*error payload includes a validdetails.method(runtime behavior)This change makes
isTotp*ErrorreturnfalseunlesshasRequiredTwoFactorMethod(error)passes, so any runtime path still forwarding only legacy{ error }/{ errorType }(withoutdetails.method) will stop matching.Most producers/fixtures already include
details.methodfortotp-required/totp-invalid/totp-max-attempts(e.g.,packages/api-client/__tests__/2fahandling.spec.ts,apps/meteor/client/lib/2fa/challenge2fa.spec.tsx, andapps/meteor/tests/e2e/account-totp.spec.ts). The remaining legacy-shaped case found ispackages/ddp-client/__tests__/Account.spec.tsemittingpageLoadLoginwith{ error: 'totp-required' }(nodetails); confirm this payload shape is not what any runtime code feeds into theisTotp*Errorpredicates.
| it('rejects the code promise when the dialog is closed via the X button', async () => { | ||
| setup(); | ||
|
|
||
| let result: ReturnType<typeof challenge2fa>; | ||
| act(() => { | ||
| result = challenge2fa({ error }); | ||
| }); | ||
| const [code] = result!; | ||
|
|
||
| expect(await screen.findByRole('dialog')).toBeInTheDocument(); | ||
| await Promise.all([ | ||
| userEvent.click(screen.getByRole('button', { name: 'Cancel' })), | ||
| expect(code).rejects.toThrow('Two-factor_authentication_cancelled'), | ||
| ]); |
There was a problem hiding this comment.
This doesn't exercise the close-icon path.
Line 200 clicks the same Cancel button as the previous test, so regressions in the modal's X-button wiring will still pass here. Please query the actual dismiss control for this case.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/meteor/client/lib/2fa/challenge2fa.spec.tsx` around lines 189 - 202, The
test "rejects the code promise when the dialog is closed via the X button"
currently clicks the same Cancel button; update the test to click the dialog's
dismiss (close-icon) control instead so the X-button path is exercised: after
calling challenge2fa(...) and asserting the dialog is present, target the close
icon via an accessible query such as screen.getByRole('button', { name: /close/i
}) or screen.getByLabelText('Close') (whichever matches the modal
implementation) and click that, then assert the code promise rejects with
'Two-factor_authentication_cancelled'; keep the rest of the test and references
to challenge2fa unchanged.
| if (unresolvedChallenge) { | ||
| // This is a retry, the modal will catch this error in order to show inline information | ||
| unresolvedChallenge.rejectChallenge(error); | ||
| saveChallenge({ onConfirm, rejectChallenge, onClose, rejectCode }); | ||
| return [code, wrapWithEndChallenge(resolveChallenge)]; | ||
| } | ||
|
|
||
| saveChallenge({ onConfirm, rejectChallenge, onClose, rejectCode }); |
There was a problem hiding this comment.
Overwriting unresolvedChallenge can strand the first caller.
packages/api-client/src/index.ts awaits the returned code promise per challenged request. If a second request hits this branch before the user submits the first modal, you replace the stored callbacks and return a new promise, but the first caller is still waiting on its old code promise, which is no longer reachable and can hang forever. Please queue concurrent challenges or explicitly reuse/cancel the existing tuple instead of overwriting it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/meteor/client/lib/2fa/challenge2fa.ts` around lines 143 - 150, The
current branch overwrites the stored unresolvedChallenge (used by saveChallenge)
which can leave the first caller's code promise unresolved; modify the logic in
challenge2fa.ts around unresolvedChallenge, saveChallenge, wrapWithEndChallenge,
resolveChallenge and rejectChallenge so concurrent challenges are not lost —
either implement a simple FIFO queue of pending challenge tuples and enqueue the
new {onConfirm, rejectChallenge, onClose, rejectCode} when unresolvedChallenge
exists, or explicitly reuse/cancel the existing tuple by rejecting the prior
unresolvedChallenge (calling its rejectChallenge with a distinct cancellation
error) before replacing it; ensure returned promises map to the correct queued
tuple (do not overwrite the previously stored callbacks without
resolving/rejecting them).
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/meteor/client/lib/2fa/challenge2fa.spec.tsx">
<violation number="1" location="apps/meteor/client/lib/2fa/challenge2fa.spec.tsx:151">
P3: This test claims to exercise the X-button dismiss path but clicks the same `Cancel` button as the preceding test. It won't catch regressions in the modal's close-icon wiring. Query the actual dismiss/close control (e.g., via an aria-label like `'Close'`) instead.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| expect(screen.getByText(title)).toBeInTheDocument(); | ||
|
|
||
| await Promise.all([ | ||
| userEvent.click(screen.getByRole('button', { name: 'Cancel' })), |
There was a problem hiding this comment.
P3: This test claims to exercise the X-button dismiss path but clicks the same Cancel button as the preceding test. It won't catch regressions in the modal's close-icon wiring. Query the actual dismiss/close control (e.g., via an aria-label like 'Close') instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/lib/2fa/challenge2fa.spec.tsx, line 151:
<comment>This test claims to exercise the X-button dismiss path but clicks the same `Cancel` button as the preceding test. It won't catch regressions in the modal's close-icon wiring. Query the actual dismiss/close control (e.g., via an aria-label like `'Close'`) instead.</comment>
<file context>
@@ -0,0 +1,253 @@
+ expect(screen.getByText(title)).toBeInTheDocument();
+
+ await Promise.all([
+ userEvent.click(screen.getByRole('button', { name: 'Cancel' })),
+ expect(code).rejects.toThrow('Two-factor_authentication_cancelled'),
+ ]);
</file context>
There was a problem hiding this comment.
The close button runs the same callback as the cancel button. I can rename the test though
There was a problem hiding this comment.
Thanks for clarifying—if both controls share the same callback, renaming the test to reflect that behavior sounds good.
|
Not ideal and too much for a regression. |
Proposed changes (including videos or screenshots)
Issue(s)
Steps to test or reproduce
Further comments
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests