fix: sanitize username input to prevent hyperlinks (#24210)
* fix(username): sanitize input to prevent hyperlinks * fix(username): sanitize input to prevent hyperlinks * Implemented CodeRabbits comments * clean username before saving and keep input consistent * sanitize initial username on load to avoid mismatch with input * remove comments * remove comments * Used existing slugify lib instead of creating new one --------- Co-authored-by: Dhairyashil <dhairyashilshinde@xoxo-2.local> Co-authored-by: Devanshu Sharma <devanshusharma658@gmail.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
co-authored by
Dhairyashil
Devanshu Sharma
Anik Dhabal Babu
Udit Takkar
parent
0ae6e92558
commit
7d16da5c98
@@ -13,6 +13,7 @@ import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||
import { useDebounce } from "@calcom/lib/hooks/useDebounce";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/types/server/routers/_app";
|
||||
@@ -81,7 +82,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
// Use the current username or if it's not set, use the one available from stripe
|
||||
setInputUsernameValue(currentUsername || stripeCustomer?.username || "");
|
||||
setInputUsernameValue(slugify(currentUsername || stripeCustomer?.username || "", true));
|
||||
}, [setInputUsernameValue, currentUsername, stripeCustomer?.username]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -105,7 +106,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
const updateUsername = trpc.viewer.me.updateProfile.useMutation({
|
||||
onSuccess: async () => {
|
||||
onSuccessMutation && (await onSuccessMutation());
|
||||
await update({ username: inputUsernameValue });
|
||||
const sanitizedUsername = slugify(inputUsernameValue || "");
|
||||
await update({ username: sanitizedUsername });
|
||||
setOpenDialogSaveUsername(false);
|
||||
},
|
||||
onError: (error) => {
|
||||
@@ -171,11 +173,12 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
};
|
||||
|
||||
const saveUsername = () => {
|
||||
const sanitizedUsername = slugify(inputUsernameValue || "");
|
||||
if (usernameChangeCondition !== UsernameChangeStatusEnum.UPGRADE) {
|
||||
updateUsername.mutate({
|
||||
username: inputUsernameValue,
|
||||
username: sanitizedUsername,
|
||||
});
|
||||
setCurrentUsername(inputUsernameValue);
|
||||
setCurrentUsername(sanitizedUsername);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -234,7 +237,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
if (searchParams?.toString() !== _searchParams.toString()) {
|
||||
router.replace(`${pathname}?${_searchParams.toString()}`);
|
||||
}
|
||||
setInputUsernameValue(event.target.value);
|
||||
const sanitized = slugify(event.target.value, true);
|
||||
setInputUsernameValue(sanitized);
|
||||
}}
|
||||
data-testid="username-input"
|
||||
/>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { DialogContent, DialogFooter, DialogClose } from "@calcom/ui/components/
|
||||
import { TextField } from "@calcom/ui/components/form";
|
||||
import { Icon } from "@calcom/ui/components/icon";
|
||||
import { Tooltip } from "@calcom/ui/components/tooltip";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
|
||||
import type { TRPCClientErrorLike } from "@trpc/client";
|
||||
|
||||
@@ -72,10 +73,11 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
|
||||
|
||||
const updateUsernameMutation = trpc.viewer.me.updateProfile.useMutation({
|
||||
onSuccess: async () => {
|
||||
const sanitizedUsername = slugify(inputUsernameValue || "");
|
||||
onSuccessMutation && (await onSuccessMutation());
|
||||
setOpenDialogSaveUsername(false);
|
||||
setCurrentUsername(inputUsernameValue);
|
||||
await update({ username: inputUsernameValue });
|
||||
setCurrentUsername(sanitizedUsername);
|
||||
await update({ username: sanitizedUsername });
|
||||
},
|
||||
onError: (error) => {
|
||||
onErrorMutation && onErrorMutation(error);
|
||||
@@ -108,8 +110,9 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
|
||||
};
|
||||
|
||||
const updateUsername = async () => {
|
||||
const sanitizedUsername = slugify(inputUsernameValue || "");
|
||||
updateUsernameMutation.mutate({
|
||||
username: inputUsernameValue,
|
||||
username: sanitizedUsername,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -134,7 +137,8 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
|
||||
)}
|
||||
onChange={(event) => {
|
||||
event.preventDefault();
|
||||
setInputUsernameValue(event.target.value);
|
||||
const sanitized = slugify(event.target.value, true);
|
||||
setInputUsernameValue(sanitized);
|
||||
}}
|
||||
data-testid="username-input"
|
||||
{...rest}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||
import { WEBSITE_URL, IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/types/server/routers/_app";
|
||||
|
||||
@@ -56,7 +57,7 @@ export const UsernameAvailabilityField = ({
|
||||
: { username: currentUsernameState || "", setQuery: setCurrentUsernameState };
|
||||
const formMethods = useForm({
|
||||
defaultValues: {
|
||||
username: currentUsername,
|
||||
username: slugify(currentUsername || user.username || ""),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -78,7 +79,11 @@ export const UsernameAvailabilityField = ({
|
||||
setCurrentUsername={setCurrentUsername}
|
||||
inputUsernameValue={value}
|
||||
usernameRef={ref}
|
||||
setInputUsernameValue={onChange}
|
||||
setInputUsernameValue={(val) => {
|
||||
const displayValue = slugify(val, true);
|
||||
formMethods.setValue("username", displayValue);
|
||||
onChange?.(displayValue);
|
||||
}}
|
||||
onSuccessMutation={onSuccessMutation}
|
||||
onErrorMutation={onErrorMutation}
|
||||
disabled={!!user.organization?.id}
|
||||
|
||||
Reference in New Issue
Block a user