Skip to content

feat: migrate batch4 client DDP callers + add 6 REST endpoints#40728

Open
ggazzo wants to merge 5 commits into
developfrom
chore/ddp-migrate-batch4
Open

feat: migrate batch4 client DDP callers + add 6 REST endpoints#40728
ggazzo wants to merge 5 commits into
developfrom
chore/ddp-migrate-batch4

Conversation

@ggazzo

@ggazzo ggazzo commented May 29, 2026

Copy link
Copy Markdown
Member

Summary

Continues the DDP→REST sweep started in #40659, #40711, #40675, #40724. This batch tackles the next set of client DDP callers whose endpoints didn't exist yet. The DDP methods stay registered for external SDK/mobile clients and each gets a methodDeprecationLogger.method(NAME, '9.0.0', '/v1/...') line pointing at the new REST route.

New endpoints

DDP method REST endpoint Notes
verifyEmail + afterVerifyEmail POST /v1/users.verifyEmail Single call: marks email verified + anonymous→user role swap
cloud:connectWorkspace POST /v1/cloud.connectWorkspace manage-cloud perm
authorization:addPermissionToRole POST /v1/permissions.addRole per-permission perm check reused
authorization:removeRoleFromPermission POST /v1/permissions.removeRole per-permission perm check reused
clearIntegrationHistory POST /v1/integrations.history.clear manage-(own-)outgoing-integrations
replayOutgoingIntegration POST /v1/integrations.outgoing.replay manage-(own-)outgoing-integrations

Endpoint extensions

  • POST /v1/users.setAvatar now accepts an optional service multipart field (stored as avatarOrigin), replacing setAvatarFromService DDP method.

Server-side hook

  • afterLogoutCleanUpCallback + Apps.IPostUserLoggedOut move into a Accounts.onLogout handler + /v1/users.logout post-token-unset block. Client sdk.call('logoutCleanUp') dropped — both DDP and REST logout paths now fire the callbacks server-side.

Reverted from this batch

  • getRoomByTypeAndName — per review feedback, kept as DDP. It may be replaced by a breaking change (e.g. URL routing moving directly to rid) so the migration is deferred.

Known follow-ups

Test plan

  • CI green (lint + typecheck pass with rebuilt rest-typings dist)
  • Admin → Users → upload avatar with OAuth provider service → avatarOrigin matches the service name
  • Workspace registration → connect with offline token → success
  • Verify email link → role swaps anonymous→user
  • Admin → Permissions table → grant/revoke a role to a permission
  • Admin → Integrations → clear history + replay an outgoing webhook entry
  • Log out (DDP) → afterLogoutCleanUpCallback server logs fire
  • Log out via POST /v1/users.logout → same callbacks fire

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added REST endpoints for cloud workspace connectivity, email verification, integration history management, and permission-role operations.
    • Enhanced avatar upload with service field support.
    • Streamlined email verification flow into a single request.
  • Improvements

    • Logout cleanup now handled server-side for improved reliability across all logout paths.
  • Deprecations

    • Multiple DDP methods marked deprecated; use corresponding REST endpoints instead.

New endpoints (with deprecation logs on their DDP counterparts):
- POST /v1/users.verifyEmail            (verifyEmail + afterVerifyEmail)
- POST /v1/cloud.connectWorkspace       (cloud:connectWorkspace)
- POST /v1/permissions.addRole          (authorization:addPermissionToRole)
- POST /v1/permissions.removeRole       (authorization:removeRoleFromPermission)
- POST /v1/integrations.history.clear   (clearIntegrationHistory)
- POST /v1/integrations.outgoing.replay (replayOutgoingIntegration)
- GET  /v1/rooms.getByTypeAndName       (getRoomByTypeAndName)

Existing endpoint extensions:
- POST /v1/users.setAvatar now accepts an optional 'service' multipart
  field, storing it as the user's avatarOrigin (replaces the
  setAvatarFromService DDP method).

Server-side hook:
- afterLogoutCleanUpCallback + Apps.IPostUserLoggedOut fire from a
  new Accounts.onLogout handler and from POST /v1/users.logout, so the
  client no longer needs to invoke logoutCleanUp after a DDP logout.

Client callers swapped:
- UserProvider drops sdk.call('logoutCleanUp')
- useUpdateAvatar sends 'service' through /v1/users.setAvatar
- useOpenRoom + EmbeddedPreload → /v1/rooms.getByTypeAndName
- RegisterWorkspaceTokenModal → /v1/cloud.connectWorkspace
- meteor/startup/accounts.ts collapses verify+after into single REST
- PermissionsTable → /v1/permissions.{add,remove}Role
- OutgoingWebhookHistoryPage + HistoryItem → integrations endpoints

DDP methods stay registered with deprecation logs pointing at the new
routes for external SDK/mobile clients until 9.0.0 removes them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ggazzo ggazzo requested review from a team as code owners May 29, 2026 03:25
@dionisio-bot

dionisio-bot Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented May 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3eacd95

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@rocket.chat/meteor Minor
@rocket.chat/rest-typings Minor
@rocket.chat/core-typings Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6ff60487-a7e1-4941-bb44-7d659d1d01ce

📥 Commits

Reviewing files that changed from the base of the PR and between 53ee70c and 3eacd95.

📒 Files selected for processing (1)
  • apps/meteor/app/api/server/v1/users.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/meteor/app/api/server/v1/users.ts
📜 Recent 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). (4)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
  • GitHub Check: Hacktron Security Check

Walkthrough

Seven DDP methods are migrated to REST endpoints: logout cleanup, avatar service, cloud workspace, email verification, permission-role add/remove, and integration history clear/replay. Shared server helpers are extracted, deprecated DDP methods delegate to those helpers with deprecation logs, new REST endpoints are registered with typed AJV validation, and client-side hooks switch from useMethod to useEndpoint.

Changes

DDP-to-REST Endpoint Migrations

Layer / File(s) Summary
REST endpoint type contracts and validators
packages/rest-typings/src/v1/cloud.ts, packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts, packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts, packages/rest-typings/src/v1/integrations/index.ts, packages/rest-typings/src/v1/integrations/integrations.ts, packages/rest-typings/src/v1/users.ts
New CloudConnectWorkspace, IntegrationsClearHistoryProps, IntegrationsReplayProps types and AJV validators are defined; CloudEndpoints, IntegrationsEndpoints, and UsersEndpoints are extended with the new routes.
Shared server-side backend helpers
apps/meteor/server/hooks/userLogoutCleanUp.ts, apps/meteor/server/hooks/index.ts, apps/meteor/server/lib/users/runAfterVerifyEmail.ts, apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts, apps/meteor/app/authorization/server/functions/permissionRole.ts
runUserLogoutCleanUp, runAfterVerifyEmail, clearIntegrationHistoryMethod, replayOutgoingIntegrationMethod, addPermissionToRoleMethod, and removeRoleFromPermissionMethod are extracted as reusable helpers with authorization, validation, and notification logic. The Accounts.onLogout hook is registered in the hooks index.
REST API endpoint registration
apps/meteor/app/api/server/v1/cloud.ts, apps/meteor/app/api/server/v1/integrations.ts, apps/meteor/app/api/server/v1/permissions.ts, apps/meteor/app/api/server/v1/users.ts
Seven endpoints are registered: POST cloud.connectWorkspace, POST integrations.history.clear, POST integrations.outgoing.replay, POST permissions.addRole, POST permissions.removeRole, POST users.verifyEmail, and an extended users.setAvatar with optional service field. Each validates the body with AJV and calls the corresponding shared helper.
DDP method deprecation and delegation
apps/meteor/app/cloud/server/methods.ts, apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts, apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts, apps/meteor/app/authorization/server/methods/addPermissionToRole.ts, apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts, apps/meteor/server/methods/afterVerifyEmail.ts, apps/meteor/server/methods/logoutCleanUp.ts
Each deprecated DDP method is stripped of inline logic and now logs a methodDeprecationLogger notice pointing to the corresponding REST route before delegating to the shared helper.
Client-side REST endpoint adoption
apps/meteor/client/hooks/useUpdateAvatar.ts, apps/meteor/client/meteor/startup/accounts.ts, apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx, apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx, apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx, apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx, apps/meteor/client/providers/UserProvider/UserProvider.tsx
Client components switch from useMethod to useEndpoint for all seven migrated operations. The logout cleanup client-side call is removed; UserProvider now only emits a local logout event with a comment documenting server-side cleanup. Email verification switches to sdk.rest.post('/v1/users.verifyEmail', { token }).
Changeset documentation
.changeset/rest-batch4-logout-cleanup.md, .changeset/rest-cloud-connect-workspace.md, .changeset/rest-integrations-history-clear-replay.md, .changeset/rest-permissions-add-remove-role.md, .changeset/rest-users-setavatar-service.md, .changeset/rest-users-verify-email.md
Seven changeset files document the endpoint additions with minor version bumps for @rocket.chat/rest-typings and @rocket.chat/meteor, specifying request body shapes, permission reuse, and DDP deprecation behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • RocketChat/Rocket.Chat#40480: Directly modifies the same email verification flow in apps/meteor/client/meteor/startup/accounts.ts, changing how verifyEmail and the follow-up afterVerifyEmail/role-swap are invoked — the same code path changed in this PR.

Suggested labels

type: feature

Suggested reviewers

  • ricardogarim
  • KevLehman
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating batch4 DDP callers to REST and adding 6 new endpoints. It is concise, specific, and clearly reflects the primary objectives of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 39.28571% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.11%. Comparing base (f57901d) to head (3eacd95).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40728      +/-   ##
===========================================
- Coverage    70.14%   70.11%   -0.03%     
===========================================
  Files         3355     3357       +2     
  Lines       129261   129284      +23     
  Branches     22340    22771     +431     
===========================================
- Hits         90673    90653      -20     
- Misses       35293    35340      +47     
+ Partials      3295     3291       -4     
Flag Coverage Δ
e2e 59.20% <22.22%> (-0.07%) ⬇️
e2e-api 46.23% <26.92%> (+<0.01%) ⬆️
unit 70.08% <83.33%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx (1)

55-63: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix connect-workspace success handling: the REST endpoint cloud.connectWorkspace always returns API.v1.success() after await connectWorkspace(this.bodyParams.token) and its 200 response schema requires { success: true }, but connectWorkspace(token) can return false on connection failures (it does not necessarily throw for invalid/used tokens). This would cause the modal to show a misleading “Connected” toast when connectWorkspace({ token }) resolves falsy instead of rejecting—either make connectWorkspace throw on failure or restore an explicit success/connected check.

🤖 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/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx`
around lines 55 - 63, connectWorkspace may resolve to a falsy value on failure
instead of throwing, so update the success handling in
RegisterWorkspaceTokenModal to check the resolved value from connectWorkspace({
token }) before considering it connected: call const result = await
connectWorkspace({ token }); if (result) then call setModal(null) and
dispatchToastMessage({ type: 'success', message: t('Connected') }); otherwise
treat it as a failure by dispatching an error toast (with a helpful message) and
calling setError(true); keep the existing catch block to handle thrown errors
from connectWorkspace as well.
apps/meteor/client/providers/UserProvider/UserProvider.tsx (1)

158-160: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Fix broken logout handler wiring in UserProvider (ee is undefined).

apps/meteor/client/providers/UserProvider/UserProvider.tsx (lines 158-160) defines onLogout: (cb) => ee.on('logout', cb), but ee (and any Emitter wiring) is not declared/imported in this module—so registering an onLogout handler will throw and subscribers won’t be notified.

🤖 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/providers/UserProvider/UserProvider.tsx` around lines 158
- 160, The onLogout handler in UserProvider is wired to an undefined emitter
variable (ee) causing runtime errors; replace this with the module's actual
event mechanism: import or reference the existing EventEmitter instance used
elsewhere (or create a local emitter) and wire onLogout to that emitter's on
method; specifically, fix the onLogout: (cb) => ... entry in UserProvider to use
the proper emitter (the same emitter used to emit 'logout' events in this module
or application) so subscribers are registered and will be notified (ensure the
emitter is declared/imported and exported consistently with other functions that
call emitter.emit('logout')).
🧹 Nitpick comments (2)
apps/meteor/server/hooks/userLogoutCleanUp.ts (1)

8-10: ⚡ Quick win

Handle rejection of the deferred cleanup promise.

void afterLogoutCleanUpCallback.run(user) is fire-and-forget inside setImmediate, so a rejection escapes as an unhandled promise rejection (no await, no .catch). Attach a catch to log/swallow it.

♻️ Proposed fix
 	setImmediate(() => {
-		void afterLogoutCleanUpCallback.run(user);
+		afterLogoutCleanUpCallback.run(user).catch((err) => {
+			SystemLogger.error({ msg: 'Error running afterLogoutCleanUpCallback', err });
+		});
 	});

Adjust the logger import to match this module's conventions.

🤖 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/server/hooks/userLogoutCleanUp.ts` around lines 8 - 10, The
deferred cleanup launched in setImmediate currently calls void
afterLogoutCleanUpCallback.run(user) which can reject and become an unhandled
promise; change that to explicitly handle rejection by attaching a .catch on the
returned promise (or await inside an async wrapper) and log or swallow the error
using this module's logger (use the same logger import convention as this file)
so any rejection from afterLogoutCleanUpCallback.run(user) is caught and
reported.
apps/meteor/client/meteor/startup/accounts.ts (1)

9-16: Remove unused verifyEmail DDP module augmentation.

apps/meteor/client/meteor/startup/accounts.ts now verifies via sdk.rest.post('/v1/users.verifyEmail', { token }), and the declare module '@rocket.chat/ddp-client' { interface ServerMethods { verifyEmail(...) } } block (lines 9-16, including its explanatory comment) appears unused. Consider removing it to avoid stale typing.

🤖 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/meteor/startup/accounts.ts` around lines 9 - 16, The
declared augmentation of the DDP types is stale: remove the entire `declare
module '`@rocket.chat/ddp-client`' { interface ServerMethods { verifyEmail(token:
string): void; } }` block (and its explanatory comment) from the file so the
unused `verifyEmail` DDP augmentation is no longer present; after removal, run
the TypeScript build or linter to confirm no remaining references to
`ServerMethods.verifyEmail` and update any imports/usages that should use
`sdk.rest.post('/v1/users.verifyEmail', { token })` instead.
🤖 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/app/api/server/v1/users.ts`:
- Around line 2101-2131: The user lookup using Users.findOne({
'services.email.verificationTokens.token': token }) is performed before calling
Meteor.callAsync('verifyEmail', token), which can cause
runAfterVerifyEmail(user._id) to be skipped if the pre-verify query returned
null; move the Users.findOne call to after the Meteor.callAsync('verifyEmail',
token) (or re-fetch the user after verifyEmail returns) so you always look up
the user post-verification and then call runAfterVerifyEmail with the found
user's _id; update the users.verifyEmail handler accordingly to ensure the
lookup and runAfterVerifyEmail happen only after Meteor.callAsync('verifyEmail',
token) succeeds.

In `@apps/meteor/client/hooks/useUpdateAvatar.ts`:
- Around line 56-61: The avatar upload is appending a data-URI string as a file
which fails multipart handling in saveAvatarAction/users.setAvatar; in
useUpdateAvatar detect when avatarObj.blob is a string (UserAvatarSuggestion
data:<type>;base64,...) and convert that data URI into a real Blob (decode
base64, create Uint8Array/Blob with the correct MIME type and optional filename)
before calling formData.append('image', blob); alternatively tighten the
isServiceObject guard to require avatarObj.blob instanceof Blob and handle
string blobs by converting them to Blob or routing to the suggestion upload
path; ensure saveAvatarAction/getUploadFormData still receives a real File/Blob
for the 'image' field.

---

Outside diff comments:
In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx`:
- Around line 158-160: The onLogout handler in UserProvider is wired to an
undefined emitter variable (ee) causing runtime errors; replace this with the
module's actual event mechanism: import or reference the existing EventEmitter
instance used elsewhere (or create a local emitter) and wire onLogout to that
emitter's on method; specifically, fix the onLogout: (cb) => ... entry in
UserProvider to use the proper emitter (the same emitter used to emit 'logout'
events in this module or application) so subscribers are registered and will be
notified (ensure the emitter is declared/imported and exported consistently with
other functions that call emitter.emit('logout')).

In
`@apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx`:
- Around line 55-63: connectWorkspace may resolve to a falsy value on failure
instead of throwing, so update the success handling in
RegisterWorkspaceTokenModal to check the resolved value from connectWorkspace({
token }) before considering it connected: call const result = await
connectWorkspace({ token }); if (result) then call setModal(null) and
dispatchToastMessage({ type: 'success', message: t('Connected') }); otherwise
treat it as a failure by dispatching an error toast (with a helpful message) and
calling setError(true); keep the existing catch block to handle thrown errors
from connectWorkspace as well.

---

Nitpick comments:
In `@apps/meteor/client/meteor/startup/accounts.ts`:
- Around line 9-16: The declared augmentation of the DDP types is stale: remove
the entire `declare module '`@rocket.chat/ddp-client`' { interface ServerMethods {
verifyEmail(token: string): void; } }` block (and its explanatory comment) from
the file so the unused `verifyEmail` DDP augmentation is no longer present;
after removal, run the TypeScript build or linter to confirm no remaining
references to `ServerMethods.verifyEmail` and update any imports/usages that
should use `sdk.rest.post('/v1/users.verifyEmail', { token })` instead.

In `@apps/meteor/server/hooks/userLogoutCleanUp.ts`:
- Around line 8-10: The deferred cleanup launched in setImmediate currently
calls void afterLogoutCleanUpCallback.run(user) which can reject and become an
unhandled promise; change that to explicitly handle rejection by attaching a
.catch on the returned promise (or await inside an async wrapper) and log or
swallow the error using this module's logger (use the same logger import
convention as this file) so any rejection from
afterLogoutCleanUpCallback.run(user) is caught and reported.
🪄 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: d1570026-d78a-4d6d-a109-e628c9a159b5

📥 Commits

Reviewing files that changed from the base of the PR and between b92bcc7 and 29d71a2.

📒 Files selected for processing (42)
  • .changeset/ddp-migrate-batch4-callers.md
  • .changeset/rest-batch4-logout-cleanup.md
  • .changeset/rest-cloud-connect-workspace.md
  • .changeset/rest-integrations-history-clear-replay.md
  • .changeset/rest-permissions-add-remove-role.md
  • .changeset/rest-rooms-get-by-type-and-name.md
  • .changeset/rest-users-setavatar-service.md
  • .changeset/rest-users-verify-email.md
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/authorization/server/functions/permissionRole.ts
  • apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
  • apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
  • apps/meteor/app/cloud/server/methods.ts
  • apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts
  • apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts
  • apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/providers/UserProvider/UserProvider.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx
  • apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx
  • apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
  • apps/meteor/server/hooks/index.ts
  • apps/meteor/server/hooks/userLogoutCleanUp.ts
  • apps/meteor/server/lib/users/runAfterVerifyEmail.ts
  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
  • apps/meteor/server/publications/room/index.ts
  • packages/rest-typings/src/v1/cloud.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • packages/rest-typings/src/v1/rooms.ts
  • packages/rest-typings/src/v1/users.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: Hacktron Security Check
  • GitHub Check: CodeQL-Build
  • 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:

  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • apps/meteor/server/hooks/userLogoutCleanUp.ts
  • apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
  • packages/rest-typings/src/v1/cloud.ts
  • apps/meteor/server/hooks/index.ts
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/app/cloud/server/methods.ts
  • packages/rest-typings/src/v1/users.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • apps/meteor/server/lib/users/runAfterVerifyEmail.ts
  • apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx
  • apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx
  • packages/rest-typings/src/v1/rooms.ts
  • apps/meteor/app/authorization/server/functions/permissionRole.ts
  • apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
  • apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
  • apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx
  • apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
  • apps/meteor/server/publications/room/index.ts
  • apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
  • apps/meteor/client/providers/UserProvider/UserProvider.tsx
🧠 Learnings (14)
📚 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:

  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • apps/meteor/server/hooks/userLogoutCleanUp.ts
  • packages/rest-typings/src/v1/cloud.ts
  • apps/meteor/server/hooks/index.ts
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/app/cloud/server/methods.ts
  • packages/rest-typings/src/v1/users.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • apps/meteor/server/lib/users/runAfterVerifyEmail.ts
  • packages/rest-typings/src/v1/rooms.ts
  • apps/meteor/app/authorization/server/functions/permissionRole.ts
  • apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
  • apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
  • apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
  • apps/meteor/server/publications/room/index.ts
  • apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/authorization/server/methods/removeRoleFromPermission.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:

  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • apps/meteor/server/hooks/userLogoutCleanUp.ts
  • packages/rest-typings/src/v1/cloud.ts
  • apps/meteor/server/hooks/index.ts
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/app/cloud/server/methods.ts
  • packages/rest-typings/src/v1/users.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • apps/meteor/server/lib/users/runAfterVerifyEmail.ts
  • packages/rest-typings/src/v1/rooms.ts
  • apps/meteor/app/authorization/server/functions/permissionRole.ts
  • apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
  • apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
  • apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
  • apps/meteor/server/publications/room/index.ts
  • apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/authorization/server/methods/removeRoleFromPermission.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:

  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • apps/meteor/server/hooks/userLogoutCleanUp.ts
  • apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
  • packages/rest-typings/src/v1/cloud.ts
  • apps/meteor/server/hooks/index.ts
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/app/cloud/server/methods.ts
  • packages/rest-typings/src/v1/users.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • apps/meteor/server/lib/users/runAfterVerifyEmail.ts
  • apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx
  • apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx
  • packages/rest-typings/src/v1/rooms.ts
  • apps/meteor/app/authorization/server/functions/permissionRole.ts
  • apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
  • apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
  • apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx
  • apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts
  • apps/meteor/server/publications/room/index.ts
  • apps/meteor/app/authorization/server/methods/addPermissionToRole.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts
  • apps/meteor/client/providers/UserProvider/UserProvider.tsx
📚 Learning: 2026-05-11T23:14:59.316Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 40469
File: packages/rest-typings/src/v1/users.ts:337-337
Timestamp: 2026-05-11T23:14:59.316Z
Learning: In Rocket.Chat REST endpoint typings (e.g., packages/rest-typings/src/v1/users.ts and other rest-typings files), keep the established convention of deriving field types from the domain model (e.g., use IUser indexed access like IUser['statusExpiresAt']) rather than swapping individual fields to serialized primitives (like string) in an ad-hoc way. If a truly different “serialized” representation is needed, perform the refactor consistently across the codebase (not just a single endpoint/field) and ensure all related REST typings stay aligned with the shared serialization types.

Applied to files:

  • packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts
  • packages/rest-typings/src/v1/integrations/index.ts
  • packages/rest-typings/src/v1/cloud.ts
  • packages/rest-typings/src/v1/users.ts
  • packages/rest-typings/src/v1/integrations/integrations.ts
  • packages/rest-typings/src/v1/rooms.ts
  • packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts
📚 Learning: 2026-03-16T21:50:37.589Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: .changeset/migrate-users-register-openapi.md:3-3
Timestamp: 2026-03-16T21:50:37.589Z
Learning: For changes related to OpenAPI migrations in Rocket.Chat/OpenAPI, when removing endpoint types and validators from rocket.chat/rest-typings (e.g., UserRegisterParamsPOST, /v1/users.register) document this as a minor changeset (not breaking) per RocketChat/Rocket.Chat-Open-API#150 Rule 7. Note that the endpoint type is re-exposed via a module augmentation .d.ts in the consuming package (e.g., packages/web-ui-registration/src/users-register.d.ts). In reviews, ensure the changeset clearly states: this is a non-breaking change, the major version should not be bumped, and the changeset reflects a minor version bump. Do not treat this as a breaking change during OpenAPI migrations.

Applied to files:

  • .changeset/rest-users-setavatar-service.md
  • .changeset/rest-permissions-add-remove-role.md
  • .changeset/rest-rooms-get-by-type-and-name.md
  • .changeset/rest-users-verify-email.md
  • .changeset/ddp-migrate-batch4-callers.md
  • .changeset/rest-cloud-connect-workspace.md
  • .changeset/rest-integrations-history-clear-replay.md
  • .changeset/rest-batch4-logout-cleanup.md
📚 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/views/root/MainLayout/EmbeddedPreload.tsx
  • apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx
  • apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx
  • apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx
  • apps/meteor/client/providers/UserProvider/UserProvider.tsx
📚 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/views/room/hooks/useOpenRoom.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/hooks/useUpdateAvatar.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/views/room/hooks/useOpenRoom.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/hooks/useUpdateAvatar.ts
📚 Learning: 2026-02-23T17:53:06.802Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 35995
File: apps/meteor/app/api/server/v1/rooms.ts:1107-1112
Timestamp: 2026-02-23T17:53:06.802Z
Learning: During PR reviews that touch endpoint files under apps/meteor/app/api/server/v1, enforce strict scope: if a PR targets a specific endpoint (e.g., rooms.favorite), do not propose changes to unrelated endpoints (e.g., rooms.invite) unless maintainers explicitly request them. Focus feedback on the touched endpoint's behavior, API surface, and related tests; avoid broad cross-endpoint changes in the same PR unless requested.

Applied to files:

  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-02-24T19:09:01.522Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:01.522Z
Learning: In Rocket.Chat OpenAPI migration PRs for endpoints under apps/meteor/app/api/server/v1, avoid introducing logic changes. Only perform scope-tight changes that preserve behavior; style-only cleanups (e.g., removing inline comments) may be deferred to follow-ups to keep the migration PR focused.

Applied to files:

  • apps/meteor/app/api/server/v1/integrations.ts
  • apps/meteor/app/api/server/v1/permissions.ts
  • apps/meteor/app/api/server/v1/cloud.ts
  • apps/meteor/app/api/server/v1/rooms.ts
  • apps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-03-09T18:39:14.020Z
Learnt from: Harxhit
Repo: RocketChat/Rocket.Chat PR: 39476
File: apps/meteor/server/methods/addAllUserToRoom.ts:0-0
Timestamp: 2026-03-09T18:39:14.020Z
Learning: When implementing batch processing in server methods, favor a single data pass to collect both the items and any derived fields needed for validation. Use the same dataset for both validation and processing to avoid races between validation and execution, and document the approach in code comments. Apply this pattern to similar Meteor Rocket.Chat server methods under apps/meteor/server/methods to prevent race conditions and ensure consistent batch behavior.

Applied to files:

  • apps/meteor/server/methods/afterVerifyEmail.ts
  • apps/meteor/server/methods/logoutCleanUp.ts
📚 Learning: 2026-04-14T21:10:31.855Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 36292
File: apps/meteor/client/hooks/useHasValidLocationHash.ts:7-12
Timestamp: 2026-04-14T21:10:31.855Z
Learning: When reviewing files in apps/meteor/client/hooks/, do not treat JSDoc-style comments on React hooks (especially exported hooks) as a violation of any “avoid code comments in implementation” guideline. It’s acceptable to use JSDoc to document the public API of exported hooks (e.g., parameter/return types, intended usage), as long as it documents behavior/contracts rather than adding narrative implementation comments.

Applied to files:

  • apps/meteor/client/hooks/useUpdateAvatar.ts
📚 Learning: 2026-03-15T14:31:25.380Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39647
File: apps/meteor/app/api/server/v1/users.ts:710-757
Timestamp: 2026-03-15T14:31:25.380Z
Learning: Do not flag this type/schema misalignment in the OpenAPI/migration review for apps/meteor/app/api/server/v1/users.ts. The UserCreateParamsPOST type intentionally uses non-optional fields: fields: string and settings?: IUserSettings without an AJV schema entry, carried over from the original rest-typings (PR `#39647`). Treat this as a known pre-existing divergence and document it as a separate follow-up fix; do not block or mark it as a review issue during the migration.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
📚 Learning: 2026-03-16T23:33:11.443Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: apps/meteor/app/api/server/v1/users.ts:862-869
Timestamp: 2026-03-16T23:33:11.443Z
Learning: In rockets OpenAPI/AJV migration reviews for RocketChat/Rocket.Chat, when reviewing migrations that involve apps/meteor/app/api/server/v1/users.ts, do not require or flag a missing query AJV schema for the fields consumed by parseJsonQuery() (i.e., fields, sort, query) as part of this endpoint's migration PR. The addition of global query-param schemas for parseJsonQuery() usage is a cross-cutting concern and out of scope for individual endpoint migrations. Only flag violations related to the specific scope of the migration, not the absence of a query schema for parseJsonQuery() in this file.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
🔇 Additional comments (41)
packages/rest-typings/src/v1/cloud.ts (1)

62-108: LGTM!

packages/rest-typings/src/v1/integrations/IntegrationsClearHistoryProps.ts (1)

1-16: LGTM!

packages/rest-typings/src/v1/integrations/IntegrationsReplayProps.ts (1)

1-18: LGTM!

packages/rest-typings/src/v1/integrations/index.ts (1)

3-7: LGTM!

packages/rest-typings/src/v1/integrations/integrations.ts (1)

3-9: LGTM!

Also applies to: 45-52

packages/rest-typings/src/v1/rooms.ts (1)

131-143: LGTM!

Also applies to: 900-904

packages/rest-typings/src/v1/users.ts (1)

375-377: LGTM!

apps/meteor/server/lib/users/runAfterVerifyEmail.ts (1)

1-31: LGTM!

apps/meteor/app/integrations/server/functions/clearIntegrationHistory.ts (1)

1-70: LGTM!

apps/meteor/app/authorization/server/functions/permissionRole.ts (1)

1-78: LGTM!

apps/meteor/server/publications/room/index.ts (1)

49-104: LGTM!

apps/meteor/app/api/server/v1/cloud.ts (1)

292-309: LGTM!

apps/meteor/app/api/server/v1/integrations.ts (1)

372-415: LGTM!

apps/meteor/app/api/server/v1/permissions.ts (1)

66-83: LGTM!

Also applies to: 208-244

apps/meteor/app/api/server/v1/rooms.ts (1)

559-586: LGTM!

apps/meteor/app/api/server/v1/users.ts (3)

348-350: LGTM!


1887-1900: LGTM!


41-45: LGTM!

apps/meteor/app/cloud/server/methods.ts (1)

115-115: LGTM!

apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts (1)

1-20: LGTM!

apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts (1)

1-20: LGTM!

apps/meteor/app/authorization/server/methods/addPermissionToRole.ts (1)

1-19: LGTM!

apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts (1)

1-19: LGTM!

apps/meteor/server/methods/afterVerifyEmail.ts (1)

1-28: LGTM!

apps/meteor/server/methods/logoutCleanUp.ts (1)

1-23: LGTM!

apps/meteor/client/meteor/startup/accounts.ts (1)

49-59: LGTM!

apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx (1)

4-4: LGTM!

Also applies to: 16-16, 39-42

apps/meteor/client/views/admin/integrations/outgoing/history/OutgoingWebhookHistoryPage.tsx (1)

3-3: LGTM!

Also applies to: 21-21, 53-62

apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx (1)

6-7: LGTM!

Also applies to: 26-41

apps/meteor/client/views/room/hooks/useOpenRoom.ts (2)

3-3: LGTM!

Also applies to: 22-22


79-79: ⚡ Quick win

Ensure REST room payload date fields are deserialized before Rooms.state.store
apps/meteor/client/views/room/hooks/useOpenRoom.ts force-casts the REST /v1/rooms.getByTypeAndName result (roomData = (await getRoomByTypeAndName(...)).room as IRoom;) and then stores it directly (Rooms.state.store(roomData)). The cached store implementation stores new records without deserializing date fields (it only uses _updatedAt/hasUpdatedAt for change tracking), so if the REST response provides ISO strings for ts, lm, lastMessage.ts/lastMessage._updatedAt/_updatedAt, those strings will be persisted where IRoom expects Date. Convert/normalize these fields before storing (or make the REST client revive them to Date).

apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx (1)

1-1: LGTM!

Also applies to: 37-37, 49-49

apps/meteor/server/hooks/index.ts (1)

2-2: LGTM!

.changeset/ddp-migrate-batch4-callers.md (1)

1-13: LGTM!

.changeset/rest-batch4-logout-cleanup.md (1)

1-6: LGTM!

.changeset/rest-cloud-connect-workspace.md (1)

1-7: LGTM!

.changeset/rest-integrations-history-clear-replay.md (1)

1-7: LGTM!

.changeset/rest-permissions-add-remove-role.md (1)

1-7: LGTM!

.changeset/rest-rooms-get-by-type-and-name.md (1)

1-7: LGTM!

.changeset/rest-users-setavatar-service.md (1)

1-5: LGTM!

.changeset/rest-users-verify-email.md (1)

1-6: LGTM!

Comment on lines +2101 to +2131
API.v1.post(
'users.verifyEmail',
{
authRequired: false,
body: ajv.compile<{ token: string }>({
type: 'object',
properties: {
token: { type: 'string', minLength: 1 },
},
required: ['token'],
additionalProperties: false,
}),
response: {
200: voidSuccessResponse,
400: validateBadRequestErrorResponse,
},
},
async function action() {
const { token } = this.bodyParams;

const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });

await Meteor.callAsync('verifyEmail', token);

if (user) {
await runAfterVerifyEmail(user._id);
}

return API.v1.success();
},
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

User lookup should occur after verifyEmail succeeds to avoid skipping cleanup.

The user is looked up before Meteor.callAsync('verifyEmail', token), but the token might be valid even if no user was found due to a race or projection mismatch. If verifyEmail succeeds (marks email verified) but the prior lookup returned null, runAfterVerifyEmail is skipped—leaving the anonymous→user role swap incomplete.

Consider moving the user lookup after the verifyEmail call or re-fetching the user after verification succeeds.

Proposed fix
 async function action() {
 	const { token } = this.bodyParams;

-	const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });
-
 	await Meteor.callAsync('verifyEmail', token);

+	const user = await Users.findOne<Pick<IUser, '_id'>>({ 'emails.verified': true, 'services.email.verificationTokens.token': { $exists: false } }, { projection: { _id: 1 } });
+	// Alternative: lookup by the token that was just consumed (if Meteor stores it differently post-verification)
+	// Or pass userId from verifyEmail if it returns it
+
 	if (user) {
 		await runAfterVerifyEmail(user._id);
 	}

 	return API.v1.success();
 }

Note: The exact query after verification depends on how verifyEmail modifies the user document. Verify that the lookup correctly identifies the user whose email was just verified.

🤖 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/app/api/server/v1/users.ts` around lines 2101 - 2131, The user
lookup using Users.findOne({ 'services.email.verificationTokens.token': token })
is performed before calling Meteor.callAsync('verifyEmail', token), which can
cause runAfterVerifyEmail(user._id) to be skipped if the pre-verify query
returned null; move the Users.findOne call to after the
Meteor.callAsync('verifyEmail', token) (or re-fetch the user after verifyEmail
returns) so you always look up the user post-verification and then call
runAfterVerifyEmail with the found user's _id; update the users.verifyEmail
handler accordingly to ensure the lookup and runAfterVerifyEmail happen only
after Meteor.callAsync('verifyEmail', token) succeeds.

Comment on lines +56 to +61
const { blob, service } = avatarObj;
const formData = new FormData();
formData.append('userId', userId);
formData.append('service', service);
formData.append('image', blob);
await saveAvatarAction(formData);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1) AvatarServiceObject definition
ast-grep --pattern 'type AvatarServiceObject = $_'
rg -nP '\bAvatarServiceObject\b' --type=ts -C2

# 2) Where service avatar objects (blob/service) are produced
rg -nP "service\s*:|blob\s*:" --type=ts -g '*Avatar*' -C3

# 3) How /v1/users.setAvatar consumes the multipart 'image' / 'service' fields
rg -nP "users\.setAvatar" --type=ts -A30 -g '**/api/server/v1/users.ts'

Repository: RocketChat/Rocket.Chat

Length of output: 15243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect useUpdateAvatar.ts around the type guards + FormData construction.
sed -n '1,140p' apps/meteor/client/hooks/useUpdateAvatar.ts

# Locate AvatarUrlObj definition and confirm its shape.
rg -n "export type AvatarUrlObj" -S packages/core-typings/src/IUser.ts
sed -n '240,340p' packages/core-typings/src/IUser.ts

# Inspect further down in users.setAvatar handler to see how `image` is used.
rg -n "const image = await getUploadFormData|setUserAvatar\\(" apps/meteor/app/api/server/v1/users.ts
sed -n '300,420p' apps/meteor/app/api/server/v1/users.ts

# Find getUploadFormData implementation to confirm field parsing expectations (best-effort).
rg -n "function getUploadFormData|getUploadFormData\\(" -S apps/meteor/app -g'*.ts' | head -n 50

Repository: RocketChat/Rocket.Chat

Length of output: 10318


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect getUploadFormData to see how mimetype/fileBuffer are extracted from multipart field `image`
sed -n '1,220p' apps/meteor/app/api/server/lib/getUploadFormData.ts

# Find client-side creation of AvatarServiceObject (blob/service) candidates
rg -n "AvatarServiceObject|contentType:|setAvatarFromService|users\.setAvatar|useUpdateAvatar\\(" apps/meteor/client apps/meteor -S

# Search for FormData append patterns that use 'image' field
rg -n "append\\(['\"]image['\"]" apps/meteor/client -S

Repository: RocketChat/Rocket.Chat

Length of output: 16661


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where the avatar state is created/updated for AccountProfileForm/AdminUserForm and passes into useUpdateAvatar
rg -n "useUpdateAvatar\\(" apps/meteor/client/views apps/meteor/client/providers -S
rg -n "const \\[avatar\\]|let avatar|avatar\\s*=" apps/meteor/client -S -g'*.ts*' | head -n 80

# Find how AvatarObject is populated from service suggestions (UserAvatarSuggestion has blob: string)
rg -n "UserAvatarSuggestion" apps/meteor/client -S
rg -n "blob\\s*:\\s*.*contentType|contentType\\s*:" apps/meteor/client/components/avatar -S -g'*.ts*'

# Find conversions from data URI/string -> Blob for uploading
rg -n "atob\\(|new Blob\\(|data:.*;base64|fetch\\(.*data:" apps/meteor/client/components/avatar apps/meteor/client/hooks -S -g'*.ts*'

# Search for remaining setAvatarFromService usage on client
rg -n "setAvatarFromService" apps/meteor/client -S

Repository: RocketChat/Rocket.Chat

Length of output: 12753


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Open forms where avatar state is passed to useUpdateAvatar
sed -n '1,220p' apps/meteor/client/views/account/profile/AccountProfileForm.tsx
sed -n '1,240p' apps/meteor/client/views/admin/users/AdminUserForm.tsx

# Inspect avatar editor that likely builds AvatarObject (reset/url/service/upload)
sed -n '1,260p' apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarEditor.tsx

# Inspect suggestion type/source to see what `blob: string` represents
sed -n '1,220p' apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestion.ts
sed -n '1,260p' apps/meteor/client/components/avatar/UserAvatarEditor/useUserAvatarSuggestions.ts

# Search for conversions that turn suggestion/blob string into Blob
rg -n "new Blob\\(|data:|atob\\(|Buffer\\.from\\(|fetch\\(|Blob\\(" apps/meteor/client/components/avatar/UserAvatarEditor -S

# Search for places that produce `AvatarServiceObject` (or `{ blob, contentType, service }`) on client
rg -n "blob\\s*:\\s*.*contentType\\s*:\\s*.*service\\s*:" apps/meteor/client -S
rg -n "AvatarServiceObject|isServiceObject\\(" apps/meteor/client -S

Repository: RocketChat/Rocket.Chat

Length of output: 21098


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect getAvatarSuggestionForUser to see what `blob` contains (base64/data URI/etc)
sed -n '130,240p' apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts

# Inspect users.getAvatarSuggestion endpoint to confirm response mapping
rg -n "users\\.getAvatarSuggestion|getAvatarSuggestionForUser" apps/meteor/app/api/server -S
rg -n "getAvatarSuggestion" apps/meteor/app/api/server/v1/users.ts -S
sed -n '1,260p' apps/meteor/app/api/server/v1/users.ts | rg -n "getAvatarSuggestion|AvatarSuggestion|users\\.getAvatarSuggestion" -n

Repository: RocketChat/Rocket.Chat

Length of output: 2091


Fix avatar upload for avatar suggestions: blob is a data-URI string but is appended as a multipart “file”

useUpdateAvatar treats any non-reset object with service as an AvatarServiceObject and does formData.append('image', blob). However, AvatarServiceObject.blob is a Blob, while UserAvatarSuggestion.blob is constructed server-side as a data:<content-type>;base64,... string and then cast into AvatarObject. Appending that string to FormData won’t create a multipart file part for the image field, and users.setAvatar uses getUploadFormData(..., { field: 'image' }) which requires an uploaded file—so the avatar update can fail for suggestions.

Relevant code
const { blob, service } = avatarObj;
const formData = new FormData();
formData.append('userId', userId);
formData.append('service', service);
formData.append('image', blob);
await saveAvatarAction(formData);

Convert suggestion blob (data URI string) to a Blob before appending, or tighten the isServiceObject guard to require blob is actually a Blob at runtime and route otherwise.

🤖 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/hooks/useUpdateAvatar.ts` around lines 56 - 61, The avatar
upload is appending a data-URI string as a file which fails multipart handling
in saveAvatarAction/users.setAvatar; in useUpdateAvatar detect when
avatarObj.blob is a string (UserAvatarSuggestion data:<type>;base64,...) and
convert that data URI into a real Blob (decode base64, create Uint8Array/Blob
with the correct MIME type and optional filename) before calling
formData.append('image', blob); alternatively tighten the isServiceObject guard
to require avatarObj.blob instanceof Blob and handle string blobs by converting
them to Blob or routing to the suggestion upload path; ensure
saveAvatarAction/getUploadFormData still receives a real File/Blob for the
'image' field.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 42 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="apps/meteor/client/hooks/useUpdateAvatar.ts">

<violation number="1" location="apps/meteor/client/hooks/useUpdateAvatar.ts:60">
P2: Service-avatar uploads can send non-binary image data because `blob` may be a string; convert it to a real `Blob` before appending to `FormData`.</violation>
</file>

<file name="apps/meteor/server/methods/logoutCleanUp.ts">

<violation number="1" location="apps/meteor/server/methods/logoutCleanUp.ts:21">
P2: Deprecated `logoutCleanUp` still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from `Accounts.onLogout`.</violation>
</file>

<file name="apps/meteor/app/api/server/v1/cloud.ts">

<violation number="1" location="apps/meteor/app/api/server/v1/cloud.ts:306">
P1: The new `cloud.connectWorkspace` route ignores the boolean result of `connectWorkspace` and always returns success, so failed workspace connections are incorrectly reported as successful.</violation>
</file>

<file name="apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx">

<violation number="1" location="apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx:56">
P1: This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.</violation>
</file>

<file name="apps/meteor/app/api/server/v1/permissions.ts">

<violation number="1" location="apps/meteor/app/api/server/v1/permissions.ts:210">
P2: Add a route-level `permissionsRequired` gate for `permissions.addRole`. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.

(Based on your team's feedback about permission checks at public API entry points.) [FEEDBACK_USED]</violation>

<violation number="2" location="apps/meteor/app/api/server/v1/permissions.ts:228">
P2: Add a route-level `permissionsRequired` gate for `permissions.removeRole`. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.

(Based on your team's feedback about permission checks at public API entry points.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/meteor/app/authorization/server/functions/permissionRole.ts">

<violation number="1" location="apps/meteor/app/authorization/server/functions/permissionRole.ts:46">
P2: Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.</violation>
</file>

<file name="apps/meteor/app/api/server/v1/users.ts">

<violation number="1" location="apps/meteor/app/api/server/v1/users.ts:2121">
P2: The user lookup should occur after `verifyEmail` succeeds. If `findOne` returns `null` due to a race or replication lag but `verifyEmail` still succeeds (it removes the token from the document), `runAfterVerifyEmail` will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the `callAsync` or re-fetch the user post-verification.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +306 to +307
await connectWorkspace(this.bodyParams.token);
return API.v1.success();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The new cloud.connectWorkspace route ignores the boolean result of connectWorkspace and always returns success, so failed workspace connections are incorrectly reported as successful.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/cloud.ts, line 306:

<comment>The new `cloud.connectWorkspace` route ignores the boolean result of `connectWorkspace` and always returns success, so failed workspace connections are incorrectly reported as successful.</comment>

<file context>
@@ -287,6 +289,25 @@ declare module '@rocket.chat/rest-typings' {
+		},
+	},
+	async function action() {
+		await connectWorkspace(this.bodyParams.token);
+		return API.v1.success();
+	},
</file context>
Suggested change
await connectWorkspace(this.bodyParams.token);
return API.v1.success();
const connected = await connectWorkspace(this.bodyParams.token);
if (!connected) {
return API.v1.failure('Failed to connect workspace');
}
return API.v1.success();

if (!isConnected) {
throw Error(t('RegisterWorkspace_Connection_Error'));
}
await connectWorkspace({ token });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/admin/workspace/VersionCard/modals/RegisterWorkspaceTokenModal.tsx, line 56:

<comment>This call no longer checks whether workspace connection actually succeeded, so failures can be reported as successful connections.</comment>

<file context>
@@ -53,11 +53,7 @@ const RegisterWorkspaceTokenModal = ({ onClose, onStatusChange, ...props }: Regi
-			if (!isConnected) {
-				throw Error(t('RegisterWorkspace_Connection_Error'));
-			}
+			await connectWorkspace({ token });
 
 			setModal(null);
</file context>

const formData = new FormData();
formData.append('userId', userId);
formData.append('service', service);
formData.append('image', blob);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Service-avatar uploads can send non-binary image data because blob may be a string; convert it to a real Blob before appending to FormData.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/hooks/useUpdateAvatar.ts, line 60:

<comment>Service-avatar uploads can send non-binary image data because `blob` may be a string; convert it to a real `Blob` before appending to `FormData`.</comment>

<file context>
@@ -54,30 +53,19 @@ export const useUpdateAvatar = (avatarObj: AvatarObject, userId: IUser['_id']) =
+			const formData = new FormData();
+			formData.append('userId', userId);
+			formData.append('service', service);
+			formData.append('image', blob);
+			await saveAvatarAction(formData);
 			return;
</file context>


// App IPostUserLogout event hook
await Apps.self?.triggerEvent(AppEvents.IPostUserLoggedOut, user);
await runUserLogoutCleanUp(user);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Deprecated logoutCleanUp still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from Accounts.onLogout.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/server/methods/logoutCleanUp.ts, line 21:

<comment>Deprecated `logoutCleanUp` still triggers logout side effects, which can duplicate cleanup/event execution now that the same hook also runs from `Accounts.onLogout`.</comment>

<file context>
@@ -15,13 +15,9 @@ declare module '@rocket.chat/ddp-client' {
-
-		// App IPostUserLogout event hook
-		await Apps.self?.triggerEvent(AppEvents.IPostUserLoggedOut, user);
+		await runUserLogoutCleanUp(user);
 	},
 });
</file context>
Suggested change
await runUserLogoutCleanUp(user);
return;

Comment on lines +228 to +231
'permissions.removeRole',
{
authRequired: true,
body: isPermissionRolePayload,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Add a route-level permissionsRequired gate for permissions.removeRole. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.

(Based on your team's feedback about permission checks at public API entry points.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/permissions.ts, line 228:

<comment>Add a route-level `permissionsRequired` gate for `permissions.removeRole`. This keeps permission failures on the middleware path (403) instead of relying on helper-thrown errors that are returned as 400.

(Based on your team's feedback about permission checks at public API entry points.) </comment>

<file context>
@@ -185,6 +205,42 @@ const permissionsEndpoints = API.v1
+		},
+	)
+	.post(
+		'permissions.removeRole',
+		{
+			authRequired: true,
</file context>
Suggested change
'permissions.removeRole',
{
authRequired: true,
body: isPermissionRolePayload,
'permissions.removeRole',
{
authRequired: true,
permissionsRequired: ['access-permissions'],
body: isPermissionRolePayload,

Comment on lines +210 to +213
'permissions.addRole',
{
authRequired: true,
body: isPermissionRolePayload,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Add a route-level permissionsRequired gate for permissions.addRole. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.

(Based on your team's feedback about permission checks at public API entry points.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/permissions.ts, line 210:

<comment>Add a route-level `permissionsRequired` gate for `permissions.addRole`. Without it, authorization failures are handled inside the helper and fall back to 400 instead of a consistent early 403.

(Based on your team's feedback about permission checks at public API entry points.) </comment>

<file context>
@@ -185,6 +205,42 @@ const permissionsEndpoints = API.v1
 		},
+	)
+	.post(
+		'permissions.addRole',
+		{
+			authRequired: true,
</file context>
Suggested change
'permissions.addRole',
{
authRequired: true,
body: isPermissionRolePayload,
'permissions.addRole',
{
authRequired: true,
permissionsRequired: ['access-permissions'],
body: isPermissionRolePayload,

void notifyOnPermissionChangedById(permission.groupPermissionId);
}

await Permissions.addRole(permission._id, role);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/authorization/server/functions/permissionRole.ts, line 46:

<comment>Validate the role exists before writing it to permission roles, otherwise invalid role IDs can be persisted.</comment>

<file context>
@@ -0,0 +1,78 @@
+		void notifyOnPermissionChangedById(permission.groupPermissionId);
+	}
+
+	await Permissions.addRole(permission._id, role);
+
+	void notifyOnPermissionChangedById(permission._id);
</file context>

async function action() {
const { token } = this.bodyParams;

const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The user lookup should occur after verifyEmail succeeds. If findOne returns null due to a race or replication lag but verifyEmail still succeeds (it removes the token from the document), runAfterVerifyEmail will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the callAsync or re-fetch the user post-verification.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/users.ts, line 2121:

<comment>The user lookup should occur after `verifyEmail` succeeds. If `findOne` returns `null` due to a race or replication lag but `verifyEmail` still succeeds (it removes the token from the document), `runAfterVerifyEmail` will be silently skipped—leaving the anonymous→user role swap incomplete. Move the lookup after the `callAsync` or re-fetch the user post-verification.</comment>

<file context>
@@ -2088,6 +2098,38 @@ API.v1
+	async function action() {
+		const { token } = this.bodyParams;
+
+		const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });
+
+		await Meteor.callAsync('verifyEmail', token);
</file context>

@hacktron-app hacktron-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Severity Count
🔴 High 1

View full scan results

Comment on lines +2118 to +2130
async function action() {
const { token } = this.bodyParams;

const user = await Users.findOne<Pick<IUser, '_id'>>({ 'services.email.verificationTokens.token': token }, { projection: { _id: 1 } });

await Meteor.callAsync('verifyEmail', token);

if (user) {
await runAfterVerifyEmail(user._id);
}

return API.v1.success();
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 High: Missing authorization check in users.update API allows account takeover and privilege escalation

The users.update API endpoint does not enforce sufficient authorization checks before calling saveUser to update user information. While it requires 2FA, it lacks explicit permission requirements (e.g., edit-other-user-info), potentially allowing authenticated users to modify the profiles of other users.

Trace
graph TD
    subgraph SG0 ["apps/meteor/app/2fa/server/code/index.ts"]
        getUserForCheck["Retrieves user data required for two-factor authentication checks."]
    end
    style SG0 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG1 ["apps/meteor/app/2fa/server/functions/resetTOTP.ts"]
        sendResetNotification_2["sendResetNotification"]
        resetTOTP["Resets a user's TOTP configuration, optionally notifying the user via email."]
    end
    style SG1 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG2 ["apps/meteor/app/api/server/ApiClass.ts"]
        APIClass.success["Returns a successful API response."]
        APIClass.failure["Returns a failure API response."]
        APIClass.forbidden["Returns a 403 Forbidden API response."]
        APIClass.this.parseJsonQuery["Parses JSON query parameters for an API context."]
    end
    style SG2 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG3 ["apps/meteor/app/api/server/helpers/getPaginationItems.ts"]
        getPaginationItems["Calculates pagination offset and count based on request parameters and system settings."]
    end
    style SG3 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG4 ["apps/meteor/app/api/server/helpers/getUserFromParams.ts"]
        getUserFromParams["Retrieves a user object based on userId or username parameters from an API request."]
    end
    style SG4 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG5 ["apps/meteor/app/api/server/helpers/getUserInfo.ts"]
        isVerifiedEmail["isVerifiedEmail"]
        getUserPreferences["getUserPreferences"]
        filterOutdatedVersionUpdateBanners["filterOutdatedVersionUpdateBanners"]
        getUserCalendar["getUserCalendar"]
        getUserInfo["Constructs and returns a comprehensive user information object for API responses, including preferences and calendar settings."]
    end
    style SG5 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG6 ["apps/meteor/app/api/server/helpers/isUserFromParams.ts"]
        isUserFromParams["Validates if the provided parameters identify the currently logged-in user."]
    end
    style SG6 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG7 ["apps/meteor/app/api/server/lib/eraseTeam.ts"]
        eraseTeamOnRelinquishRoomOwnerships["eraseTeamOnRelinquishRoomOwnerships"]
        eraseRoomLooseValidation["eraseRoomLooseValidation"]
    end
    style SG7 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG8 ["apps/meteor/app/api/server/lib/getUploadFormData.ts"]
        getUploadFormData["Parses multipart/form-data from an HTTP request to extract file data and fields."]
    end
    style SG8 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG9 ["apps/meteor/app/api/server/lib/isValidQuery.ts"]
        ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts["Top-level logic providing a utility to validate query objects against allowed attributes and operations."]
    end
    style SG9 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG10 ["apps/meteor/app/api/server/lib/users.ts"]
        findUsersToAutocomplete["Provides autocomplete suggestions for users based on search terms and access permissions."]
        getInclusiveFields["Filters a query object to retain only inclusive fields."]
        getNonEmptyFields["Returns default fields if the provided fields object is empty or invalid."]
        getNonEmptyQuery["Returns a default query if the provided query object is empty, optionally including email searches."]
        findPaginatedUsersByStatus["Retrieves a paginated list of users filtered by status, roles, and other criteria."]
    end
    style SG10 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG11 ["apps/meteor/app/api/server/v1/users.ts"]
        get["get"]
        action{{"action"}}
    end
    style SG11 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG12 ["apps/meteor/app/authentication/server/startup/index.js"]
        Accounts.insertUserDoc["Accounts.insertUserDoc"]
    end
    style SG12 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG13 ["apps/meteor/app/authorization/server/functions/hasPermission.ts"]
        hasPermissionAsync["Checks if a user has a specific permission, optionally within a room scope."]
    end
    style SG13 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG14 ["apps/meteor/app/authorization/server/index.ts"]
        ._Rocket.Chat_apps_meteor_app_authorization_server_index.ts["Exports authorization-related functions and registers server methods."]
    end
    style SG14 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG15 ["apps/meteor/app/file-upload/server/lib/FileUpload.ts"]
        FileUpload.getStore["Retrieves a file storage handler by model name."]
        FileUpload.getStoreByName["FileUpload.getStoreByName"]
        FileUpload.get["FileUpload.get"]
        FileUpload.removeFilesByRoomId["FileUpload.removeFilesByRoomId"]
        FileUploadClass.deleteById["FileUploadClass.deleteById"]
    end
    style SG15 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG16 ["apps/meteor/app/file/server/file.server.ts"]
        RocketChatFile.dataURIParse["RocketChatFile.dataURIParse"]
    end
    style SG16 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG17 ["apps/meteor/app/invites/server/functions/validateInviteToken.ts"]
        validateInviteToken["validateInviteToken"]
    end
    style SG17 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG18 ["apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts"]
        addUserToDefaultChannels["addUserToDefaultChannels"]
    end
    style SG18 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG19 ["apps/meteor/app/lib/server/functions/addUserToRoom.ts"]
        addUserToRoom["addUserToRoom"]
    end
    style SG19 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG20 ["apps/meteor/app/lib/server/functions/checkEmailAvailability.ts"]
        checkEmailAvailability["Checks if an email address is available in the database."]
    end
    style SG20 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG21 ["apps/meteor/app/lib/server/functions/checkUsernameAvailability.ts"]
        toRegExp["toRegExp"]
        usernameIsBlocked["usernameIsBlocked"]
        checkUsernameAvailabilityWithValidation["Checks if a username is available with validation logic for user editing."]
        checkUsernameAvailability["Checks if a username is available in the system, considering blocked lists and existing users/teams."]
    end
    style SG21 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG22 ["apps/meteor/app/lib/server/functions/deleteRoom.ts"]
        deleteRoom["deleteRoom"]
    end
    style SG22 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG23 ["apps/meteor/app/lib/server/functions/deleteUser.ts"]
        deleteUser["Deletes a user account, cleans up associated data (messages, subscriptions, files, roles), and triggers cleanup callbacks."]
    end
    style SG23 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG24 ["apps/meteor/app/lib/server/functions/getAvatarSuggestionForUser.ts"]
        getAvatarSuggestionForUser["Fetches and processes potential user avatar URLs from various social/OAuth providers and Gravatar."]
    end
    style SG24 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG25 ["apps/meteor/app/lib/server/functions/getDefaultChannels.ts"]
        getDefaultChannels["getDefaultChannels"]
    end
    style SG25 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG26 ["apps/meteor/app/lib/server/functions/getFullUserData.ts"]
        getCustomFields["getCustomFields"]
        getFields["getFields"]
        findTargetUser["findTargetUser"]
        getFullUserDataByUniqueSearchTerm["Retrieves a full user object based on a unique search term, enforcing authorization checks and filtering sensitive fields."]
    end
    style SG26 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG27 ["apps/meteor/app/lib/server/functions/getRoomsWithSingleOwner.ts"]
        shouldRemoveOrChangeOwner["shouldRemoveOrChangeOwner"]
        getSubscribedRoomsForUserWithDetails["getSubscribedRoomsForUserWithDetails"]
    end
    style SG27 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG28 ["apps/meteor/app/lib/server/functions/getUserSingleOwnedRooms.ts"]
        getUserSingleOwnedRooms["getUserSingleOwnedRooms"]
    end
    style SG28 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG29 ["apps/meteor/app/lib/server/functions/getUsernameSuggestion.ts"]
        slug["slug"]
        usernameIsAvailable["usernameIsAvailable"]
        name["name"]
        generateUsernameSuggestion["Generates a unique username suggestion for a user based on their name, email, or OAuth services."]
    end
    style SG29 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG30 ["apps/meteor/app/lib/server/functions/joinDefaultChannels.ts"]
        joinDefaultChannels["joinDefaultChannels"]
    end
    style SG30 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG31 ["apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts"]
        bulkTeamCleanup["bulkTeamCleanup"]
        bulkRoomCleanUp["bulkRoomCleanUp"]
        relinquishRoomOwnerships["relinquishRoomOwnerships"]
    end
    style SG31 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG32 ["apps/meteor/app/lib/server/functions/saveCustomFields.ts"]
        saveCustomFields["Validates and saves custom user profile fields to the database."]
    end
    style SG32 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG33 ["apps/meteor/app/lib/server/functions/saveCustomFieldsWithoutValidation.ts"]
        getCustomFieldsMeta["getCustomFieldsMeta"]
        saveCustomFieldsWithoutValidation["Saves custom user fields to the database without performing additional validation."]
    end
    style SG33 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG34 ["apps/meteor/app/lib/server/functions/saveUser/index.ts"]
        ._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts["Exports core user saving and validation functions for use in other modules."]
    end
    style SG34 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG35 ["apps/meteor/app/lib/server/functions/saveUser/sendUserEmail.ts"]
        sendUserEmail["sendUserEmail"]
        sendWelcomeEmail["Sends a welcome email to a new user upon registration."]
    end
    style SG35 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG36 ["apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts"]
        canEditExtension["Validates if a specific VoIP extension can be assigned to a user."]
    end
    style SG36 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG37 ["apps/meteor/app/lib/server/functions/saveUserIdentity.ts"]
        saveUserIdentity["saveUserIdentity"]
        updateUsernameReferences["updateUsernameReferences"]
    end
    style SG37 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG38 ["apps/meteor/app/lib/server/functions/setRealName.ts"]
        setRealName["setRealName"]
    end
    style SG38 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG39 ["apps/meteor/app/lib/server/functions/setStatusText.ts"]
        setStatusText["Updates a user's status text, truncates it to 120 characters, persists to the database, and broadcasts the update."]
    end
    style SG39 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG40 ["apps/meteor/app/lib/server/functions/setUserAvatar.ts"]
        setAvatarFromServiceWithValidation["setAvatarFromServiceWithValidation"]
        setUserAvatar["Sets a user's avatar from a data URI, URL, or REST input, handling file storage and database updates."]
    end
    style SG40 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG41 ["apps/meteor/app/lib/server/functions/setUsername.ts"]
        isUserInFederatedRooms["isUserInFederatedRooms"]
        setUsernameWithValidation["Validates and updates a user's username, ensuring uniqueness and compliance with federation rules."]
        setUsername["_setUsername"]
    end
    style SG41 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG42 ["apps/meteor/app/lib/server/functions/updateGroupDMsName.ts"]
        getFname["getFname"]
        getName["getName"]
        getUsersWhoAreInTheSameGroupDMsAs["getUsersWhoAreInTheSameGroupDMsAs"]
        updateGroupDMsName["updateGroupDMsName"]
        getMembers["getMembers"]
    end
    style SG42 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG43 ["apps/meteor/app/lib/server/functions/validateCustomFields.js"]
        validateCustomFields["Validates user-defined custom fields against configured schema, ensuring required fields, types, and lengths are respected."]
    end
    style SG43 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG44 ["apps/meteor/app/lib/server/functions/validateName.ts"]
        validateName["validateName"]
    end
    style SG44 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG45 ["apps/meteor/app/lib/server/functions/validateNameChars.ts"]
        validateNameChars["Validates a name string for invalid characters, including URI-decoded checks."]
    end
    style SG45 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG46 ["apps/meteor/app/lib/server/functions/validateUsername.ts"]
        validateUsername["Validates a username string against a configurable regular expression pattern."]
    end
    style SG46 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG47 ["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 SG47 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG48 ["apps/meteor/app/lib/server/lib/deprecationWarningLogger.ts"]
        compareVersions["Compares a version string against the threshold for throwing deprecation errors."]
        method["Logs deprecation warnings for methods, including replacement information and metrics."]
        warn["Logs a generic deprecation warning."]
    end
    style SG48 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG49 ["apps/meteor/app/lib/server/lib/notifyListener.ts"]
        notifyOnRoomChangedById["notifyOnRoomChangedById"]
        notifyOnRoomChangedByUsernamesOrUids["notifyOnRoomChangedByUsernamesOrUids"]
        notifyOnIntegrationChangedByUserId["notifyOnIntegrationChangedByUserId"]
        notifyOnLivechatDepartmentAgentChanged["notifyOnLivechatDepartmentAgentChanged"]
        notifyOnSettingChanged["notifyOnSettingChanged"]
        notifyOnSettingChangedById["notifyOnSettingChangedById"]
        notifyOnUserChange["Broadcasts a notification about a user record change."]
        notifyOnUserChangeAsync["Executes a callback and notifies on user change if watchers are disabled."]
        notifyOnSubscriptionChanged["Broadcasts a notification when a subscription is changed."]
        notifyOnSubscriptionChangedByRoomIdAndUserId["Broadcasts a notification for a subscription identified by room ID and user ID."]
        notifyOnSubscriptionChangedById["notifyOnSubscriptionChangedById"]
        notifyOnSubscriptionChangedByUserPreferences["notifyOnSubscriptionChangedByUserPreferences"]
        notifyOnSubscriptionChangedByRoomId["notifyOnSubscriptionChangedByRoomId"]
        notifyOnSubscriptionChangedByAutoTranslateAndUserId["notifyOnSubscriptionChangedByAutoTranslateAndUserId"]
        notifyOnSubscriptionChangedByUserIdAndRoomType["notifyOnSubscriptionChangedByUserIdAndRoomType"]
        notifyOnSubscriptionChangedByNameAndRoomType["notifyOnSubscriptionChangedByNameAndRoomType"]
        notifyOnSubscriptionChangedByUserId["notifyOnSubscriptionChangedByUserId"]
    end
    style SG49 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG50 ["apps/meteor/app/lib/server/methods/createToken.ts"]
        generateAccessToken["Generates an access token for a given user if the provided secret matches the environment variable."]
    end
    style SG50 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG51 ["apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts"]
        deleteUserOwnAccount["Handles the deletion of a user's own account after verifying their password and system settings."]
    end
    style SG51 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG52 ["apps/meteor/app/mailer/server/api.ts"]
        replacekey["replacekey"]
        translate["translate"]
        replace["replace"]
        replaceEscaped["replaceEscaped"]
        wrap["wrap"]
        checkAddressFormat["checkAddressFormat"]
        sendNoWrap["sendNoWrap"]
        send["send"]
    end
    style SG52 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG53 ["apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts"]
        getDefaultSubscriptionPref["getDefaultSubscriptionPref"]
    end
    style SG53 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG54 ["apps/meteor/app/utils/lib/getURL.ts"]
        getCloudUrl["getCloudUrl"]
        getURL["_getURL"]
        getURLWithoutSettings["getURLWithoutSettings"]
    end
    style SG54 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG55 ["apps/meteor/app/utils/lib/mimeTypes.ts"]
        getMimeTypeFromFileName["getMimeTypeFromFileName"]
        getMimeType["getMimeType"]
    end
    style SG55 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG56 ["apps/meteor/app/utils/server/functions/isSMTPConfigured.ts"]
        isSMTPConfigured["Checks if SMTP is configured by verifying the MAIL_URL environment variable or SMTP_Host setting."]
    end
    style SG56 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG57 ["apps/meteor/app/utils/server/getURL.ts"]
        getURL_2["Generates a URL for a given path, incorporating site URL and CDN settings."]
    end
    style SG57 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG58 ["apps/meteor/app/utils/server/lib/getUserPreference.ts"]
        getUserPreference["getUserPreference"]
    end
    style SG58 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG59 ["apps/meteor/client/meteor/overrides/userAndUsers.ts"]
        Meteor.userId["Overrides Meteor.userId to return the current user ID from the local store."]
    end
    style SG59 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG60 ["apps/meteor/client/meteor/user.ts"]
        watchUserId["Watches and returns the current user ID."]
    end
    style SG60 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG61 ["apps/meteor/client/meteor/watch.ts"]
        watch["watch"]
    end
    style SG61 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG62 ["apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts"]
        generatePersonalAccessTokenOfUser["Generates a new personal access token for a user after checking permissions and ensuring uniqueness."]
    end
    style SG62 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG63 ["apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts"]
        regeneratePersonalAccessTokenOfUser["Regenerates a personal access token for a user after verifying authorization and token existence."]
    end
    style SG63 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG64 ["apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts"]
        removePersonalAccessTokenOfUser["Removes a personal access token for a user after verifying authorization."]
    end
    style SG64 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG65 ["apps/meteor/lib/roles/calculateRoomRolePriorityFromRoles.ts"]
        getRoomRolePriorityForRole["getRoomRolePriorityForRole"]
        calculateRoomRolePriorityFromRoles["calculateRoomRolePriorityFromRoles"]
    end
    style SG65 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG66 ["apps/meteor/lib/utils/isObject.ts"]
        isObject["isObject"]
    end
    style SG66 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG67 ["apps/meteor/lib/utils/parseCSV.ts"]
        parseCSV["parseCSV"]
    end
    style SG67 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG68 ["apps/meteor/lib/utils/stringUtils.ts"]
        makeString["makeString"]
        defaultToWhiteSpace["defaultToWhiteSpace"]
        trim["trim"]
        ltrim["ltrim"]
        rtrim["rtrim"]
        strLeft["strLeft"]
        strRightBack["strRightBack"]
    end
    style SG68 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG69 ["apps/meteor/server/database/utils.ts"]
        isExtendedSession["isExtendedSession"]
        onceTransactionCommitedSuccessfully["onceTransactionCommitedSuccessfully"]
        withError["withError"]
    end
    style SG69 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG70 ["apps/meteor/server/hooks/userLogoutCleanUp.ts"]
        runUserLogoutCleanUp["Triggers cleanup operations after a user logs out."]
    end
    style SG70 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG71 ["apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts"]
        getSubscriptionAutotranslateDefaultConfig["getSubscriptionAutotranslateDefaultConfig"]
    end
    style SG71 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG72 ["apps/meteor/server/lib/isUserIdFederated.ts"]
        isUserIdFederated["isUserIdFederated"]
    end
    style SG72 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG73 ["apps/meteor/server/lib/resetUserE2EKey.ts"]
        sendResetNotification["sendResetNotification"]
        resetUserE2EEncriptionKey["Resets a user's E2E encryption key and forces a re-login."]
    end
    style SG73 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG74 ["apps/meteor/server/lib/roles/addUserRoles.ts"]
        addUserRolesAsync["Adds roles to a user, optionally scoped to a specific room."]
    end
    style SG74 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG75 ["apps/meteor/server/lib/roles/removeUserFromRoles.ts"]
        removeUserFromRolesAsync["Removes specified roles from a user, optionally scoped to a room."]
    end
    style SG75 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG76 ["apps/meteor/server/lib/roles/syncRoomRolePriority.ts"]
        syncRoomRolePriorityForUserAndRoom["Synchronizes the room role priority for a user in a specific room."]
        updateRolePriority["updateRolePriority"]
    end
    style SG76 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG77 ["apps/meteor/server/lib/roles/validateRoleList.ts"]
        validateRoleList["Validates a list of role IDs against existing roles in the database."]
    end
    style SG77 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG78 ["apps/meteor/server/lib/rooms/roomCoordinator.ts"]
        RoomCoordinatorServer.allowMemberAction["RoomCoordinatorServer.allowMemberAction"]
        RoomCoordinatorServer.getRoomDirectives["Retrieves the server-side directives for a specific room type."]
    end
    style SG78 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG79 ["apps/meteor/server/lib/users/runAfterVerifyEmail.ts"]
        runAfterVerifyEmail["Updates user roles after email verification."]
    end
    style SG79 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG80 ["apps/meteor/server/methods/registerUser.ts"]
        registerUser["Registers a new user, handles anonymous access, and validates registration requirements."]
    end
    style SG80 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG81 ["apps/meteor/server/methods/requestDataDownload.ts"]
        requestDataDownload["Requests a data export operation for the user."]
    end
    style SG81 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG82 ["apps/meteor/server/methods/resetAvatar.ts"]
        resetAvatar["Resets a user's avatar after authorization checks."]
        userId["userId"]
    end
    style SG82 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG83 ["apps/meteor/server/methods/saveUserPreferences.ts"]
        updateNotificationPreferences["updateNotificationPreferences"]
        saveUserPreferences["Updates user preferences and propagates notification changes."]
    end
    style SG83 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG84 ["apps/meteor/server/methods/sendConfirmationEmail.ts"]
        sendConfirmationEmail["Sends a verification email to a user based on their email address."]
    end
    style SG84 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG85 ["apps/meteor/server/methods/sendForgotPasswordEmail.ts"]
        sendForgotPasswordEmail["Sends a password reset email to a user if they exist."]
    end
    style SG85 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG86 ["apps/meteor/server/methods/setUserActiveStatus.ts"]
        executeSetUserActiveStatus["Updates the active status of a user, requiring administrative permissions."]
        setUserActiveStatus["setUserActiveStatus"]
    end
    style SG86 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG87 ["apps/meteor/server/services/authorization/service.ts"]
        Authorization.hasPermission["Checks if a user has a specific permission, optionally scoped to a room."]
        Authorization.all["Authorization.all"]
    end
    style SG87 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG88 ["apps/meteor/server/services/user/lib/getNewUserRoles.ts"]
        getNewUserRoles["getNewUserRoles"]
    end
    style SG88 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG89 ["ee/apps/account-service/src/lib/utils.ts"]
        generateStampedLoginToken["_generateStampedLoginToken"]
        hashLoginToken["Hashes a login token using SHA-256 for secure storage."]
    end
    style SG89 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG90 ["packages/models/src/models/BaseRaw.ts"]
        BaseRaw.doNotMixInclusionAndExclusionFields["BaseRaw.doNotMixInclusionAndExclusionFields"]
        BaseRaw.ensureDefaultFields["BaseRaw.ensureDefaultFields"]
        BaseRaw.find["BaseRaw.find"]
        BaseRaw.updateOne["BaseRaw.updateOne"]
        BaseRaw.updateMany["BaseRaw.updateMany"]
        BaseRaw.deleteMany["BaseRaw.deleteMany"]
        BaseRaw.setUpdatedAt["BaseRaw.setUpdatedAt"]
    end
    style SG90 fill:#2a2a2a,stroke:#444,color:#aaa
    subgraph SG91 ["packages/models/src/models/LivechatDepartmentAgents.ts"]
        LivechatDepartmentAgentsRaw.findByAgentId["LivechatDepartmentAgentsRaw.findByAgentId"]
        LivechatDepartmentAgentsRaw.removeByAgentId["LivechatDepartmentAgentsRaw.removeByAgentId"]
        LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId["LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId"]
    end
    style SG91 fill:#2a2a2a,stroke:#444,color:#aaa
    action --> hashLoginToken
    action --> getFullUserDataByUniqueSearchTerm
    action --> deleteUser
    action --> generateUsernameSuggestion
    action --> validateCustomFields
    action --> saveCustomFields
    action --> getAvatarSuggestionForUser
    action --> checkUsernameAvailabilityWithValidation
    action --> checkUsernameAvailability
    action --> validateUsername
    action --> validateNameChars
    action --> saveCustomFieldsWithoutValidation
    action --> canEditExtension
    action --> ._Rocket.Chat_apps_meteor_app_lib_server_functions_saveUser_index.ts
    action --> sendWelcomeEmail
    action --> setUserAvatar
    action --> setStatusText
    action --> checkEmailAvailability
    action --> setUsernameWithValidation
    action --> notifyOnUserChange
    action --> notifyOnUserChangeAsync
    action --> deleteUserOwnAccount
    action --> generateAccessToken
    action --> resetUserE2EEncriptionKey
    action --> sendForgotPasswordEmail
    action --> executeSetUserActiveStatus
    action --> resetAvatar
    action --> sendConfirmationEmail
    action --> requestDataDownload
    action --> registerUser
    action --> saveUserPreferences
    action --> removePersonalAccessTokenOfUser
    action --> generatePersonalAccessTokenOfUser
    action --> regeneratePersonalAccessTokenOfUser
    action --> resetTOTP
    action --> getUserForCheck
    action --> isSMTPConfigured
    action --> hasPermissionAsync
    action --> findUsersToAutocomplete
    action --> findPaginatedUsersByStatus
    action --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
    action --> getUploadFormData
    action --> APIClass.success
    action --> APIClass.failure
    action --> APIClass.forbidden
    action --> APIClass.this.parseJsonQuery
    action --> getUserInfo
    action --> getPaginationItems
    action --> getUserFromParams
    action --> isUserFromParams
    action --> runUserLogoutCleanUp
    action --> runAfterVerifyEmail
    action --> get
    getFullUserDataByUniqueSearchTerm --> getFields
    getFullUserDataByUniqueSearchTerm --> findTargetUser
    getFullUserDataByUniqueSearchTerm --> hasPermissionAsync
    deleteUser --> shouldRemoveOrChangeOwner
    deleteUser --> getSubscribedRoomsForUserWithDetails
    deleteUser --> getUserSingleOwnedRooms
    deleteUser --> updateGroupDMsName
    deleteUser --> relinquishRoomOwnerships
    deleteUser --> notifyOnRoomChangedById
    deleteUser --> notifyOnIntegrationChangedByUserId
    deleteUser --> notifyOnLivechatDepartmentAgentChanged
    deleteUser --> notifyOnUserChange
    deleteUser --> FileUpload.getStore
    deleteUser --> LivechatDepartmentAgentsRaw.findByAgentId
    deleteUser --> LivechatDepartmentAgentsRaw.removeByAgentId
    generateUsernameSuggestion --> slug
    generateUsernameSuggestion --> usernameIsAvailable
    generateUsernameSuggestion --> name
    validateCustomFields --> trim
    saveCustomFields --> validateCustomFields
    saveCustomFields --> saveCustomFieldsWithoutValidation
    saveCustomFields --> trim
    checkUsernameAvailabilityWithValidation --> checkUsernameAvailability
    checkUsernameAvailability --> toRegExp
    checkUsernameAvailability --> usernameIsBlocked
    checkUsernameAvailability --> validateName
    saveCustomFieldsWithoutValidation --> getCustomFieldsMeta
    saveCustomFieldsWithoutValidation --> notifyOnSubscriptionChangedByUserIdAndRoomType
    saveCustomFieldsWithoutValidation --> onceTransactionCommitedSuccessfully
    saveCustomFieldsWithoutValidation --> trim
    sendWelcomeEmail --> sendUserEmail
    setUserAvatar --> onceTransactionCommitedSuccessfully
    setUserAvatar --> FileUpload.getStore
    setUserAvatar --> RocketChatFile.dataURIParse
    setStatusText --> onceTransactionCommitedSuccessfully
    setUsernameWithValidation --> checkUsernameAvailability
    setUsernameWithValidation --> saveUserIdentity
    setUsernameWithValidation --> validateUsername
    setUsernameWithValidation --> joinDefaultChannels
    setUsernameWithValidation --> isUserInFederatedRooms
    setUsernameWithValidation --> notifyOnUserChange
    notifyOnUserChangeAsync --> notifyOnUserChange
    deleteUserOwnAccount --> deleteUser
    deleteUserOwnAccount --> method
    deleteUserOwnAccount --> deleteUserOwnAccount
    deleteUserOwnAccount --> trim
    deleteUserOwnAccount --> Meteor.userId
    generateAccessToken --> generateStampedLoginToken
    resetUserE2EEncriptionKey --> isUserIdFederated
    resetUserE2EEncriptionKey --> notifyOnUserChange
    resetUserE2EEncriptionKey --> notifyOnSubscriptionChangedByUserId
    resetUserE2EEncriptionKey --> sendResetNotification
    sendForgotPasswordEmail --> sendForgotPasswordEmail
    executeSetUserActiveStatus --> setUserActiveStatus
    executeSetUserActiveStatus --> hasPermissionAsync
    resetAvatar --> method
    resetAvatar --> resetAvatar
    resetAvatar --> userId
    resetAvatar --> hasPermissionAsync
    requestDataDownload --> method
    requestDataDownload --> requestDataDownload
    registerUser --> generateStampedLoginToken
    registerUser --> ._Rocket.Chat_apps_meteor_app_lib_server_index.ts
    registerUser --> Accounts.insertUserDoc
    registerUser --> registerUser
    registerUser --> validateInviteToken
    registerUser --> trim
    saveUserPreferences --> notifyOnUserChange
    saveUserPreferences --> notifyOnSubscriptionChangedByAutoTranslateAndUserId
    saveUserPreferences --> notifyOnSubscriptionChangedByUserId
    saveUserPreferences --> method
    saveUserPreferences --> updateNotificationPreferences
    saveUserPreferences --> saveUserPreferences
    saveUserPreferences --> Meteor.userId
    removePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
    removePersonalAccessTokenOfUser --> hasPermissionAsync
    generatePersonalAccessTokenOfUser --> hashLoginToken
    generatePersonalAccessTokenOfUser --> hasPermissionAsync
    regeneratePersonalAccessTokenOfUser --> removePersonalAccessTokenOfUser
    regeneratePersonalAccessTokenOfUser --> generatePersonalAccessTokenOfUser
    regeneratePersonalAccessTokenOfUser --> hasPermissionAsync
    resetTOTP --> isUserIdFederated
    resetTOTP --> notifyOnUserChange
    resetTOTP --> sendResetNotification_2
    hasPermissionAsync --> Authorization.hasPermission
    findUsersToAutocomplete --> hasPermissionAsync
    findPaginatedUsersByStatus --> hasPermissionAsync
    getUploadFormData --> getMimeType
    APIClass.success --> isObject
    APIClass.failure --> isObject
    APIClass.this.parseJsonQuery --> APIClass.this.parseJsonQuery
    getUserInfo --> getURL_2
    getUserInfo --> isVerifiedEmail
    getUserInfo --> getUserPreferences
    getUserInfo --> filterOutdatedVersionUpdateBanners
    getUserInfo --> getUserCalendar
    runAfterVerifyEmail --> removeUserFromRolesAsync
    runAfterVerifyEmail --> addUserRolesAsync
    get --> getURL_2
    get --> hasPermissionAsync
    get --> getInclusiveFields
    get --> getNonEmptyFields
    get --> getNonEmptyQuery
    get --> ._Rocket.Chat_apps_meteor_app_api_server_lib_isValidQuery.ts
    get --> APIClass.success
    get --> APIClass.forbidden
    get --> APIClass.this.parseJsonQuery
    get --> getPaginationItems
    get --> getUserFromParams
    get --> get
    getFields --> getCustomFields
    getSubscribedRoomsForUserWithDetails --> ._Rocket.Chat_apps_meteor_app_authorization_server_index.ts
    updateGroupDMsName --> getFname
    updateGroupDMsName --> getName
    updateGroupDMsName --> getUsersWhoAreInTheSameGroupDMsAs
    updateGroupDMsName --> getMembers
    updateGroupDMsName --> notifyOnSubscriptionChangedByRoomId
    relinquishRoomOwnerships --> bulkRoomCleanUp
    relinquishRoomOwnerships --> addUserRolesAsync
    FileUpload.getStore --> FileUpload.getStoreByName
    FileUpload.getStore --> FileUpload.get
    LivechatDepartmentAgentsRaw.findByAgentId --> BaseRaw.find
    LivechatDepartmentAgentsRaw.removeByAgentId --> BaseRaw.deleteMany
    name --> slug
    trim --> makeString
    trim --> defaultToWhiteSpace
    onceTransactionCommitedSuccessfully --> isExtendedSession
    onceTransactionCommitedSuccessfully --> withError
    sendUserEmail --> send
    saveUserIdentity --> updateUsernameReferences
    saveUserIdentity --> validateName
    saveUserIdentity --> setRealName
    saveUserIdentity --> setUsername
    saveUserIdentity --> onceTransactionCommitedSuccessfully
    joinDefaultChannels --> addUserToDefaultChannels
    method --> compareVersions
    method --> warn
    Meteor.userId --> watchUserId
    sendResetNotification --> send
    setUserActiveStatus --> executeSetUserActiveStatus
    setUserActiveStatus --> Meteor.userId
    Accounts.insertUserDoc --> getAvatarSuggestionForUser
    Accounts.insertUserDoc --> setAvatarFromServiceWithValidation
    Accounts.insertUserDoc --> joinDefaultChannels
    Accounts.insertUserDoc --> notifyOnSettingChangedById
    Accounts.insertUserDoc --> addUserRolesAsync
    Accounts.insertUserDoc --> getNewUserRoles
    Accounts.insertUserDoc --> parseCSV
    updateNotificationPreferences --> notifyOnSubscriptionChangedByUserPreferences
    sendResetNotification_2 --> send
    Authorization.hasPermission --> Authorization.all
    getMimeType --> getMimeTypeFromFileName
    getURL_2 --> getURLWithoutSettings
    getUserPreferences --> getUserPreference
    removeUserFromRolesAsync --> notifyOnSubscriptionChangedByRoomIdAndUserId
    removeUserFromRolesAsync --> syncRoomRolePriorityForUserAndRoom
    removeUserFromRolesAsync --> validateRoleList
    addUserRolesAsync --> notifyOnSubscriptionChangedByRoomIdAndUserId
    addUserRolesAsync --> syncRoomRolePriorityForUserAndRoom
    addUserRolesAsync --> validateRoleList
    bulkRoomCleanUp --> bulkTeamCleanup
    bulkRoomCleanUp --> notifyOnSubscriptionChanged
    bulkRoomCleanUp --> FileUpload.removeFilesByRoomId
    bulkRoomCleanUp --> eraseRoomLooseValidation
    FileUpload.get --> FileUpload.getStoreByName
    FileUpload.get --> FileUpload.get
    BaseRaw.find --> BaseRaw.doNotMixInclusionAndExclusionFields
    BaseRaw.find --> BaseRaw.find
    BaseRaw.deleteMany --> BaseRaw.find
    BaseRaw.deleteMany --> BaseRaw.updateOne
    BaseRaw.deleteMany --> BaseRaw.deleteMany
    defaultToWhiteSpace --> makeString
    send --> replace
    send --> wrap
    send --> sendNoWrap
    updateUsernameReferences --> updateGroupDMsName
    updateUsernameReferences --> notifyOnRoomChangedByUsernamesOrUids
    updateUsernameReferences --> notifyOnSubscriptionChangedByNameAndRoomType
    updateUsernameReferences --> notifyOnSubscriptionChangedByUserId
    updateUsernameReferences --> FileUpload.getStore
    updateUsernameReferences --> LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId
    setRealName --> onceTransactionCommitedSuccessfully
    setUsername --> addUserToRoom
    setUsername --> getAvatarSuggestionForUser
    setUsername --> checkUsernameAvailability
    setUsername --> validateUsername
    setUsername --> setUserAvatar
    setUsername --> isUserInFederatedRooms
    setUsername --> onceTransactionCommitedSuccessfully
    addUserToDefaultChannels --> getDefaultChannels
    addUserToDefaultChannels --> notifyOnSubscriptionChangedById
    addUserToDefaultChannels --> getSubscriptionAutotranslateDefaultConfig
    addUserToDefaultChannels --> getDefaultSubscriptionPref
    warn --> compareVersions
    warn --> warn
    watchUserId --> watch
    setAvatarFromServiceWithValidation --> setUserAvatar
    setAvatarFromServiceWithValidation --> hasPermissionAsync
    getNewUserRoles --> parseCSV
    getURLWithoutSettings --> getURL
    syncRoomRolePriorityForUserAndRoom --> updateRolePriority
    syncRoomRolePriorityForUserAndRoom --> calculateRoomRolePriorityFromRoles
    bulkTeamCleanup --> eraseTeamOnRelinquishRoomOwnerships
    FileUpload.removeFilesByRoomId --> FileUpload.getStore
    FileUpload.removeFilesByRoomId --> FileUploadClass.deleteById
    eraseRoomLooseValidation --> deleteRoom
    BaseRaw.doNotMixInclusionAndExclusionFields --> BaseRaw.ensureDefaultFields
    BaseRaw.updateOne --> BaseRaw.updateOne
    BaseRaw.updateOne --> BaseRaw.setUpdatedAt
    replace --> replacekey
    replace --> translate
    replace --> replace
    replace --> strLeft
    replace --> strRightBack
    wrap --> replace
    wrap --> replaceEscaped
    sendNoWrap --> notifyOnSettingChanged
    sendNoWrap --> checkAddressFormat
    LivechatDepartmentAgentsRaw.replaceUsernameOfAgentByUserId --> BaseRaw.updateMany
    addUserToRoom --> notifyOnRoomChangedById
    addUserToRoom --> notifyOnSubscriptionChanged
    addUserToRoom --> RoomCoordinatorServer.allowMemberAction
    addUserToRoom --> RoomCoordinatorServer.getRoomDirectives
    getURL --> getCloudUrl
    getURL --> trim
    getURL --> ltrim
    getURL --> rtrim
    updateRolePriority --> calculateRoomRolePriorityFromRoles
    calculateRoomRolePriorityFromRoles --> getRoomRolePriorityForRole
    eraseTeamOnRelinquishRoomOwnerships --> eraseRoomLooseValidation
    FileUploadClass.deleteById --> FileUpload.getStoreByName
    deleteRoom --> notifyOnRoomChangedById
    deleteRoom --> notifyOnSubscriptionChanged
    deleteRoom --> FileUpload.getStore
    deleteRoom --> FileUpload.removeFilesByRoomId
    BaseRaw.setUpdatedAt --> BaseRaw.setUpdatedAt
    replacekey --> replace
    strLeft --> makeString
    strRightBack --> makeString
    replaceEscaped --> replace
    BaseRaw.updateMany --> BaseRaw.updateMany
    BaseRaw.updateMany --> BaseRaw.setUpdatedAt
    getCloudUrl --> ltrim
    getCloudUrl --> rtrim
    ltrim --> makeString
    ltrim --> defaultToWhiteSpace
    rtrim --> makeString
    rtrim --> defaultToWhiteSpace
Loading
Fix with AI

Open in Cursor Open in Claude

Fix the following security vulnerability found by Hacktron.

File: apps/meteor/app/api/server/v1/users.ts
Lines: 2118-2130
Severity: high

Vulnerability: Missing authorization check in users.update API allows account takeover and privilege escalation

Description:
The `users.update` API endpoint does not enforce sufficient authorization checks before calling `saveUser` to update user information. While it requires 2FA, it lacks explicit permission requirements (e.g., `edit-other-user-info`), potentially allowing authenticated users to modify the profiles of other users.

Affected Code:
API.v1.post(
	'users.update',
	{
		authRequired: true,
		twoFactorRequired: true,
		body: isUsersUpdateParamsPOST,
		response: {
			200: userObjectResponse,
			400: validateBadRequestErrorResponse,
			401: validateUnauthorizedErrorResponse,
		},
	},
	async function action() {
		// ...
		await saveUser(this.userId, userData, { auditStore });
        // ...
	},
);

Fix this vulnerability. Only change what's necessary - don't modify unrelated code.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), or !accepted_risk <reason>. Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing. Any other reply is saved as a triage note.

View finding in Hacktron

- users.ts setAvatar: cast service through 'rest' overload literal since
  the helper accepts arbitrary strings at runtime but the TS overload
  for Buffer dataURI restricts the type.
- UserProvider: restore the local logout Emitter for onLogout(cb)
  consumers (e2ee cleanup, etc.) — only the sdk.call('logoutCleanUp')
  side effect is gone, the in-process broadcaster stays.
- useOpenRoom: cast Partial<IRoom> response through unknown to IRoom to
  match downstream consumers that expect the full shape.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/meteor/client/providers/UserProvider/UserProvider.tsx (1)

32-36: 💤 Low value

Drop the implementation comment.

This explanatory block belongs in the PR description/commit message rather than inline. As per coding guidelines, "Avoid code comments in the implementation".

♻️ Proposed change
-// Local logout broadcaster — `onLogout(cb)` consumers (e.g. e2ee cleanup) still
-// subscribe to this. The post-logout side effects that used to require a
-// `sdk.call('logoutCleanUp')` round-trip (afterLogoutCleanUpCallback +
-// Apps.IPostUserLoggedOut) now fire server-side via `Accounts.onLogout` and
-// `POST /v1/users.logout`, so this emitter is purely client-side fan-out.
 const ee = new Emitter();
🤖 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/providers/UserProvider/UserProvider.tsx` around lines 32 -
36, Remove the explanatory implementation comment block above the local logout
broadcaster and associated notes; specifically delete the multi-line comment
that references onLogout(cb), afterLogoutCleanUpCallback, Accounts.onLogout,
POST /v1/users.logout, and Apps.IPostUserLoggedOut so only the code implementing
the local logout emitter remains (no inline explanation).
🤖 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.

Nitpick comments:
In `@apps/meteor/client/providers/UserProvider/UserProvider.tsx`:
- Around line 32-36: Remove the explanatory implementation comment block above
the local logout broadcaster and associated notes; specifically delete the
multi-line comment that references onLogout(cb), afterLogoutCleanUpCallback,
Accounts.onLogout, POST /v1/users.logout, and Apps.IPostUserLoggedOut so only
the code implementing the local logout emitter remains (no inline explanation).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3e657c99-f150-4d26-b826-f87b02e52185

📥 Commits

Reviewing files that changed from the base of the PR and between 29d71a2 and 53ee70c.

📒 Files selected for processing (3)
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/client/providers/UserProvider/UserProvider.tsx
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/meteor/client/views/room/hooks/useOpenRoom.ts
  • apps/meteor/app/api/server/v1/users.ts
📜 Review details
🧰 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/providers/UserProvider/UserProvider.tsx
🧠 Learnings (2)
📚 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/providers/UserProvider/UserProvider.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/providers/UserProvider/UserProvider.tsx
🔇 Additional comments (2)
apps/meteor/client/providers/UserProvider/UserProvider.tsx (2)

2-2: LGTM!

Also applies to: 22-22


37-38: LGTM!

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/app/api/server/v1/users.ts">

<violation number="1" location="apps/meteor/app/api/server/v1/users.ts:350">
P1: Unsafe type assertion `as 'rest'` on a value that may not be `'rest'`. If `fields.service` provides a value like an OAuth provider name (e.g., `"github"`), the cast silences the type error without changing runtime behavior: `setUserAvatar` will receive the non-`'rest'` value, bypass the `service === 'rest'` branch, and fall into `RocketChatFile.dataURIParse(dataURI)` with a `Buffer` argument — which expects a string and will fail at runtime.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

await setUserAvatar(user, fileBuffer, mimetype, 'rest');
const service = typeof fields.service === 'string' && fields.service.length > 0 ? fields.service : 'rest';

await setUserAvatar(user, fileBuffer, mimetype, service as 'rest');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unsafe type assertion as 'rest' on a value that may not be 'rest'. If fields.service provides a value like an OAuth provider name (e.g., "github"), the cast silences the type error without changing runtime behavior: setUserAvatar will receive the non-'rest' value, bypass the service === 'rest' branch, and fall into RocketChatFile.dataURIParse(dataURI) with a Buffer argument — which expects a string and will fail at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/api/server/v1/users.ts, line 350:

<comment>Unsafe type assertion `as 'rest'` on a value that may not be `'rest'`. If `fields.service` provides a value like an OAuth provider name (e.g., `"github"`), the cast silences the type error without changing runtime behavior: `setUserAvatar` will receive the non-`'rest'` value, bypass the `service === 'rest'` branch, and fall into `RocketChatFile.dataURIParse(dataURI)` with a `Buffer` argument — which expects a string and will fail at runtime.</comment>

<file context>
@@ -347,7 +347,7 @@ API.v1
 			const service = typeof fields.service === 'string' && fields.service.length > 0 ? fields.service : 'rest';
 
-			await setUserAvatar(user, fileBuffer, mimetype, service);
+			await setUserAvatar(user, fileBuffer, mimetype, service as 'rest');
 
 			return API.v1.success();
</file context>
Suggested change
await setUserAvatar(user, fileBuffer, mimetype, service as 'rest');
if (service !== 'rest') {
return API.v1.failure("The 'service' param must be 'rest' when uploading a file");
}
await setUserAvatar(user, fileBuffer, mimetype, service);

Comment thread apps/meteor/server/publications/room/index.ts Outdated
tassoevan
tassoevan previously approved these changes May 29, 2026
Per review feedback — getRoomByTypeAndName may be replaced by a
breaking change (e.g. URLs moving to point directly to rid), so the
DDP method stays as-is and the publications/room/index.ts file is
restored to its develop baseline. Removed:

- GET /v1/rooms.getByTypeAndName endpoint + rest-typings entry
- getRoomByTypeAndNameMethod extraction + deprecation log
- Client callers (useOpenRoom, EmbeddedPreload) restored to useMethod

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ggazzo ggazzo changed the title chore: migrate batch4 client DDP callers + add 7 REST endpoints chore: migrate batch4 client DDP callers + add 6 REST endpoints May 29, 2026
@tassoevan tassoevan changed the title chore: migrate batch4 client DDP callers + add 6 REST endpoints feat: migrate batch4 client DDP callers + add 6 REST endpoints May 29, 2026
tassoevan
tassoevan previously approved these changes May 29, 2026
@coderabbitai coderabbitai Bot added type: feature Pull requests that introduces new feature and removed type: chore labels Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants