diff --git a/apps/meteor/app/ui-master/server/index.ts b/apps/meteor/app/ui-master/server/index.ts
index 7efdd94c25159..24a673b3b9b85 100644
--- a/apps/meteor/app/ui-master/server/index.ts
+++ b/apps/meteor/app/ui-master/server/index.ts
@@ -6,6 +6,7 @@ import { Inject } from 'meteor/meteorhacks:inject-initial';
import { Tracker } from 'meteor/tracker';
import { applyHeadInjections, headInjections, injectIntoBody, injectIntoHead } from './inject';
+import { getMessageMaxParseLength } from '../../../lib/getMessageMaxParseLength';
import { withDebouncing } from '../../../lib/utils/highOrderFunctions';
import { settings } from '../../settings/server';
import { getURL } from '../../utils/server/getURL';
@@ -126,6 +127,9 @@ Meteor.startup(() => {
})(__meteor_runtime_config__.ROOT_URL_PATH_PREFIX);
injectIntoHead('base', ``);
+
+ const escapedMessageMaxParseLength = escapeHTML(String(getMessageMaxParseLength()));
+ injectIntoHead('MESSAGE_MAX_PARSE_LENGTH', ``);
});
const renderDynamicCssList = withDebouncing({ wait: 500 })(async () => {
diff --git a/apps/meteor/lib/constants.ts b/apps/meteor/lib/constants.ts
index 6d258a8876d36..23807a3503f46 100644
--- a/apps/meteor/lib/constants.ts
+++ b/apps/meteor/lib/constants.ts
@@ -2,3 +2,4 @@ export const NOTIFICATION_ATTACHMENT_COLOR = '#FD745E';
export const MAX_MULTIPLE_UPLOADED_FILES = 10;
export const MAX_CUSTOM_SOUND_SIZE_BYTES = 5242880;
export const CUSTOM_SOUND_ALLOWED_MIME_TYPES = ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/x-wav'];
+export const MESSAGE_MAX_PARSE_LENGTH_DEFAULT = 0;
diff --git a/apps/meteor/lib/getMessageMaxParseLength.ts b/apps/meteor/lib/getMessageMaxParseLength.ts
new file mode 100644
index 0000000000000..941fa9be61809
--- /dev/null
+++ b/apps/meteor/lib/getMessageMaxParseLength.ts
@@ -0,0 +1,6 @@
+import { MESSAGE_MAX_PARSE_LENGTH_DEFAULT } from './constants';
+
+export function getMessageMaxParseLength(): number {
+ const parsed = Number.parseInt(process.env.MESSAGE_MAX_PARSE_LENGTH ?? '', 10);
+ return Number.isFinite(parsed) ? parsed : MESSAGE_MAX_PARSE_LENGTH_DEFAULT;
+}
diff --git a/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts b/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts
index f4e26cdb97ee5..b2b47ad0b9228 100644
--- a/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts
+++ b/apps/meteor/server/services/messages/hooks/BeforeSaveMarkdownParser.ts
@@ -2,6 +2,8 @@ import { isE2EEMessage } from '@rocket.chat/core-typings';
import type { IMessage } from '@rocket.chat/core-typings';
import { parse } from '@rocket.chat/message-parser';
+import { getMessageMaxParseLength } from '../../../../lib/getMessageMaxParseLength';
+
type ParserConfig = {
colors?: boolean;
emoticons?: boolean;
@@ -26,6 +28,12 @@ export class BeforeSaveMarkdownParser {
return message;
}
+ const messageMaxParseLength = getMessageMaxParseLength();
+ if (messageMaxParseLength > 0 && message.msg && message.msg.length > messageMaxParseLength) {
+ delete message.md;
+ return message;
+ }
+
try {
if (message.msg) {
message.md = parse(message.msg, config);
diff --git a/apps/meteor/tests/unit/lib/getMessageMaxParseLength.spec.ts b/apps/meteor/tests/unit/lib/getMessageMaxParseLength.spec.ts
new file mode 100644
index 0000000000000..b9173ccd133cd
--- /dev/null
+++ b/apps/meteor/tests/unit/lib/getMessageMaxParseLength.spec.ts
@@ -0,0 +1,39 @@
+import { expect } from 'chai';
+
+import { getMessageMaxParseLength } from '../../../lib/getMessageMaxParseLength';
+
+describe('getMessageMaxParseLength', () => {
+ afterEach(() => {
+ delete process.env.MESSAGE_MAX_PARSE_LENGTH;
+ });
+
+ it('should return 0 (default) when env var is not set', () => {
+ delete process.env.MESSAGE_MAX_PARSE_LENGTH;
+ expect(getMessageMaxParseLength()).to.equal(0);
+ });
+
+ it('should return 0 (default) when env var is empty string', () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '';
+ expect(getMessageMaxParseLength()).to.equal(0);
+ });
+
+ it('should return 0 (default) when env var is not a number', () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = 'abc';
+ expect(getMessageMaxParseLength()).to.equal(0);
+ });
+
+ it('should return the parsed number when env var is a valid integer', () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '5000';
+ expect(getMessageMaxParseLength()).to.equal(5000);
+ });
+
+ it('should return 0 when env var is "0"', () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '0';
+ expect(getMessageMaxParseLength()).to.equal(0);
+ });
+
+ it('should return 0 (default) when env var is Infinity', () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = 'Infinity';
+ expect(getMessageMaxParseLength()).to.equal(0);
+ });
+});
diff --git a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts
index ae0f3ed284a7a..c744883ce9e3c 100644
--- a/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts
+++ b/apps/meteor/tests/unit/server/services/messages/hooks/BeforeSaveMarkdownParser.tests.ts
@@ -1,4 +1,5 @@
import { expect } from 'chai';
+import { beforeEach } from 'mocha';
import { BeforeSaveMarkdownParser } from '../../../../../../server/services/messages/hooks/BeforeSaveMarkdownParser';
@@ -16,6 +17,14 @@ const createMessage = (msg?: string, extra: any = {}) => ({
});
describe('Markdown parser', () => {
+ beforeEach(() => {
+ delete process.env.MESSAGE_MAX_PARSE_LENGTH;
+ });
+
+ afterEach(() => {
+ delete process.env.MESSAGE_MAX_PARSE_LENGTH;
+ });
+
it('should do nothing if markdown parser is disabled', async () => {
const markdownParser = new BeforeSaveMarkdownParser(false);
@@ -38,11 +47,36 @@ describe('Markdown parser', () => {
expect(message).to.not.have.property('md');
});
- it('should parse markdown', async () => {
+ it('should skip parsing when msg exceeds MESSAGE_MAX_PARSE_LENGTH', async () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '10';
const markdownParser = new BeforeSaveMarkdownParser(true);
const message = await markdownParser.parseMarkdown({
- message: createMessage('hey'),
+ message: createMessage('a'.repeat(11)),
+ config: {},
+ });
+
+ expect(message).to.not.have.property('md');
+ });
+
+ it('should parse normally when msg is within MESSAGE_MAX_PARSE_LENGTH', async () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '100';
+ const markdownParser = new BeforeSaveMarkdownParser(true);
+
+ const message = await markdownParser.parseMarkdown({
+ message: createMessage('short msg'),
+ config: {},
+ });
+
+ expect(message).to.have.property('md');
+ });
+
+ it('should parse normally when MESSAGE_MAX_PARSE_LENGTH is 0', async () => {
+ process.env.MESSAGE_MAX_PARSE_LENGTH = '0';
+ const markdownParser = new BeforeSaveMarkdownParser(true);
+
+ const message = await markdownParser.parseMarkdown({
+ message: createMessage('short msg'),
config: {},
});