Fix webhook pages in Settings (#10902)

## Context

Some users were able to set an empty URL as webhook targetUrl, which was
breaking the Webhook List and Detail pages

## Fix
- Making sure to protect getHostNameOrThrow by isValidUrl
- rework webhook form to prevent creation of invalid webhooks

Fixes https://github.com/twentyhq/twenty/issues/10822
This commit is contained in:
Charles Bochet
2025-03-14 18:26:28 +01:00
committed by GitHub
parent b47dcba313
commit c833b1c449
2 changed files with 37 additions and 12 deletions

View File

@ -5,7 +5,7 @@ import { IconChevronRight } from 'twenty-ui';
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { getUrlHostnameOrThrow } from 'twenty-shared';
import { getUrlHostnameOrThrow, isValidUrl } from 'twenty-shared';
export const StyledApisFieldTableRow = styled(TableRow)`
grid-template-columns: 1fr 28px;
@ -39,7 +39,9 @@ export const SettingsDevelopersWebhookTableRow = ({
return (
<StyledApisFieldTableRow to={to}>
<StyledUrlTableCell>
{getUrlHostnameOrThrow(fieldItem.targetUrl)}
{isValidUrl(fieldItem.targetUrl)
? getUrlHostnameOrThrow(fieldItem.targetUrl)
: fieldItem.targetUrl}
</StyledUrlTableCell>
<StyledIconTableCell>
<StyledIconChevronRight

View File

@ -35,7 +35,12 @@ export const useWebhookUpdateForm = ({
const [formData, setFormData] = useState<WebhookFormData>({
targetUrl: '',
description: '',
operations: [],
operations: [
{
object: '*',
action: '*',
},
],
secret: '',
});
@ -98,20 +103,35 @@ export const useWebhookUpdateForm = ({
});
}, 300);
const validateData = (data: Partial<WebhookFormData>) => {
if (isDefined(data?.targetUrl)) {
const trimmedUrl = data.targetUrl.trim();
const isTargetUrlValid = isValidUrl(trimmedUrl);
setIsTargetUrlValid(isTargetUrlValid);
if (isTargetUrlValid) {
setTitle(getUrlHostnameOrThrow(trimmedUrl) || 'New Webhook');
const isFormValidAndSetErrors = () => {
const { targetUrl } = formData;
if (isDefined(targetUrl)) {
const trimmedUrl = targetUrl.trim();
const isValid = isValidUrl(trimmedUrl);
if (!isValid) {
setIsTargetUrlValid(false);
return false;
}
setIsTargetUrlValid(true);
}
return true;
};
const updateWebhook = async (data: Partial<WebhookFormData>) => {
validateData(data);
setFormData((prev) => ({ ...prev, ...data }));
if (!isFormValidAndSetErrors()) {
return;
}
if (isDefined(data?.targetUrl)) {
setTitle(getUrlHostnameOrThrow(data.targetUrl) || 'New Webhook');
}
await handleSave();
};
@ -173,7 +193,10 @@ export const useWebhookUpdateForm = ({
operations,
secret: data.secret,
});
setTitle(getUrlHostnameOrThrow(data.targetUrl) || 'New Webhook');
if (isValidUrl(data.targetUrl)) {
setTitle(getUrlHostnameOrThrow(data.targetUrl));
}
setLoading(false);
},
});