Skip to content

Conversation

@ggazzo
Copy link
Member

@ggazzo ggazzo commented Jan 31, 2025

https://rocketchat.atlassian.net/browse/ARCH-1485

hono is a new router designed to replace express.
it is based on new language features and made 100% to be used with typescript

  • Better typings

  • Improved way to implement middlewares

  • Multiple architectures (node/bun/deno)

  • Faster (!/?)

Proposed changes (including videos or screenshots)

Issue(s)

Steps to test or reproduce

Further comments


This pull request introduces significant changes to the Rocket.Chat codebase by integrating the Hono framework as the primary router, replacing the existing Express setup. The changes span multiple files and focus on improving type safety, request handling, and middleware functionality. Key updates include:

  • Refactoring the API server implementation to enhance request handling, rate limiting, and authentication flows.
  • Migrating various middleware components from Express to Hono, including CORS, logging, metrics, and tracer functionalities, while maintaining core functionalities and improving type safety.
  • Updating header access methods across multiple API endpoints to use the get() method, enhancing security and consistency with modern web standards.
  • Introducing a Hono adapter for Express requests to facilitate request conversion and response mapping.
  • Improving file upload handling by transitioning from Express request handling to the Web Streams API.
  • Enhancing request validation and error handling in several integrations, including Twilio and livechat endpoints.
  • Modifying test files to align with the new request handling approach and improve type safety.

Overall, this pull request aims to modernize the Rocket.Chat codebase by adopting the Hono framework, improving maintainability, and ensuring better type safety and security across the application.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 31, 2025

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 31, 2025

⚠️ No Changeset found

Latest commit: ff2508b

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.

This PR includes no changesets

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

Click here to learn what changesets are, and how to add one.

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

@github-actions
Copy link
Contributor

github-actions bot commented Jan 31, 2025

PR Preview Action v1.6.0

🚀 View preview at
https://RocketChat.github.io/Rocket.Chat/pr-preview/pr-35078/

Built to branch gh-pages at 2025-04-08 18:53 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@ggazzo ggazzo force-pushed the chore/remove-restivus branch from 78b097a to bc242f5 Compare January 31, 2025 21:54
Base automatically changed from chore/remove-restivus to develop January 31, 2025 23:19
@ggazzo ggazzo force-pushed the chore/hono branch 2 times, most recently from c7f983c to f5a3898 Compare March 17, 2025 13:23
@codecov
Copy link

codecov bot commented Mar 19, 2025

Codecov Report

Attention: Patch coverage is 97.56098% with 1 line in your changes missing coverage. Please review.

Project coverage is 59.64%. Comparing base (939cc28) to head (ff2508b).
Report is 3 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #35078      +/-   ##
===========================================
+ Coverage    59.63%   59.64%   +0.01%     
===========================================
  Files         2832     2832              
  Lines        68301    68322      +21     
  Branches     15131    15133       +2     
===========================================
+ Hits         40730    40750      +20     
- Misses       24962    24963       +1     
  Partials      2609     2609              
Flag Coverage Δ
unit 75.62% <97.56%> (+0.01%) ⬆️

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.

@ggazzo ggazzo force-pushed the chore/hono branch 5 times, most recently from 11c5ea1 to 79ba5e9 Compare March 26, 2025 19:15
@ggazzo ggazzo force-pushed the chore/hono branch 4 times, most recently from a0962f3 to 301b101 Compare April 1, 2025 20:37
@ggazzo ggazzo force-pushed the chore/hono branch 5 times, most recently from 0c6dd15 to 9d5cc6e Compare April 4, 2025 02:43
@ggazzo ggazzo added this to the 7.6.0 milestone Apr 4, 2025
@MarcosSpessatto MarcosSpessatto added the stat: QA assured Means it has been tested and approved by a company insider label Apr 8, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Apr 8, 2025
@ggazzo
Copy link
Member Author

ggazzo commented Apr 8, 2025

@kody start-review

@kody-ai
Copy link

kody-ai bot commented Apr 8, 2025

Code Review Completed! 🔥

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

Kody Guide: Usage and Configuration
Interacting with Kody
  • 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.

Current Kody Configuration
Review Options

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

Access your configuration settings here.

@ggazzo ggazzo requested a review from Copilot April 8, 2025 20:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 46 out of 46 changed files in this pull request and generated 1 comment.

export async function getLoggedInUser(request: Request): Promise<Pick<IUser, '_id' | 'username'> | null> {
const token = request.headers['x-auth-token'];
const userId = request.headers['x-user-id'];
const token = request.headers.get('x-auth-token');
Copy link

Choose a reason for hiding this comment

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

kody code-review Security high

const token = request.headers.get('x-auth-token')?.trim();
const userId = request.headers.get('x-user-id')?.trim();

Multiple instances of missing input validation for critical parameters, potentially leading to security vulnerabilities or runtime errors.

This issue appears in multiple locations:

  • apps/meteor/app/api/server/helpers/getLoggedInUser.ts: Lines 6-7
  • apps/meteor/app/integrations/server/api/api.js: Lines 243-243
  • apps/meteor/server/services/omnichannel-integrations/providers/twilio.ts: Lines 248-250
  • apps/meteor/server/services/omnichannel-integrations/providers/voxtelesys.ts: Lines 165-167
    Please add validation checks for critical parameters to ensure they are not null, undefined, or malformed before processing.

Talk to Kody by mentioning @kody

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

Comment on lines 7 to 13
import { ajv } from '@rocket.chat/rest-typings/src/v1/Ajv';
import { wrapExceptions } from '@rocket.chat/tools';
import type { ValidateFunction } from 'ajv';
import express from 'express';
import type { Request, Response } from 'express';
import type express from 'express';
import { Accounts } from 'meteor/accounts-base';
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';
Copy link

Choose a reason for hiding this comment

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

kody code-review Kody Rules medium

// Import the necessary types from the '@rocket.chat/rest-typings' package or define custom types if needed.
import type { Request, Response } from '@rocket.chat/rest-typings';

// Or, if using custom types:
// interface Request { /* ... */ }
// interface Response { /* ... */ }

The removal of the explicit 'express' import and the associated types 'Request' and 'Response' might lead to issues with type checking and code completion. While the 'WebApp' object might provide similar functionality, it's crucial to ensure type safety. Consider adding explicit types for request and response objects within the affected methods to maintain type correctness and avoid potential runtime errors.

Talk to Kody by mentioning @kody

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

export type AuthorizationStatusCodes = Exclude<Range<451>, Range<400>>;

export type ErrorStatusCodes = Exclude<Range<511>, Range<500>>;
export type ErrorStatusCodes = Exclude<Exclude<Range<511>, Range<500>>, 509>;
Copy link

Choose a reason for hiding this comment

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

kody code-review Potential Issues high

export type ErrorStatusCodes = 500 | 510;

The ErrorStatusCodes type now excludes 509 from the range. This could potentially cause issues if 509 is a valid error code in the system. Consider documenting this exclusion or ensuring it's intentional.

Talk to Kody by mentioning @kody

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


const nodeReadableStream = new Readable({
async read() {
const reader = webReadableStream.getReader();
Copy link

Choose a reason for hiding this comment

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

kody code-review Error Handling high

let reader;
try {
  const error = unknown;
  reader = webReadableStream.getReader();
} catch (error: unknown) {
  this.destroy(error);
  return;
}

Add error handling for the webReadableStream.getReader() operation to catch potential stream reading errors.

Talk to Kody by mentioning @kody

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

});

request.pipe(bb);
const webReadableStream = await request.blob().then((blob) => blob.stream());
Copy link

Choose a reason for hiding this comment

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

kody code-review Kody Rules medium

+	nodeReadableStream.pipe(bb);
+
+	try {
+		return new Promise<UploadResultWithOptionalFile<K>>((resolve, reject) => {
+			returnResult = resolve;
+	} finally {
+		nodeReadableStream.destroy();
+		bb.removeAllListeners();
+	}

The code pipes a Node.js Readable stream to the 'busboy' instance but doesn't remove the listener or close the stream when finished or in case of errors. This can lead to memory leaks. Add a 'close' event listener to the Readable stream and/or 'busboy' to ensure proper cleanup. Consider using a try/finally block to guarantee execution of the cleanup logic.

Talk to Kody by mentioning @kody

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

return c.body('CORS not enabled. Go to "Admin > General > REST Api" to enable it.', 405);
}

const CORSOriginSetting = String(settings.get('API_CORS_Origin'));
Copy link

Choose a reason for hiding this comment

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

kody code-review Error Handling high

const CORSOriginSetting = String(settings.get('API_CORS_Origin') ?? '*');

Add validation for CORSOriginSetting to ensure it's not null or undefined before processing.

Talk to Kody by mentioning @kody

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

Comment on lines +15 to +25
const honoRes = await hono.request(
expressReq.originalUrl,
{
...req,
...(['POST', 'PUT', 'DELETE'].includes(expressReq.method) && { body: expressReq as unknown as ReadableStream }),
headers: new Headers(Object.fromEntries(Object.entries(expressReq.headers)) as Record<string, string>),
},
{
incoming: expressReq,
},
);
Copy link

Choose a reason for hiding this comment

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

kody code-review Error Handling high

try {
	const honoRes = await hono.request(
		expressReq.originalUrl,
		{
			...req,
			...(['POST', 'PUT', 'DELETE'].includes(expressReq.method) && { body: expressReq as unknown as ReadableStream }),
			headers: new Headers(Object.fromEntries(Object.entries(expressReq.headers)) as Record<string, string>),
		},
		{
			incoming: expressReq,
		},
	);
	const responseText = await honoRes.text().catch((error) => {
		console.error('Error reading response:', error);
		throw error;
	});
	res.status(honoRes.status);
	honoRes.headers.forEach((value, key) => res.setHeader(key, value));
	res.send(Buffer.from(responseText));
} catch (error) {
	console.error('Hono adapter error:', error);
	res.status(500).send('Internal Server Error');
}

Add error handling for the Hono request and response processing to prevent unhandled promise rejections.

Talk to Kody by mentioning @kody

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

_id: this.user._id,
ip: this.requestIp,
useragent: this.request.headers['user-agent'] || '',
useragent: this.request.headers.get('user-agent') || '',
Copy link

Choose a reason for hiding this comment

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

kody code-review Security high

const userAgent = this.request.headers.get('user-agent') || '';
if (!/^[\w\d\s\-\.\/\:;()\[\]{}@!?=+]*$/.test(userAgent)) {
  throw new Error('Invalid user-agent header');
}
useragent: userAgent,

Add validation for user-agent header to ensure it matches expected format and prevent potential injection attacks.

Talk to Kody by mentioning @kody

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

Comment on lines +25 to +31
'/oauth/*',
express.json({
limit: '50mb',
}),
express.urlencoded({
extended: true,
limit: '50mb',
Copy link

Choose a reason for hiding this comment

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

kody code-review Security critical

express.json({
				limit: '1mb',
			}),
			express.urlencoded({
				extended: true,
				limit: '1mb',
			}),

The 50MB limit for JSON and URL-encoded data is quite high and could lead to potential memory exhaustion attacks. Consider reducing this limit to a more reasonable size.

Talk to Kody by mentioning @kody

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants