Skip to content

Commit

Permalink
fix: imported fixes (#33269)
Browse files Browse the repository at this point in the history
Co-authored-by: Julio A. <52619625+julio-cfa@users.noreply.github.com>
  • Loading branch information
dionisio-bot[bot] and julio-cfa committed Sep 13, 2024
1 parent 74ef9fe commit fa2d079
Show file tree
Hide file tree
Showing 13 changed files with 595 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/many-rules-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates)
28 changes: 21 additions & 7 deletions apps/meteor/app/authorization/server/functions/canDeleteMessage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { IUser } from '@rocket.chat/core-typings';
import type { IUser, IRoom } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';

import { getValue } from '../../../settings/server/raw';
import { canAccessRoomAsync } from './canAccessRoom';
import { hasPermissionAsync } from './hasPermission';

const elapsedTime = (ts: Date): number => {
Expand All @@ -13,6 +14,25 @@ export const canDeleteMessageAsync = async (
uid: string,
{ u, rid, ts }: { u: Pick<IUser, '_id' | 'username'>; rid: string; ts: Date },
): Promise<boolean> => {
const room = await Rooms.findOneById<Pick<IRoom, '_id' | 'ro' | 'unmuted' | 't' | 'teamId' | 'prid'>>(rid, {
projection: {
_id: 1,
ro: 1,
unmuted: 1,
t: 1,
teamId: 1,
prid: 1,
},
});

if (!room) {
return false;
}

if (!(await canAccessRoomAsync(room, { _id: uid }))) {
return false;
}

const forceDelete = await hasPermissionAsync(uid, 'force-delete-message', rid);

if (forceDelete) {
Expand Down Expand Up @@ -45,12 +65,6 @@ export const canDeleteMessageAsync = async (
}
}

const room = await Rooms.findOneById(rid, { projection: { ro: 1, unmuted: 1 } });

if (!room) {
return false;
}

if (room.ro === true && !(await hasPermissionAsync(uid, 'post-readonly', rid))) {
// Unless the user was manually unmuted
if (u.username && !(room.unmuted || []).includes(u.username)) {
Expand Down
38 changes: 36 additions & 2 deletions apps/meteor/app/otr/server/methods/updateOTRAck.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { api } from '@rocket.chat/core-services';
import type { IOTRMessage } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { canAccessRoomAsync } from '../../../authorization/server/functions/canAccessRoom';

declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
Expand All @@ -11,10 +15,40 @@ declare module '@rocket.chat/ui-contexts' {
}

Meteor.methods<ServerMethods>({
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: String,
ts: Date,
u: {
_id: String,
username: String,
name: String,
},
});

if (message?.t !== 'otr') {
throw new Meteor.Error('error-invalid-message', 'Invalid message type', { method: 'updateOTRAck' });
}

const room = await Rooms.findOneByIdAndType(message.rid, 'd', { 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 acknowledgeMessage: IOTRMessage = { ...message, otrAck: ack };
void api.broadcast('otrAckUpdate', { roomId: message.rid, acknowledgeMessage });
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -61,7 +63,14 @@ const AppDetails = ({ app }: AppDetailsProps) => {
<Box fontScale='h4' mbe={8} color='titles-labels'>
{t('Description')}
</Box>
<Box dangerouslySetInnerHTML={{ __html: isMarkdown ? detailedDescription.rendered : description }} withRichContent />
<Box
dangerouslySetInnerHTML={{
__html: isMarkdown
? DOMPurify.sanitize(detailedDescription.rendered, purifyOptions)
: DOMPurify.sanitize(description, purifyOptions),
}}
withRichContent
/>
</Box>

<Box is='section'>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -36,7 +38,7 @@ const AppReleasesItem = ({ release, ...props }: ReleaseItemProps): ReactElement
return (
<Accordion.Item title={title} {...props}>
{release.detailedChangelog?.rendered ? (
<Box dangerouslySetInnerHTML={{ __html: release.detailedChangelog?.rendered }} color='default' />
<Box dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(release.detailedChangelog?.rendered, purifyOptions) }} color='default' />
) : (
<Box color='default'>{t('No_release_information_provided')}</Box>
)}
Expand Down
50 changes: 50 additions & 0 deletions apps/meteor/client/views/marketplace/lib/purifyOptions.ts
Original file line number Diff line number Diff line change
@@ -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',
],
};
4 changes: 4 additions & 0 deletions apps/meteor/server/models/raw/Rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,10 @@ export class RoomsRaw extends BaseRaw<IRoom> implements IRoomsModel {
return this.findOne(query, options);
}

findOneByIdAndType(roomId: IRoom['_id'], type: IRoom['t'], options: FindOptions<IRoom> = {}): Promise<IRoom | null> {
return this.findOne({ _id: roomId, t: type }, options);
}

setCallStatus(_id: IRoom['_id'], status: IRoom['callStatus']): Promise<UpdateResult> {
const query: Filter<IRoom> = {
_id,
Expand Down
Loading

0 comments on commit fa2d079

Please sign in to comment.