From fbf4245760c0002cb8e5c9c7dbac5d896be21884 Mon Sep 17 00:00:00 2001 From: "Julio A." <52619625+julio-cfa@users.noreply.github.com> Date: Fri, 1 Nov 2024 21:21:09 +0100 Subject: [PATCH] fix: imported fixes (#33454) --- .changeset/funny-icons-develop.md | 5 ++ .../app/otr/server/methods/updateOTRAck.ts | 48 +++++++++++++++++- .../tabs/AppDetails/AppDetails.tsx | 11 +++- .../tabs/AppReleases/AppReleasesItem.tsx | 4 +- .../views/marketplace/lib/purifyOptions.ts | 50 +++++++++++++++++++ .../core-typings/src/IMessage/IMessage.ts | 3 ++ packages/gazzodown/package.json | 2 + .../gazzodown/src/emoji/EmojiRenderer.tsx | 10 ++-- 8 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 .changeset/funny-icons-develop.md create mode 100644 apps/meteor/client/views/marketplace/lib/purifyOptions.ts diff --git a/.changeset/funny-icons-develop.md b/.changeset/funny-icons-develop.md new file mode 100644 index 000000000000..eacb88108a0f --- /dev/null +++ b/.changeset/funny-icons-develop.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates) diff --git a/apps/meteor/app/otr/server/methods/updateOTRAck.ts b/apps/meteor/app/otr/server/methods/updateOTRAck.ts index eaba2906ed06..70b2a4f7c812 100644 --- a/apps/meteor/app/otr/server/methods/updateOTRAck.ts +++ b/apps/meteor/app/otr/server/methods/updateOTRAck.ts @@ -1,6 +1,9 @@ +import { Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { canAccessRoomAsync } from '../../../authorization/server'; import notifications from '../../../notifications/server/lib/Notifications'; declare module '@rocket.chat/ui-contexts' { @@ -11,10 +14,51 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods({ - updateOTRAck({ message, ack }) { - if (!Meteor.userId()) { + async updateOTRAck({ message, ack }) { + const uid = Meteor.userId(); + + if (!uid) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateOTRAck' }); } + + check(ack, String); + check(message, { + _id: String, + rid: String, + msg: String, + t: Match.Any, + ts: Date, + u: { + _id: String, + username: String, + name: String, + }, + _updatedAt: Date, + urls: Match.Any, + mentions: Match.Any, + channels: Match.Optional([ + { + _id: String, + name: String, + }, + ]), + otr: Match.Maybe({ ack: String }), + }); + + if (message?.t !== 'otr') { + throw new Meteor.Error('error-invalid-message', 'Invalid message type', { method: 'updateOTRAck' }); + } + + const room = await Rooms.findOneById(message.rid, { projection: { t: 1, _id: 1, uids: 1 } }); + + if (!room) { + throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'updateOTRAck' }); + } + + if (!(await canAccessRoomAsync(room, { _id: uid })) || (room.uids && (!message.u._id || !room.uids.includes(message.u._id)))) { + throw new Meteor.Error('error-invalid-user', 'Invalid user, not in room', { method: 'updateOTRAck' }); + } + const otrStreamer = notifications.streamRoomMessage; otrStreamer.emit(message.rid, { ...message, otr: { ack } }); }, diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx index 8d17f669db83..5f3e427b8391 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppDetails/AppDetails.tsx @@ -2,10 +2,12 @@ import { Box, Callout, Chip, Margins } from '@rocket.chat/fuselage'; import { ExternalLink } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; +import DOMPurify from 'dompurify'; import React from 'react'; import ScreenshotCarouselAnchor from '../../../components/ScreenshotCarouselAnchor'; import type { AppInfo } from '../../../definitions/AppInfo'; +import { purifyOptions } from '../../../lib/purifyOptions'; import AppDetailsAPIs from './AppDetailsAPIs'; import { normalizeUrl } from './normalizeUrl'; @@ -61,7 +63,14 @@ const AppDetails = ({ app }: AppDetailsProps) => { {t('Description')} - + diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppReleases/AppReleasesItem.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppReleases/AppReleasesItem.tsx index bc27053ea1d2..974b6d148e61 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppReleases/AppReleasesItem.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppReleases/AppReleasesItem.tsx @@ -1,9 +1,11 @@ import { Accordion, Box } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; +import DOMPurify from 'dompurify'; import type { ReactElement } from 'react'; import React from 'react'; import { useTimeAgo } from '../../../../../hooks/useTimeAgo'; +import { purifyOptions } from '../../../lib/purifyOptions'; type IRelease = { version: string; @@ -36,7 +38,7 @@ const AppReleasesItem = ({ release, ...props }: ReleaseItemProps): ReactElement return ( {release.detailedChangelog?.rendered ? ( - + ) : ( {t('No_release_information_provided')} )} diff --git a/apps/meteor/client/views/marketplace/lib/purifyOptions.ts b/apps/meteor/client/views/marketplace/lib/purifyOptions.ts new file mode 100644 index 000000000000..cef1a2c8c707 --- /dev/null +++ b/apps/meteor/client/views/marketplace/lib/purifyOptions.ts @@ -0,0 +1,50 @@ +export const purifyOptions = { + ALLOWED_TAGS: [ + 'b', + 'i', + 'em', + 'strong', + 'br', + 'p', + 'ul', + 'ol', + 'li', + 'article', + 'aside', + 'figure', + 'section', + 'summary', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'hgroup', + 'div', + 'hr', + 'span', + 'wbr', + 'abbr', + 'acronym', + 'cite', + 'code', + 'dfn', + 'figcaption', + 'mark', + 's', + 'samp', + 'sub', + 'sup', + 'var', + 'time', + 'q', + 'del', + 'ins', + 'rp', + 'rt', + 'ruby', + 'bdi', + 'bdo', + ], +}; diff --git a/packages/core-typings/src/IMessage/IMessage.ts b/packages/core-typings/src/IMessage/IMessage.ts index 3ed5e470585f..2b2766b6c017 100644 --- a/packages/core-typings/src/IMessage/IMessage.ts +++ b/packages/core-typings/src/IMessage/IMessage.ts @@ -159,6 +159,9 @@ export interface IMessage extends IRocketChatRecord { t?: MessageTypesValues; e2e?: 'pending' | 'done'; otrAck?: string; + otr?: { + ack: string; + }; urls?: MessageUrl[]; diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index d26c32bffe79..95061dfac972 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -77,6 +77,8 @@ "react": "*" }, "dependencies": { + "@types/dompurify": "^3.0.5", + "dompurify": "^3.1.7", "highlight.js": "^11.5.1", "react-error-boundary": "^3.1.4" }, diff --git a/packages/gazzodown/src/emoji/EmojiRenderer.tsx b/packages/gazzodown/src/emoji/EmojiRenderer.tsx index 7a4ca5324930..7d8d6dbfe1bf 100644 --- a/packages/gazzodown/src/emoji/EmojiRenderer.tsx +++ b/packages/gazzodown/src/emoji/EmojiRenderer.tsx @@ -1,5 +1,6 @@ import { MessageEmoji, ThreadMessageEmoji } from '@rocket.chat/fuselage'; import type * as MessageParser from '@rocket.chat/message-parser'; +import DOMPurify from 'dompurify'; import { ReactElement, useMemo, useContext, memo } from 'react'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -13,11 +14,12 @@ const EmojiRenderer = ({ big = false, preview = false, ...emoji }: EmojiProps): const { detectEmoji } = useContext(MarkupInteractionContext); const fallback = useMemo(() => ('unicode' in emoji ? emoji.unicode : `:${emoji.shortCode ?? emoji.value.value}:`), [emoji]); + const sanitizedFallback = DOMPurify.sanitize(fallback); const descriptors = useMemo(() => { - const detected = detectEmoji?.(fallback); + const detected = detectEmoji?.(sanitizedFallback); return detected?.length !== 0 ? detected : undefined; - }, [detectEmoji, fallback]); + }, [detectEmoji, sanitizedFallback]); return ( <> @@ -34,8 +36,8 @@ const EmojiRenderer = ({ big = false, preview = false, ...emoji }: EmojiProps): )} )) ?? ( - - {fallback} + + {sanitizedFallback} )}