From 2da2552c3bf7091f81f20ff5852fd4416ff90ce9 Mon Sep 17 00:00:00 2001 From: Amit Ranjan Date: Tue, 24 Sep 2024 02:21:02 +0530 Subject: [PATCH 1/2] fix: improve error handling for invalid URL submission --- frontend/src/components/Menu/MenuDialog.tsx | 4 +++- frontend/src/constants.ts | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Menu/MenuDialog.tsx b/frontend/src/components/Menu/MenuDialog.tsx index 936a23bc9..f5463db4d 100644 --- a/frontend/src/components/Menu/MenuDialog.tsx +++ b/frontend/src/components/Menu/MenuDialog.tsx @@ -25,6 +25,7 @@ import { ContentContainer } from "@/app-components/dialogs/layouts/ContentContai import { ContentItem } from "@/app-components/dialogs/layouts/ContentItem"; import { Input } from "@/app-components/inputs/Input"; import { ToggleableInput } from "@/app-components/inputs/ToggleableInput"; +import { URL_REGEX } from "@/constants"; import { IMenuItem, IMenuItemAttributes, MenuType } from "@/types/menu.types"; export type MenuDialogProps = DialogProps & { @@ -66,7 +67,8 @@ export const MenuDialog: FC = ({ url: { required: t("message.url_is_invalid"), validate: (value: string = "") => - isAbsoluteUrl(value) || t("message.url_is_invalid"), + (isAbsoluteUrl(value) && URL_REGEX.test(value)) || + t("message.url_is_invalid"), }, payload: {}, }; diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts index 6e9d73765..17d93abaa 100644 --- a/frontend/src/constants.ts +++ b/frontend/src/constants.ts @@ -21,3 +21,6 @@ export const DATE_TIME_FORMAT = { export const USER_DEFAULT_PICTURE = "https://avatars.dicebear.com/v2/identicon/6659e07058af581e68e33d05.svg"; + +export const URL_REGEX = + /^(https?:\/\/)?((([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:[0-9]{1,5})?(\/[^\s]*)?)$/i; From 3efa0fb60f4862281a6dcd31dab2b9a352c522a1 Mon Sep 17 00:00:00 2001 From: Amit Ranjan Date: Tue, 24 Sep 2024 12:00:20 +0530 Subject: [PATCH 2/2] fix: incorporated the url class --- frontend/src/components/Menu/MenuDialog.tsx | 6 ++---- frontend/src/constants.ts | 3 --- frontend/src/utils/URL.ts | 16 +++++++++++++++- package-lock.json | 7 +++++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Menu/MenuDialog.tsx b/frontend/src/components/Menu/MenuDialog.tsx index f5463db4d..e0144922d 100644 --- a/frontend/src/components/Menu/MenuDialog.tsx +++ b/frontend/src/components/Menu/MenuDialog.tsx @@ -14,7 +14,6 @@ import { DialogProps, MenuItem, } from "@mui/material"; -import { isAbsoluteUrl } from "next/dist/shared/lib/utils"; import { useEffect, FC } from "react"; import { useForm, Controller } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -25,8 +24,8 @@ import { ContentContainer } from "@/app-components/dialogs/layouts/ContentContai import { ContentItem } from "@/app-components/dialogs/layouts/ContentItem"; import { Input } from "@/app-components/inputs/Input"; import { ToggleableInput } from "@/app-components/inputs/ToggleableInput"; -import { URL_REGEX } from "@/constants"; import { IMenuItem, IMenuItemAttributes, MenuType } from "@/types/menu.types"; +import { isAbsoluteUrl } from "@/utils/URL"; export type MenuDialogProps = DialogProps & { open: boolean; @@ -67,8 +66,7 @@ export const MenuDialog: FC = ({ url: { required: t("message.url_is_invalid"), validate: (value: string = "") => - (isAbsoluteUrl(value) && URL_REGEX.test(value)) || - t("message.url_is_invalid"), + isAbsoluteUrl(value) || t("message.url_is_invalid"), }, payload: {}, }; diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts index 17d93abaa..6e9d73765 100644 --- a/frontend/src/constants.ts +++ b/frontend/src/constants.ts @@ -21,6 +21,3 @@ export const DATE_TIME_FORMAT = { export const USER_DEFAULT_PICTURE = "https://avatars.dicebear.com/v2/identicon/6659e07058af581e68e33d05.svg"; - -export const URL_REGEX = - /^(https?:\/\/)?((([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:[0-9]{1,5})?(\/[^\s]*)?)$/i; diff --git a/frontend/src/utils/URL.ts b/frontend/src/utils/URL.ts index 086e4d7ad..8db093d2a 100644 --- a/frontend/src/utils/URL.ts +++ b/frontend/src/utils/URL.ts @@ -29,7 +29,6 @@ export const getFromQuery = ({ export const buildURL = (baseUrl: string, relativePath: string): string => { try { - const url = new URL(relativePath, baseUrl); return url.toString(); @@ -37,3 +36,18 @@ export const buildURL = (baseUrl: string, relativePath: string): string => { throw new Error(`Invalid base URL: ${baseUrl}`); } }; + +export const isAbsoluteUrl = (value: string = ""): boolean => { + try { + const url = new URL(value); + const hostnameParts = url.hostname.split("."); + + return ( + (url.protocol === "http:" || url.protocol === "https:") && + hostnameParts.length > 1 && + hostnameParts[hostnameParts.length - 1].length > 1 + ); + } catch (error) { + return false; + } +}; diff --git a/package-lock.json b/package-lock.json index 5ac837dd9..00fa48e8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "hexabot", "version": "2.0.0", + "license": "AGPL-3.0-only", "workspaces": [ "frontend", "widget" @@ -19,7 +20,8 @@ }, "frontend": { "name": "hexabot-ui", - "version": "0.1.0", + "version": "2.0.0", + "license": "AGPL-3.0-only", "dependencies": { "@chatscope/chat-ui-kit-react": "^2.0.3", "@chatscope/chat-ui-kit-styles": "^1.4.0", @@ -9802,7 +9804,8 @@ }, "widget": { "name": "hexabot-widget", - "version": "0.0.0", + "version": "2.0.0", + "license": "AGPL-3.0-only", "dependencies": { "@types/emoji-js": "^3.5.2", "autolinker": "^4.0.0",