diff --git a/.changeset/funny-islands-run.md b/.changeset/funny-islands-run.md new file mode 100644 index 0000000000000..67523184d7222 --- /dev/null +++ b/.changeset/funny-islands-run.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': major +--- + +fix(integrations): Add validation for Incoming Webhook Avatar diff --git a/.changeset/light-singers-join.md b/.changeset/light-singers-join.md new file mode 100644 index 0000000000000..87ee18ec55aa0 --- /dev/null +++ b/.changeset/light-singers-join.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix(integrations): Add validation for Incoming Webhook Avatar diff --git a/.changeset/mighty-queens-hug.md b/.changeset/mighty-queens-hug.md new file mode 100644 index 0000000000000..a03e97c78f44d --- /dev/null +++ b/.changeset/mighty-queens-hug.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix(integrations): Add validation for Incoming Webhook Emoji field format diff --git a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx index 6c80c4e7828b7..5e9586750c0d9 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/EditIncomingWebhook.tsx @@ -5,12 +5,17 @@ import { useSetModal, useTranslation, useRouter, useRouteParameter } from '@rock import { useId, useCallback } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; + + + import IncomingWebhookForm from './IncomingWebhookForm'; import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '../../../../components/Page'; import { useCreateIntegration } from '../hooks/useCreateIntegration'; import { useDeleteIntegration } from '../hooks/useDeleteIntegration'; import { useUpdateIntegration } from '../hooks/useUpdateIntegration'; + + export type EditIncomingWebhookFormData = { enabled: boolean; channel: string; @@ -51,6 +56,8 @@ const EditIncomingWebhook = ({ webhookData }: EditIncomingWebhookProps) => { const setModal = useSetModal(); const tab = useRouteParameter('type'); + + const deleteIntegration = useDeleteIntegration(INCOMING_TYPE); const updateIntegration = useUpdateIntegration(INCOMING_TYPE); const createIntegration = useCreateIntegration(INCOMING_TYPE); @@ -63,6 +70,9 @@ const EditIncomingWebhook = ({ webhookData }: EditIncomingWebhookProps) => { formState: { isDirty }, } = methods; + + + const handleDeleteIntegration = useCallback(() => { const onDelete = async () => { if (!webhookData?._id) { diff --git a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx index aa8e77f5b2746..6067897c0219c 100644 --- a/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx +++ b/apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx @@ -32,6 +32,33 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized { + if (!value) return true; + try { + const url = new URL(value); + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + return t('The_URL_is_invalid', { url: value }); + } + return true; + } catch (error) { + return t('The_URL_is_invalid', { url: value }); + } + }; + + const validateEmoji = (value: string) => { + if (!value) return true; + + if (/^:.+:$/.test(value)) { + return true; + } + + if ([...value.trim()].length === 1 && /\p{Extended_Pictographic}/u.test(value)) { + return true; + } + + return t('Invalid_emoji_format_Must_be_in_colon_format', { example: ':ghost:' }); + }; + const { control, watch, @@ -236,47 +263,67 @@ const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized {t('Choose_the_alias_that_will_appear_before_the_username_in_messages')} + {t('Avatar_URL')} ( + rules={{ validate: validateAvatarUrl }} + render={({ field, fieldState: { error } }) => ( } + aria-invalid={Boolean(error)} /> )} /> + {errors?.avatar && ( + + {errors.avatar.message} + + )} + {t('You_can_change_a_different_avatar_too')} {t('Should_be_a_URL_of_an_image')} + {t('Emoji')} ( + rules={{ validate: validateEmoji }} + render={({ field, fieldState: { error } }) => ( } + aria-invalid={Boolean(error)} /> )} /> + + {errors?.emoji && ( + + {errors.emoji.message} + + )} + {t('You_can_use_an_emoji_as_avatar')} + {t('Override_Destination_Channel')} diff --git a/apps/meteor/client/views/admin/integrations/outgoing/EditOutgoingWebhook.tsx b/apps/meteor/client/views/admin/integrations/outgoing/EditOutgoingWebhook.tsx index 339e5a07e99c7..fef665438df81 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/EditOutgoingWebhook.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/EditOutgoingWebhook.tsx @@ -12,6 +12,9 @@ import { useCreateIntegration } from '../hooks/useCreateIntegration'; import { useDeleteIntegration } from '../hooks/useDeleteIntegration'; import { useUpdateIntegration } from '../hooks/useUpdateIntegration'; +// + + type EditOutgoingWebhookFormData = { enabled: boolean; impersonateUser: boolean; @@ -68,6 +71,7 @@ type EditOutgoingWebhookProps = { const EditOutgoingWebhook = ({ webhookData }: EditOutgoingWebhookProps) => { const t = useTranslation(); + const setModal = useSetModal(); const router = useRouter(); @@ -87,6 +91,8 @@ const EditOutgoingWebhook = ({ webhookData }: EditOutgoingWebhookProps) => { const createIntegration = useCreateIntegration(OUTGOING_TYPE); const updateIntegration = useUpdateIntegration(OUTGOING_TYPE); + + const handleDeleteIntegration = useCallback(() => { const onDelete = async () => { deleteIntegration.mutate({ type: OUTGOING_TYPE, integrationId: webhookData?._id }); @@ -151,7 +157,7 @@ const EditOutgoingWebhook = ({ webhookData }: EditOutgoingWebhookProps) => { )} - +