Skip to content

Commit b3e9ea9

Browse files
Merge branch 'develop' into chore/migrate-livechat-omnichannel-openapi
2 parents 3c3e7ca + f4dcad4 commit b3e9ea9

File tree

77 files changed

+365
-391
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+365
-391
lines changed

.changeset/blue-points-dream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates)

apps/meteor/app/api/server/middlewares/authentication.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export function authenticationMiddleware(
2727
if (userId && authToken) {
2828
req.user = (await Users.findOneByIdAndLoginToken(userId as string, hashLoginToken(authToken as string))) || undefined;
2929
} else {
30-
req.user = await oAuth2ServerAuth({
31-
headers: req.headers as Record<string, string | undefined>,
32-
query: req.query as Record<string, string | undefined>,
33-
});
30+
const { authorization } = req.headers;
31+
const accessToken = typeof req.query.access_token === 'string' ? req.query.access_token : undefined;
32+
delete req.query.access_token;
33+
req.user = await oAuth2ServerAuth({ authorization, accessToken });
3434
}
3535

3636
if (config.rejectUnauthorized && !req.user) {

apps/meteor/app/oauth2-server-config/server/oauth/oauth2-server.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ async function getAccessToken(accessToken: string) {
1919
return OAuthAccessTokens.findOneByAccessToken(accessToken);
2020
}
2121

22-
export async function oAuth2ServerAuth(partialRequest: {
23-
headers: Record<string, string | undefined>;
24-
query: Record<string, string | undefined>;
25-
}): Promise<IUser | undefined> {
26-
const headerToken = partialRequest.headers.authorization?.replace('Bearer ', '');
27-
const queryToken = partialRequest.query.access_token;
28-
const incomingToken = headerToken || queryToken;
22+
export async function oAuth2ServerAuth(partialRequest: { authorization?: string; accessToken?: string }): Promise<IUser | undefined> {
23+
const headerToken = partialRequest.authorization?.replace('Bearer ', '');
24+
const incomingToken = headerToken || partialRequest.accessToken;
2925

3026
if (!incomingToken) {
3127
return;
@@ -76,10 +72,14 @@ oauth2server.app.get('/oauth/userinfo', async (req: Request, res: Response) => {
7672
});
7773

7874
API.v1.addAuthMethod((routeContext) => {
79-
const headers = Object.fromEntries(routeContext.request.headers.entries());
80-
const query = (isPlainObject(routeContext.queryParams) ? routeContext.queryParams : {}) as Record<string, string | undefined>;
75+
const authorization = routeContext.request.headers.get('authorization') ?? undefined;
76+
const query = isPlainObject(routeContext.queryParams) ? routeContext.queryParams : {};
77+
const accessToken = typeof query.access_token === 'string' ? query.access_token : undefined;
78+
if (routeContext.queryParams?.access_token) {
79+
delete routeContext.queryParams.access_token;
80+
}
8181

82-
return oAuth2ServerAuth({ headers, query });
82+
return oAuth2ServerAuth({ authorization, accessToken });
8383
});
8484

8585
(WebApp.connectHandlers as unknown as ReturnType<typeof express>).use(oauth2server.app);

apps/meteor/client/components/FingerprintChangeModal.tsx

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Box } from '@rocket.chat/fuselage';
2-
import { GenericModal } from '@rocket.chat/ui-client';
3-
import DOMPurify from 'dompurify';
2+
import { ExternalLink, GenericModal } from '@rocket.chat/ui-client';
43
import type { ReactElement } from 'react';
5-
import { useTranslation } from 'react-i18next';
4+
import { Trans, useTranslation } from 'react-i18next';
65

76
import { links } from '../lib/links';
87

@@ -24,26 +23,15 @@ const FingerprintChangeModal = ({ onConfirm, onCancel, onClose }: FingerprintCha
2423
confirmText={t('Configuration_update')}
2524
cancelText={t('New_workspace')}
2625
>
27-
<Box
28-
is='p'
29-
mbe={16}
30-
dangerouslySetInnerHTML={{
31-
__html: DOMPurify.sanitize(t('Unique_ID_change_detected_description')),
32-
}}
33-
/>
34-
<Box
35-
is='p'
36-
mbe={16}
37-
dangerouslySetInnerHTML={{
38-
__html: DOMPurify.sanitize(
39-
t('Unique_ID_change_detected_learn_more_link', { fingerPrintChangedFaq: links.go.fingerPrintChangedFaq }),
40-
{
41-
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
42-
ALLOWED_ATTR: ['href', 'title'],
43-
},
44-
),
45-
}}
46-
/>
26+
<Box is='p' mbe={16}>
27+
<Trans i18nKey='Unique_ID_change_detected_description' />
28+
</Box>
29+
<Box is='p' mbe={16}>
30+
<Trans
31+
i18nKey='Unique_ID_change_detected_learn_more_link'
32+
components={{ a: <ExternalLink to={links.go.fingerPrintChangedFaq} /> }}
33+
/>
34+
</Box>
4735
</GenericModal>
4836
);
4937
};

apps/meteor/client/components/FingerprintChangeModalConfirmation.tsx

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Box } from '@rocket.chat/fuselage';
2-
import { GenericModal } from '@rocket.chat/ui-client';
3-
import DOMPurify from 'dompurify';
2+
import { ExternalLink, GenericModal } from '@rocket.chat/ui-client';
43
import type { ReactElement } from 'react';
5-
import { useTranslation } from 'react-i18next';
4+
import { Trans, useTranslation } from 'react-i18next';
65

76
import { links } from '../lib/links';
87

@@ -30,28 +29,19 @@ const FingerprintChangeModalConfirmation = ({
3029
confirmText={newWorkspace ? t('Confirm_new_workspace') : t('Confirm_configuration_update')}
3130
onClose={onClose}
3231
>
33-
<Box
34-
is='p'
35-
mbe={16}
36-
dangerouslySetInnerHTML={{
37-
__html: newWorkspace
38-
? DOMPurify.sanitize(t('Confirm_new_workspace_description'))
39-
: DOMPurify.sanitize(t('Confirm_configuration_update_description')),
40-
}}
41-
/>
42-
<Box
43-
is='p'
44-
mbe={16}
45-
dangerouslySetInnerHTML={{
46-
__html: DOMPurify.sanitize(
47-
t('Unique_ID_change_detected_learn_more_link', { fingerPrintChangedFaq: links.go.fingerPrintChangedFaq }),
48-
{
49-
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
50-
ALLOWED_ATTR: ['href', 'title'],
51-
},
52-
),
53-
}}
54-
/>
32+
<Box is='p' mbe={16}>
33+
{newWorkspace ? (
34+
<Trans i18nKey='Confirm_new_workspace_description' />
35+
) : (
36+
<Trans i18nKey='Confirm_configuration_update_description' />
37+
)}
38+
</Box>
39+
<Box is='p' mbe={16}>
40+
<Trans
41+
i18nKey='Unique_ID_change_detected_learn_more_link'
42+
components={{ a: <ExternalLink to={links.go.fingerPrintChangedFaq} /> }}
43+
/>
44+
</Box>
5545
</GenericModal>
5646
);
5747
};

apps/meteor/client/views/account/security/ChangePassphrase.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Box, Field, FieldError, FieldGroup, FieldHint, FieldLabel, FieldRow, Pa
22
import { PasswordVerifierList } from '@rocket.chat/ui-client';
33
import { useToastMessageDispatch, usePasswordPolicy } from '@rocket.chat/ui-contexts';
44
import { useMutation } from '@tanstack/react-query';
5-
import DOMPurify from 'dompurify';
65
import { useEffect, useId } from 'react';
76
import { Controller, useForm } from 'react-hook-form';
87
import { Trans, useTranslation } from 'react-i18next';
@@ -99,12 +98,9 @@ export const ChangePassphrase = (): JSX.Element => {
9998

10099
return (
101100
<>
102-
<Box
103-
is='p'
104-
fontScale='p1'
105-
id={e2ePasswordExplanationId}
106-
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(t('E2E_Encryption_Password_Explanation')) }}
107-
/>
101+
<Box is='p' fontScale='p1' id={e2ePasswordExplanationId}>
102+
<Trans i18nKey='E2E_Encryption_Password_Explanation' />
103+
</Box>
108104
<Box mbs={36} w='full'>
109105
<Box is='h3' fontScale='h4' mbe={12}>
110106
{t('Change_E2EE_password')}

apps/meteor/client/views/account/tokens/AccountTokensTable/AccountTokensTable.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import {
1010
} from '@rocket.chat/ui-client';
1111
import { useSetModal, useToastMessageDispatch, useUserId, useMethod, useEndpoint } from '@rocket.chat/ui-contexts';
1212
import { useQuery, useQueryClient } from '@tanstack/react-query';
13-
import DOMPurify from 'dompurify';
1413
import type { ReactElement, RefObject } from 'react';
1514
import { useMemo, useCallback } from 'react';
16-
import { useTranslation } from 'react-i18next';
15+
import { Trans, useTranslation } from 'react-i18next';
1716

1817
import AccountTokensRow from './AccountTokensRow';
1918
import AddToken from './AddToken';
@@ -74,16 +73,9 @@ const AccountTokensTable = (): ReactElement => {
7473

7574
setModal(
7675
<GenericModal title={t('API_Personal_Access_Token_Generated')} onConfirm={closeModal}>
77-
<Box
78-
dangerouslySetInnerHTML={{
79-
__html: DOMPurify.sanitize(
80-
t('API_Personal_Access_Token_Generated_Text_Token_s_UserId_s', {
81-
token,
82-
userId,
83-
}),
84-
),
85-
}}
86-
/>
76+
<Box>
77+
<Trans i18nKey='API_Personal_Access_Token_Generated_Text_Token_s_UserId_s' values={{ token, userId }} />
78+
</Box>
8779
</GenericModal>,
8880
);
8981

apps/meteor/client/views/account/tokens/AccountTokensTable/AddToken.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import type { SelectOption } from '@rocket.chat/fuselage';
22
import { Box, TextInput, Button, Margins, Select, FieldError, FieldGroup, Field, FieldRow } from '@rocket.chat/fuselage';
33
import { GenericModal } from '@rocket.chat/ui-client';
44
import { useSetModal, useToastMessageDispatch, useUserId, useMethod } from '@rocket.chat/ui-contexts';
5-
import DOMPurify from 'dompurify';
65
import { useCallback, useId, useMemo } from 'react';
76
import { Controller, useForm } from 'react-hook-form';
8-
import { useTranslation } from 'react-i18next';
7+
import { Trans, useTranslation } from 'react-i18next';
98

109
type AddTokenFormData = {
1110
name: string;
@@ -53,16 +52,9 @@ const AddToken = ({ reload }: AddTokenProps) => {
5352

5453
setModal(
5554
<GenericModal title={t('API_Personal_Access_Token_Generated')} onConfirm={handleDismissModal} onClose={handleDismissModal}>
56-
<Box
57-
dangerouslySetInnerHTML={{
58-
__html: DOMPurify.sanitize(
59-
t('API_Personal_Access_Token_Generated_Text_Token_s_UserId_s', {
60-
token,
61-
userId,
62-
}),
63-
),
64-
}}
65-
/>
55+
<Box>
56+
<Trans i18nKey='API_Personal_Access_Token_Generated_Text_Token_s_UserId_s' values={{ token, userId }} />
57+
</Box>
6658
</GenericModal>,
6759
);
6860
} catch (error) {
Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
11
import { Box } from '@rocket.chat/fuselage';
2-
import DOMPurify from 'dompurify';
3-
import { useTranslation } from 'react-i18next';
2+
import { ExternalLink } from '@rocket.chat/ui-client';
3+
import { Trans } from 'react-i18next';
44

5-
const NewBot = () => {
6-
const { t } = useTranslation();
7-
8-
return (
9-
<Box
10-
pb={20}
11-
fontScale='h4'
12-
key='bots'
13-
dangerouslySetInnerHTML={{
14-
__html: DOMPurify.sanitize(t('additional_integrations_Bots'), {
15-
ALLOWED_TAGS: ['a'],
16-
ALLOWED_ATTR: ['href', 'target'],
17-
}),
18-
}}
5+
const NewBot = () => (
6+
<Box pb={20} fontScale='h4' key='bots'>
7+
<Trans
8+
i18nKey='additional_integrations_Bots'
9+
components={{ a: <ExternalLink to='https://github.com/RocketChat/hubot-rocketchat' /> }}
1910
/>
20-
);
21-
};
11+
</Box>
12+
);
2213

2314
export default NewBot;

apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { useAbsoluteUrl } from '@rocket.chat/ui-contexts';
2121
import DOMPurify from 'dompurify';
2222
import { useId, useMemo } from 'react';
2323
import { Controller, useFormContext } from 'react-hook-form';
24-
import { useTranslation } from 'react-i18next';
24+
import { Trans, useTranslation } from 'react-i18next';
2525

2626
import type { EditIncomingWebhookFormData } from './EditIncomingWebhook';
2727
import useClipboardWithToast from '../../../../hooks/useClipboardWithToast';
@@ -177,17 +177,9 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized<IIncomi
177177
/>
178178
</FieldRow>
179179
<FieldHint id={`${channelField}-hint-1`}>{t('Messages_that_are_sent_to_the_Incoming_WebHook_will_be_posted_here')}</FieldHint>
180-
<FieldHint
181-
id={`${channelField}-hint-2`}
182-
dangerouslySetInnerHTML={{
183-
__html: DOMPurify.sanitize(
184-
t('Start_with_s_for_user_or_s_for_channel_Eg_s_or_s', {
185-
postProcess: 'sprintf',
186-
sprintf: ['@', '#', '@john', '#general'],
187-
}),
188-
),
189-
}}
190-
/>
180+
<FieldHint id={`${channelField}-hint-2`}>
181+
<Trans i18nKey='Start_with_s_for_user_or_s_for_channel_Eg_s_or_s' components={{ code: <code className='inline' /> }} />
182+
</FieldHint>
191183
{errors?.channel && (
192184
<FieldError aria-live='assertive' id={`${channelField}-error`}>
193185
{errors?.channel.message}
@@ -272,10 +264,9 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized<IIncomi
272264
/>
273265
</FieldRow>
274266
<FieldHint id={`${emojiField}-hint-1`}>{t('You_can_use_an_emoji_as_avatar')}</FieldHint>
275-
<FieldHint
276-
id={`${emojiField}-hint-2`}
277-
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(t('Example_s', { postProcess: 'sprintf', sprintf: [':ghost:'] })) }}
278-
/>
267+
<FieldHint id={`${emojiField}-hint-2`}>
268+
<Trans i18nKey='Example_s' values={{ value: ':ghost:' }} components={{ code: <code className='inline' /> }} />
269+
</FieldHint>
279270
</Field>
280271
<Field>
281272
<FieldRow>

0 commit comments

Comments
 (0)