Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

complete xss prevent #98

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,496 changes: 1,973 additions & 2,523 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@types/lint-staged": "^13.3.0",
"axios": "^1.6.8",
"cross-env": "^7.0.3",
"dompurify": "^3.1.5",
"framer-motion": "^11.0.25",
"jwt-decode": "^4.0.0",
"react": "^18.2.0",
Expand All @@ -42,6 +43,7 @@
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/node": "^20.13.0",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
Expand Down
14 changes: 9 additions & 5 deletions src/pages/ChangePassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useNavigate } from "react-router-dom";
import { useAuthStore } from "@stores/AuthStore.ts";
import { SvgIcon } from "@common/components";
import { ButtonPreviewPassword } from "@components/index";

import DOMPurify from "dompurify";
export interface FormDataChangePassword {
oldPassword: string;
confirmPassword: string;
Expand All @@ -19,17 +19,21 @@ export interface FormDataChangePassword {

const schema = yup.object().shape({
oldPassword: yup.string().required(),

password: yup
.string()
.required("Password is required")
.matches(
/^(?=.*\d)(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$/,
"Password must contain at least 8 characters, including at least one digit, one special character, one uppercase letter, and one lowercase letter",
),
)
.matches(/^[^']*$/, "Password cannot contain single quotes"),

confirmPassword: yup
.string()
.required("Confirm password is required")
.oneOf([yup.ref("password")], "Confirm password must match with password"),
.oneOf([yup.ref("password")], "Confirm password must match with password")
.matches(/^[^']*$/, "Confirm password cannot contain single quotes"),
});

const ChangePassword = () => {
Expand All @@ -55,8 +59,8 @@ const ChangePassword = () => {
dataPassChange,
) => {
const updateData: FormDataChangePasswordApi = {
old_password: dataPassChange.oldPassword,
new_password: dataPassChange.password,
old_password: DOMPurify.sanitize(dataPassChange.oldPassword),
new_password: DOMPurify.sanitize(dataPassChange.password),
};

const { message, error } = await usersService.changePassword({
Expand Down
15 changes: 12 additions & 3 deletions src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import usersService from "@services/users.service";
import { useAuthStore } from "@stores/AuthStore";
import { jwtDecode } from "jwt-decode";
import { BrandLogo } from "@common/components";
import DOMPurify from "dompurify";

type LoginFieldSchema<T extends FieldValues> = {
name: Path<T>;
Expand All @@ -44,11 +45,15 @@ const PasswordSchema: LoginFieldSchema<LoginFormData> = {
};

const schema = yup.object().shape({
email_phone: yup.string().required("Email or Phone is required"),
email_phone: yup
.string()
.required("Email or Phone is required")
.matches(/^[^']*$/, "Password cannot contain then sensitive character"),
password: yup
.string()
.required("Password is required")
.min(8, "Password must be at least 8 characters"),
.min(8, "Password must be at least 8 characters")
.matches(/^[^']*$/, "Password cannot contain then sensitive character"),
});

const Login = () => {
Expand All @@ -63,7 +68,11 @@ const Login = () => {

const { mutate } = useMutation({
mutationFn: (body: LoginFormData) => {
return usersService.login(body);
const data: LoginFormData = {
email_phone: DOMPurify.sanitize(body.email_phone),
password: DOMPurify.sanitize(body.password),
};
return usersService.login(data);
},
});

Expand Down
13 changes: 9 additions & 4 deletions src/pages/Password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { isAxiosUnprocessableEntityError } from "@utils/utils.ts";
import { ResponseApi } from "@utils/utils.type.ts";
import { SvgIcon } from "@common/components";
import { ButtonPreviewPassword } from "@components/index";
import DOMPurify from "dompurify";

export interface UserInfoForm {
data: {
Expand All @@ -34,14 +35,18 @@ const Password = () => {
const location = useLocation();
const toggleVisibility = () => setIsVisible(!isVisible);
const schema = yup.object().shape({
password: yup.string().required("Password is required"),
password: yup
.string()
.required("Password is required")
.matches(/^[^']*$/, "Password cannot contain then sensitive character"),
confirmPassword: yup
.string()
.required("Confirm password is required")
.oneOf(
[yup.ref("password")],
"Confirm password must match with passwords",
),
)
.matches(/^[^']*$/, "Password cannot contain then sensitive character"),
});
const {
register,
Expand All @@ -58,8 +63,8 @@ const Password = () => {
try {
const response = await usersService.resetPassword({
email_phone: location.state.email_phone,
password: dataForm.password,
confirm_password: dataForm.confirmPassword,
password: DOMPurify.sanitize(dataForm.password),
confirm_password: DOMPurify.sanitize(dataForm.confirmPassword),
otp: location.state.otp,
});
if (response.status === 200) {
Expand Down
12 changes: 10 additions & 2 deletions src/pages/Recovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { isAxiosUnprocessableEntityError } from "@utils/utils.ts";
import { ResponseApi } from "@utils/utils.type.ts";
import usersService from "@services/users.service";
import { isAxiosError } from "axios";
import DOMPurify from "dompurify";

const Recovery = () => {
const navigate = useNavigate();
Expand All @@ -34,12 +35,19 @@ const Recovery = () => {
"Phone number must be exactly 10 digits",
(val: string | undefined) => val!.length === 10,
)
.required("Phone number is required"),
.required("Phone number is required")
.matches(
/^[^']*$/,
"Email or Phone cannot contain then sensitive character",
),
});

const { mutate } = useMutation({
mutationFn: (body: RecoveryForm) => {
return usersService.recovery(body);
const data: RecoveryForm = {
email_phone: DOMPurify.sanitize(body.email_phone),
};
return usersService.recovery(data);
},
});

Expand Down
13 changes: 10 additions & 3 deletions src/pages/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ const schema: yup.ObjectSchema<Omit<RegisterForm, "email" | "phone_number">> =
yup.object().shape({
first_name: yup
.string()
.required(ValidationRules.firstnameRule.required.message),
.required(ValidationRules.firstnameRule.required.message)
.matches(/^[^']*$/, "First Name cannot contain then sensitive character"),
last_name: yup
.string()
.required(ValidationRules.lastnameRule.required.message),
.required(ValidationRules.lastnameRule.required.message)
.matches(/^[^']*$/, "Last Name cannot contain then sensitive character"),
email_phone: yup
.string()
.required("This field is required")
Expand All @@ -43,6 +45,10 @@ const schema: yup.ObjectSchema<Omit<RegisterForm, "email" | "phone_number">> =
).test(value);
return isEmail || isPhone;
},
)
.matches(
/^[^']*$/,
"Email or Phone cannot contain then sensitive character",
),
password: yup
.string()
Expand All @@ -54,7 +60,8 @@ const schema: yup.ObjectSchema<Omit<RegisterForm, "email" | "phone_number">> =
.matches(
new RegExp(ValidationRules.passwordRule.pattern.value),
ValidationRules.passwordRule.pattern.message,
),
)
.matches(/^[^']*$/, "Password cannot contain then sensitive character"),
agreeToTerms: yup.boolean().required().isTrue(),
subcribe: yup.boolean(),
});
Expand Down
7 changes: 6 additions & 1 deletion src/pages/VerifyAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useEffect, useRef, useState } from "react";
import usersService from "@services/users.service";
import { useNavigate } from "react-router-dom";
import { useAuthStore } from "@stores/AuthStore";
import DOMPurify from "dompurify";

enum VerifyMethod {
EMAIL = "email",
Expand Down Expand Up @@ -51,7 +52,11 @@ const VerifyAccount = () => {

const { mutate } = useMutation({
mutationFn: (_body: { email_phone: string; token: string }) => {
return usersService.sendVerifyAccountOTP(_body);
const data = {
email_phone: DOMPurify.sanitize(_body.email_phone),
token: DOMPurify.sanitize(_body.token),
};
return usersService.sendVerifyAccountOTP(data);
},
});

Expand Down