Repository: RocketChat/Rocket.Chat
Pull request #35829: regression: Apps APIs not parsing urlencoded
Author: login: sampaiodiego
Labels: name: stat: ready to merge
description: PR tested and approved waiting for merge
name: stat: QA assured
description: Means it has been tested and approved by a company insider
Created: 2025-04-16T20:55:10Z | Merged: 2025-04-17T14:42:36Z

## Pull Request Description
[rocketchat.atlassian.net/browse/ARCH-1485](https://rocketchat.atlassian.net/browse/ARCH-1485)
### Overview
This pull request addresses an issue in the Rocket.Chat application where the Apps APIs were not correctly parsing URL-encoded data. The changes are implemented in the `AppApisBridge` class within the `api.ts` file.

### Key Changes
- **Middleware Enhancement**: Introduced new middleware routes to handle both private and public API requests.
- **URL-encoded Body Parsing**: Added functionality to parse URL-encoded bodies alongside the existing JSON body parsing. This ensures that API requests with URL-encoded data are correctly processed.

### Technical Context
The modification enhances the flexibility and robustness of the API handling within the Rocket.Chat application by supporting multiple content types in API requests. This change is crucial for applications that rely on URL-encoded data formats, ensuring compatibility and proper data handling.

### Testing and Validation
To validate these changes, ensure that API requests with URL-encoded data are correctly parsed and processed by the application. This can be tested by sending requests with different content types and verifying the application's response and data handling.


Diff:
diff --git a/apps/meteor/app/apps/server/bridges/api.ts b/apps/meteor/app/apps/server/bridges/api.ts
index 947ce62d690e0..f115897c82d82 100644
--- a/apps/meteor/app/apps/server/bridges/api.ts
+++ b/apps/meteor/app/apps/server/bridges/api.ts
@@ -3,18 +3,14 @@ import type { RequestMethod } from '@rocket.chat/apps-engine/definition/accessor
 import type { IApiRequest, IApiEndpoint, IApi } from '@rocket.chat/apps-engine/definition/api';
 import { ApiBridge } from '@rocket.chat/apps-engine/server/bridges/ApiBridge';
 import type { AppApi } from '@rocket.chat/apps-engine/server/managers/AppApi';
-import bodyParser from 'body-parser';
 import type { Response, Request, IRouter, RequestHandler } from 'express';
 import express from 'express';
 import { Meteor } from 'meteor/meteor';
 import { WebApp } from 'meteor/webapp';
 
+import { apiServer } from './router';
 import { authenticationMiddleware } from '../../../api/server/middlewares/authentication';
 
-const apiServer = express();
-
-apiServer.disable('x-powered-by');
-
 WebApp.rawConnectHandlers.use(apiServer);
 
 interface IRequestWithPrivateHash extends Request {
@@ -29,7 +25,7 @@ export class AppApisBridge extends ApiBridge {
 		super();
 		this.appRouters = new Map();
 
-		apiServer.use('/api/apps/private/:appId/:hash', bodyParser.json(), (req: IRequestWithPrivateHash, res: Response) => {
+		apiServer.use('/api/apps/private/:appId/:hash', (req: IRequestWithPrivateHash, res: Response) => {
 			const notFound = (): Response => res.sendStatus(404);
 
 			const router = this.appRouters.get(req.params.appId);
@@ -42,7 +38,7 @@ export class AppApisBridge extends ApiBridge {
 			notFound();
 		});
 
-		apiServer.use('/api/apps/public/:appId', bodyParser.json(), (req: Request, res: Response) => {
+		apiServer.use('/api/apps/public/:appId', (req: Request, res: Response) => {
 			const notFound = (): Response => res.sendStatus(404);
 
 			const router = this.appRouters.get(req.params.appId);
diff --git a/apps/meteor/app/apps/server/bridges/router.spec.ts b/apps/meteor/app/apps/server/bridges/router.spec.ts
new file mode 100644
index 0000000000000..61ebe714a2acd
--- /dev/null
+++ b/apps/meteor/app/apps/server/bridges/router.spec.ts
@@ -0,0 +1,95 @@
+import request from 'supertest';
+
+import { apiServer } from './router';
+
+apiServer.post('/api/apps/private/:appId/:hash', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+apiServer.post('/api/apps/public/:appId', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+describe('API Server Routes', () => {
+	it('should handle POST requests to /api/apps/private/:appId/:hash using json encoding', async () => {
+		const appId = 'testAppId';
+		const hash = 'testHash';
+		await request(apiServer)
+			.post(`/api/apps/private/${appId}/${hash}`)
+			.set('Content-Type', 'application/json')
+			.send({
+				key: 'value',
+			})
+			.expect('Content-Type', /json/)
+			.expect(200)
+			.then((res) => {
+				expect(res.body).toEqual({
+					body: { key: 'value' },
+					params: { appId, hash },
+					query: {},
+				});
+			});
+	});
+
+	it('should handle POST requests to /api/apps/private/:appId/:hash using x-www-form-urlencoded encoding', async () => {
+		const appId = 'testAppId';
+		const hash = 'testHash';
+		await request(apiServer)
+			.post(`/api/apps/private/${appId}/${hash}`)
+			.set('Content-Type', 'application/x-www-form-urlencoded')
+			.send('key=value')
+			.expect('Content-Type', /json/)
+			.expect(200)
+			.then((res) => {
+				expect(res.body).toEqual({
+					body: { key: 'value' },
+					params: { appId, hash },
+					query: {},
+				});
+			});
+	});
+
+	it('should handle POST requests to /api/apps/public/:appId using json encoding', async () => {
+		const appId = 'testAppId';
+		await request(apiServer)
+			.post(`/api/apps/public/${appId}`)
+			.set('Content-Type', 'application/json')
+			.send({
+				key: 'value',
+			})
+			.expect('Content-Type', /json/)
+			.expect(200)
+			.then((res) => {
+				expect(res.body).toEqual({
+					body: { key: 'value' },
+					params: { appId },
+					query: {},
+				});
+			});
+	});
+
+	it('should handle POST requests to /api/apps/public/:appId using x-www-form-urlencoded encoding', async () => {
+		const appId = 'testAppId';
+		await request(apiServer)
+			.post(`/api/apps/public/${appId}`)
+			.set('Content-Type', 'application/x-www-form-urlencoded')
+			.send('key=value')
+			.expect('Content-Type', /json/)
+			.expect(200)
+			.then((res) => {
+				expect(res.body).toEqual({
+					body: { key: 'value' },
+					params: { appId },
+					query: {},
+				});
+			});
+	});
+});
diff --git a/apps/meteor/app/apps/server/bridges/router.ts b/apps/meteor/app/apps/server/bridges/router.ts
new file mode 100644
index 0000000000000..993162b63c16f
--- /dev/null
+++ b/apps/meteor/app/apps/server/bridges/router.ts
@@ -0,0 +1,9 @@
+import bodyParser from 'body-parser';
+import express from 'express';
+
+export const apiServer = express();
+
+apiServer.disable('x-powered-by');
+
+apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded(), bodyParser.json());
+apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded(), bodyParser.json());


Reviews:
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-16T20:57:14Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T21:04:50Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:13:31Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:13:34Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:13:37Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:13:55Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:13:58Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T13:14:01Z
user: login: ggazzo
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-17T14:38:55Z

Review comments:
diff_hunk: @@ -29,20 +29,25 @@ export class AppApisBridge extends ApiBridge {
 		super();
 		this.appRouters = new Map();
 
-		apiServer.use('/api/apps/private/:appId/:hash', bodyParser.json(), (req: IRequestWithPrivateHash, res: Response) => {
-			const notFound = (): Response => res.sendStatus(404);
+		apiServer.use(
+			'/api/apps/private/:appId/:hash',
+			bodyParser.urlencoded(),
+			bodyParser.json(),
+			(req: IRequestWithPrivateHash, res: Response) => {
+				const notFound = (): Response => res.sendStatus(404);
 
-			const router = this.appRouters.get(req.params.appId);
+				const router = this.appRouters.get(req.params.appId);
 
-			if (router) {
-				req._privateHash = req.params.hash;
-				return router(req, res, notFound);
-			}
+				if (router) {
+					req._privateHash = req.params.hash;
+					return router(req, res, notFound);
path: apps/meteor/app/apps/server/bridges/api.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
try {
    return router(req, res, notFound);
} catch (error) {
    console.error('Error handling private API request:', error);
    res.status(500).send('Internal Server Error');
}
```
The router function lacks error handling, which could lead to uncaught exceptions and server crashes during request handling.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/api.ts: Lines 43-43
* apps/meteor/app/apps/server/bridges/api.ts: Lines 53-53
Please wrap the router function calls in try-catch blocks to catch and handle any potential errors, ensuring the server remains stable and errors are logged appropriately.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-16T20:57:13Z
author_association: NONE
diff_hunk: @@ -29,20 +29,25 @@
 		super();
 		this.appRouters = new Map();
 
-		apiServer.use('/api/apps/private/:appId/:hash', bodyParser.json(), (req: IRequestWithPrivateHash, res: Response) => {
-			const notFound = (): Response => res.sendStatus(404);
+		apiServer.use(
+			'/api/apps/private/:appId/:hash',
+			bodyParser.urlencoded(),
+			bodyParser.json(),
+			(req: IRequestWithPrivateHash, res: Response) => {
+				const notFound = (): Response => res.sendStatus(404);
 
-			const router = this.appRouters.get(req.params.appId);
+				const router = this.appRouters.get(req.params.appId);
 
-			if (router) {
-				req._privateHash = req.params.hash;
-				return router(req, res, notFound);
-			}
+				if (router) {
+					req._privateHash = req.params.hash;
+					return router(req, res, notFound);
path: apps/meteor/app/apps/server/bridges/api.ts
user: login: github-advanced-security[bot]
body: ## Unvalidated dynamic method call

Invocation of method with [user-controlled](1) name may dispatch to unexpected target and cause an exception.

[Show more details](https://github.com/RocketChat/Rocket.Chat/security/code-scanning/476)
created_at: 2025-04-16T21:04:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,9 @@
+import bodyParser from 'body-parser';
+import express from 'express';
+
+export const apiServer = express();
+
+apiServer.disable('x-powered-by');
+
+apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded(), bodyParser.json());
+apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded(), bodyParser.json());
path: apps/meteor/app/apps/server/bridges/router.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Security](https://img.shields.io/badge/Security-D50000) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
const commonBodyParsers = [
  bodyParser.urlencoded({ extended: true, limit: '1mb' }), // Example limit
  bodyParser.json({ limit: '1mb' }) // Example limit
];

apiServer.use('/api/apps/private/:appId/:hash', ...commonBodyParsers);
apiServer.use('/api/apps/public/:appId', ...commonBodyParsers);
```
The body parsers (`json` and `urlencoded`) lack size limits, which could lead to potential Denial-of-Service (DoS) attacks from excessively large request payloads.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
Please add size limits to the body parsers to mitigate potential DoS vulnerabilities.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:13:31Z
author_association: NONE
diff_hunk: @@ -0,0 +1,9 @@
+import bodyParser from 'body-parser';
+import express from 'express';
+
+export const apiServer = express();
+
+apiServer.disable('x-powered-by');
+
+apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded(), bodyParser.json());
+apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded(), bodyParser.json());
path: apps/meteor/app/apps/server/bridges/router.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
// ... (existing route definitions) ...

// Add after all routes/middleware
apiServer.use((err, req, res, next) => {
  console.error('API Server Error:', err.stack);
  // Avoid sending stack traces in production
  res.status(500).json({ error: 'Internal Server Error' }); 
});
```
The `apiServer` instance lacks explicit error handling middleware, which could lead to unhandled errors and reduced robustness.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.ts: Lines 4-9
* apps/meteor/app/apps/server/bridges/router.ts: Lines 4-9
Please add explicit error handling middleware to catch and manage errors gracefully, improving robustness and aiding debugging.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:13:34Z
author_association: NONE
diff_hunk: @@ -0,0 +1,96 @@
+import request from 'supertest';
+
+import { apiServer } from './router';
+import { ur } from '@faker-js/faker';
+
+apiServer.post('/api/apps/private/:appId/:hash', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+apiServer.post('/api/apps/public/:appId', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+describe('API Server Routes', () => {
+	it('should handle POST requests to /api/apps/private/:appId/:hash using json encoding', async () => {
path: apps/meteor/app/apps/server/bridges/router.spec.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Refactoring](https://img.shields.io/badge/Refactoring-304FFE) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
// Conceptual Example:
async function testPostEndpoint(config: {
  path: string;
  contentType: string;
  sendData: any;
  expectedBodyData: any;
  expectedParams: object;
}) {
  await request(apiServer)
    .post(config.path)
    .set('Content-Type', config.contentType)
    .send(config.sendData)
    .expect('Content-Type', /json/)
    .expect(200)
    .then((res: request.Response) => {
      expect(res.body).toEqual({
        body: config.expectedBodyData,
        params: config.expectedParams,
        query: {},
      });
    });
}

// Usage in tests:
it('...', async () => {
  await testPostEndpoint({ path: '/api/apps/private/testAppId/testHash', contentType: 'application/json', sendData: { key: 'value' }, expectedBodyData: { key: 'value' }, expectedParams: { appId: 'testAppId', hash: 'testHash' } });
});
it('...', async () => {
  await testPostEndpoint({ path: '/api/apps/private/testAppId/testHash', contentType: 'application/x-www-form-urlencoded', sendData: 'key=value', expectedBodyData: { key: 'value' }, expectedParams: { appId: 'testAppId', hash: 'testHash' } });
});
// etc.
```
The four tests (`it` blocks) share a nearly identical structure, leading to code duplication and making maintenance harder.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.spec.ts: Lines 23-95
* apps/meteor/app/apps/server/bridges/router.spec.ts: Lines 23-95
Please refactor the common logic into a reusable asynchronous helper function to reduce code duplication and improve maintainability.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:13:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,9 @@
+import bodyParser from 'body-parser';
+import express from 'express';
+
+export const apiServer = express();
+
+apiServer.disable('x-powered-by');
+
+apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded(), bodyParser.json());
+apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded(), bodyParser.json());
path: apps/meteor/app/apps/server/bridges/router.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Security](https://img.shields.io/badge/Security-D50000) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
const bodyLimit = '1mb'; // Example limit, adjust as needed

apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded({ limit: bodyLimit, extended: true }), bodyParser.json({ limit: bodyLimit }));
apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded({ limit: bodyLimit, extended: true }), bodyParser.json({ limit: bodyLimit }));
```
The `bodyParser.json()` and `bodyParser.urlencoded()` middleware are used without specifying size limits, which can expose the server to Denial of Service (DoS) attacks.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
Please add size limits to the body-parser middleware to prevent potential DoS attacks via large request bodies.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:13:55Z
author_association: NONE
diff_hunk: @@ -0,0 +1,96 @@
+import request from 'supertest';
+
+import { apiServer } from './router';
+import { ur } from '@faker-js/faker';
+
+apiServer.post('/api/apps/private/:appId/:hash', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+apiServer.post('/api/apps/public/:appId', (req, res) => {
+	res.json({
+		body: req.body,
+		params: req.params,
+		query: req.query,
+	});
+});
+
+describe('API Server Routes', () => {
+	it('should handle POST requests to /api/apps/private/:appId/:hash using json encoding', async () => {
path: apps/meteor/app/apps/server/bridges/router.spec.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Refactoring](https://img.shields.io/badge/Refactoring-304FFE) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
async function testPostEndpoint(config: {
  path: string;
  contentType: string;
  sendData: any;
  expectedBodyData: any;
  expectedParams: object;
}) {
  await request(apiServer)
    .post(config.path)
    .set('Content-Type', config.contentType)
    .send(config.sendData)
    .expect('Content-Type', /json/)
    .expect(200)
    .then((res: request.Response) => {
      expect(res.body).toEqual({
        body: config.expectedBodyData,
        params: config.expectedParams,
        query: {},
      });
    });
}

it('should handle POST request...', async () => {
  await testPostEndpoint({
    path: '/api/apps/private/testAppId/testHash',
    contentType: 'application/json',
    sendData: { key: 'value' },
    expectedBodyData: { key: 'value' },
    expectedParams: { appId: 'testAppId', hash: 'testHash' }
  });
});
```
The four tests share a nearly identical structure, leading to code duplication and potential maintenance challenges.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.spec.ts: Lines 23-95
* apps/meteor/app/apps/server/bridges/router.spec.ts: Lines 23-95
Please refactor the repetitive test logic into a reusable helper function to reduce redundancy and improve maintainability.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:13:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,9 @@
+import bodyParser from 'body-parser';
+import express from 'express';
+
+export const apiServer = express();
+
+apiServer.disable('x-powered-by');
+
+apiServer.use('/api/apps/private/:appId/:hash', bodyParser.urlencoded(), bodyParser.json());
+apiServer.use('/api/apps/public/:appId', bodyParser.urlencoded(), bodyParser.json());
path: apps/meteor/app/apps/server/bridges/router.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Refactoring](https://img.shields.io/badge/Refactoring-304FFE) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
const commonBodyParsers = [bodyParser.urlencoded({ extended: true }), bodyParser.json()];
apiServer.use('/api/apps/private/:appId/:hash', ...commonBodyParsers);
apiServer.use('/api/apps/public/:appId', ...commonBodyParsers);
```
The `bodyParser.urlencoded()` and `bodyParser.json()` middleware setup is duplicated for both routes, leading to redundancy and potential maintenance issues.

This issue appears in multiple locations:
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
* apps/meteor/app/apps/server/bridges/router.ts: Lines 8-9
Please refactor the duplicated body-parser middleware initialization into a reusable variable or common parent route for better maintainability.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-17T13:14:01Z
author_association: NONE

PR comments:
user: login: dionisio-bot[bot]
created_at: 2025-04-16T20:55:14Z
body: Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the [PR guidelines](https://handbook.rocket.chat/departments-and-operations/research-and-development/engineering/development/pr-general-instructions-and-handling)
author_association: CONTRIBUTOR
user: login: changeset-bot[bot]
created_at: 2025-04-16T20:55:14Z
body: ###  ⚠️  No Changeset found

Latest commit: e48b8d986c4669cd8d5ea2c4cd918b45189baa2c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/RocketChat/Rocket.Chat/new/fix-urlencoded-app-api?filename=.changeset/old-flies-brush.md&value=---%0A%22%40rocket.chat%2Fmeteor%22%3A%20patch%0A---%0A%0Aregression%3A%20Apps%20APIs%20not%20parsing%20urlencoded%0A)


author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-04-16T20:55:15Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/automations/AutomationCodeReview/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: codecov[bot]
created_at: 2025-04-16T21:16:29Z
body: ## [Codecov](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 61.17%. Comparing base [(`b7ed309`)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/commit/b7ed3098e78c07e7d912616bc9abafe4eea26057?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) to head [(`e48b8d9`)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/commit/e48b8d986c4669cd8d5ea2c4cd918b45189baa2c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat).
> Report is 2 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829/graphs/tree.svg?width=650&height=150&src=pr&token=lEAH159Fca&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat)

```diff
@@             Coverage Diff             @@
##           develop   #35829      +/-   ##
===========================================
- Coverage    61.19%   61.17%   -0.02%     
===========================================
  Files         2971     2971              
  Lines        70839    70839              
  Branches     16185    16185              
===========================================
- Hits         43347    43339       -8     
- Misses       24546    24553       +7     
- Partials      2946     2947       +1     
```

| [Flag](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | Coverage Δ | |
|---|---|---|
| [e2e](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | `57.70% <ø> (-0.03%)` | :arrow_down: |
| [unit](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35829/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | `75.61% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-04-16T21:55:30Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/automations/AutomationCodeReview/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-04-17T13:11:39Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-04-17T13:12:07Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-17T13:47:46Z
body: [PR Preview Action](https://github.com/rossjrw/pr-preview-action) v1.6.1
:---:
| <p></p> :rocket: View preview at <br> https://RocketChat.github.io/Rocket.Chat/pr-preview/pr-35829/ <br><br>
| <h6>Built to branch [`gh-pages`](https://github.com/RocketChat/Rocket.Chat/tree/gh-pages) at 2025-04-17 14:06 UTC. <br> Preview will be ready when the [GitHub Pages deployment](https://github.com/RocketChat/Rocket.Chat/deployments) is complete. <br><br> </h6>
<!-- Sticky Pull Request Commentpr-preview -->
author_association: CONTRIBUTOR