diff --git a/package-lock.json b/package-lock.json
index 9a27f3b9..82370420 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"axios": "^1.12.2",
"date-fns": "^3.0.6",
"dayjs": "^1.11.9",
+ "dompurify": "^3.4.15",
"formik": "^2.2.9",
"framer-motion": "^7.3.6",
"history": "^5.3.0",
@@ -8419,6 +8420,15 @@
}
]
},
+ "node_modules/dompurify": {
+ "version": "3.4.15",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.15.tgz",
+ "integrity": "sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
diff --git a/package.json b/package.json
index 3ee87f36..755e24e2 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"axios": "^1.12.2",
"date-fns": "^3.0.6",
"dayjs": "^1.11.9",
+ "dompurify": "^3.4.15",
"formik": "^2.2.9",
"framer-motion": "^7.3.6",
"history": "^5.3.0",
diff --git a/src/components/SafeHtml.js b/src/components/SafeHtml.js
new file mode 100644
index 00000000..32f88aab
--- /dev/null
+++ b/src/components/SafeHtml.js
@@ -0,0 +1,17 @@
+import PropTypes from 'prop-types';
+import { sanitizeHtml } from 'utils/sanitizeHtml';
+
+/**
+ * Renders HTML after {@link sanitizeHtml}. Use instead of raw
+ * dangerouslySetInnerHTML for messages, settings, CMS, and i18n HTML.
+ */
+const SafeHtml = ({ html, component: Component = 'div', ...rest }) => {
+ return ;
+};
+
+SafeHtml.propTypes = {
+ html: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ component: PropTypes.elementType
+};
+
+export default SafeHtml;
diff --git a/src/pages/Message/Details/index.js b/src/pages/Message/Details/index.js
index f3f4c5cb..c6639902 100644
--- a/src/pages/Message/Details/index.js
+++ b/src/pages/Message/Details/index.js
@@ -19,6 +19,7 @@ import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { FormattedMessage } from "react-intl";
import usePageTitle from 'components/usePageTitle';
import { PRIMARY_CONTAINED_BUTTON_SX } from 'themes/colorConst';
+import SafeHtml from 'components/SafeHtml';
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -93,7 +94,7 @@ const Index = () => {
{DateUtils.datetimeStr(record?.sentDate)}
-
+
diff --git a/src/pages/Payment/MultiPaymentWindow.js b/src/pages/Payment/MultiPaymentWindow.js
index f1bbcef9..a7f4799d 100644
--- a/src/pages/Payment/MultiPaymentWindow.js
+++ b/src/pages/Payment/MultiPaymentWindow.js
@@ -25,6 +25,7 @@ import Loadable from 'components/Loadable';
const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
import * as FormatUtils from "utils/FormatUtils";
+import SafeHtml from 'components/SafeHtml';
import VisaIcon from "assets/images/icons/visacard.svg";
import MasterIcon from "assets/images/icons/mastercard.svg";
import JcbIcon from "assets/images/icons/jcb.svg";
@@ -500,7 +501,7 @@ const MultiPaymentWindow = (props) => {
-
+
diff --git a/src/pages/Proof/Payment/Pay.js b/src/pages/Proof/Payment/Pay.js
index ad673edf..0de0b344 100644
--- a/src/pages/Proof/Payment/Pay.js
+++ b/src/pages/Proof/Payment/Pay.js
@@ -28,6 +28,7 @@ const BackgroundHead = {
import { PNSPS_BUTTON_THEME, PNSPS_LONG_BUTTON_THEME } from "../../../themes/buttonConst";
import { ThemeProvider } from "@emotion/react";
import { FormattedMessage, useIntl } from "react-intl";
+import SafeHtml from 'components/SafeHtml';
// ==============================|| DASHBOARD - DEFAULT ||============================== //
const Index = ({ record }) => {
@@ -107,7 +108,7 @@ const Index = ({ record }) => {
-
+
@@ -116,14 +117,12 @@ const Index = ({ record }) => {
-
+ })} />
@@ -213,7 +212,7 @@ const Index = ({ record }) => {
-
+
diff --git a/src/pages/Proof/Payment/Pay_DN.js b/src/pages/Proof/Payment/Pay_DN.js
index fe40882e..8a92d8aa 100644
--- a/src/pages/Proof/Payment/Pay_DN.js
+++ b/src/pages/Proof/Payment/Pay_DN.js
@@ -14,6 +14,7 @@ import * as DateUtils from "utils/DateUtils";
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { FormattedMessage, useIntl } from "react-intl";
+import SafeHtml from 'components/SafeHtml';
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -76,19 +77,16 @@ const isAfter = checkIsOnlyOnlinePayment();
)}
-
+ )} />
-
+ )} />
-
+ )} />
-
+ )} />
diff --git a/src/pages/Proof/Payment/Pay_Office.js b/src/pages/Proof/Payment/Pay_Office.js
index 1c201051..9eeefdb8 100644
--- a/src/pages/Proof/Payment/Pay_Office.js
+++ b/src/pages/Proof/Payment/Pay_Office.js
@@ -14,6 +14,7 @@ import * as DateUtils from "utils/DateUtils";
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { FormattedMessage, useIntl } from "react-intl";
+import SafeHtml from 'components/SafeHtml';
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -78,59 +79,47 @@ const isAfter = checkIsOnlyOnlinePayment();
-
+ )} />
-
+ )} />
-
+ )} />
-
+ )} />
-
+ )} />
-
+ )} />
diff --git a/src/pages/Proof/Payment/Pay_Online.js b/src/pages/Proof/Payment/Pay_Online.js
index 33582994..93684400 100644
--- a/src/pages/Proof/Payment/Pay_Online.js
+++ b/src/pages/Proof/Payment/Pay_Online.js
@@ -22,6 +22,7 @@ import Loadable from 'components/Loadable';
const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
+import SafeHtml from 'components/SafeHtml';
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: '100%',
@@ -195,32 +196,28 @@ const Index = () => {
)}
-
+ )} />
-
+ )} />
{checkPaymentSuspension()?
:null
@@ -272,13 +269,11 @@ const Index = () => {
}
-
+ )} />
@@ -338,7 +333,7 @@ const Index = () => {
-
+
diff --git a/src/pages/Proof/Reply_Public/ProofForm.js b/src/pages/Proof/Reply_Public/ProofForm.js
index 56783bf3..a994122c 100644
--- a/src/pages/Proof/Reply_Public/ProofForm.js
+++ b/src/pages/Proof/Reply_Public/ProofForm.js
@@ -23,6 +23,7 @@ import { useNavigate } from "react-router-dom";
import * as DateUtils from "utils/DateUtils"
import Loadable from 'components/Loadable';
import { notifyActionSuccess } from 'utils/CommonFunction';
+import SafeHtml from 'components/SafeHtml';
import { PNSPS_BUTTON_THEME } from "themes/buttonConst";
import { PRIMARY_CONTAINED_BUTTON_SX } from "themes/colorConst";
import { ThemeProvider } from "@emotion/react";
@@ -382,7 +383,7 @@ const FormPanel = ({ formData }) => {
-
+
>
);
@@ -468,7 +469,7 @@ const FormPanel = ({ formData }) => {
{checkPaymentSuspension()?
-
+
:null
}
{
@@ -536,7 +537,7 @@ const FormPanel = ({ formData }) => {
-
+
>
);
@@ -599,13 +600,13 @@ const FormPanel = ({ formData }) => {
}
{/*
-
+
*/}
{!isOnlyOnlinePayment?
-
+
:null
}
diff --git a/src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js b/src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js
index c56db966..04e96f19 100644
--- a/src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js
+++ b/src/pages/PublicNotice/ApplyForm/PublicNoticeApplyForm.js
@@ -34,6 +34,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import { useState, useEffect, useRef } from 'react';
import { checkPaymentSuspension } from "utils/Utils";
import { PRIMARY_CONTAINED_BUTTON_SX } from "themes/colorConst";
+import SafeHtml from 'components/SafeHtml';
// ==============================|| DASHBOARD - DEFAULT ||============================== //
@@ -409,13 +410,12 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
ml:2
}}
>
-
)}
@@ -459,7 +459,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
-
+
>
);
@@ -603,14 +603,14 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
-
+
-
-
-
-
+
+
+
+
@@ -630,7 +630,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
}}
/>
-
+
@@ -653,7 +653,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) =>
-
+
diff --git a/src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js b/src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js
index e8b50064..bb9404f2 100644
--- a/src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js
+++ b/src/pages/PublicNotice/Details_Public/ApplicationDetailCard.js
@@ -41,6 +41,7 @@ import EditNoteIcon from '@mui/icons-material/EditNote';
import DownloadIcon from '@mui/icons-material/Download';
import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst";
import { PRIMARY_CONTAINED_BUTTON_SX } from "themes/colorConst";
+import SafeHtml from 'components/SafeHtml';
import { ThemeProvider } from "@emotion/react";
import { FormattedMessage, useIntl } from "react-intl";
// ==============================|| DASHBOARD - DEFAULT ||============================== //
@@ -834,7 +835,7 @@ const ApplicationDetailCard = (
-
+
diff --git a/src/pages/PublicNotice/ListPanel/PendingPaymentTab.js b/src/pages/PublicNotice/ListPanel/PendingPaymentTab.js
index af6677f6..859ad5c4 100644
--- a/src/pages/PublicNotice/ListPanel/PendingPaymentTab.js
+++ b/src/pages/PublicNotice/ListPanel/PendingPaymentTab.js
@@ -28,6 +28,7 @@ import {
// import { dateStr } from "utils/DateUtils";
import { ThemeProvider, useTheme } from "@emotion/react";
import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst";
+import SafeHtml from 'components/SafeHtml';
import { FormattedMessage, useIntl } from "react-intl";
import { PRIMARY_TEXT_BUTTON_SX, PRIMARY_CONTAINED_BUTTON_SX } from "themes/colorConst";
// ==============================|| EVENT TABLE ||============================== //
@@ -580,7 +581,7 @@ export default function SubmittedTab({ setCount, url }) {
-
+
diff --git a/src/pages/Setting/SystemSetting/Table.js b/src/pages/Setting/SystemSetting/Table.js
index 8d4f966e..6b43df4b 100644
--- a/src/pages/Setting/SystemSetting/Table.js
+++ b/src/pages/Setting/SystemSetting/Table.js
@@ -7,6 +7,7 @@ import MainCard from "components/MainCard";
import * as React from "react";
import { FiDataGrid } from "components/FiDataGrid";
import { GET_SYS_PARAMS } from "utils/ApiPathConst";
+import SafeHtml from 'components/SafeHtml';
// ==============================|| DASHBOARD - DEFAULT ||============================== //
@@ -45,7 +46,7 @@ const Table = ({onRowClick, searchCriteria, refreshTrigger}) => {
flex: 1,
minWidth: 400,
renderCell:(params)=>{
- return
+ return
}
},
];
diff --git a/src/pages/authentication/AuthWrapper.js b/src/pages/authentication/AuthWrapper.js
index 41dba56a..9d282b94 100644
--- a/src/pages/authentication/AuthWrapper.js
+++ b/src/pages/authentication/AuthWrapper.js
@@ -10,6 +10,7 @@ import backbroundImg from 'assets/images/bg_ml.jpg';
import lgceImg from 'assets/images/2025_lgce.jpg';
import 'assets/style/loginStyles.css';
import { SysContext } from 'components/SysSettingProvider';
+import SafeHtml from 'components/SafeHtml';
const AuthCard = Loadable(lazy(() => import('./AuthCardCustom')));
const BackgroundHead = {
@@ -175,11 +176,9 @@ const AuthWrapper = ({ children }) => {
'& p': { marginBottom: '12px', lineHeight: 1.7 }
}}
>
-
+
+
+
@@ -207,24 +206,18 @@ const AuthWrapper = ({ children }) => {
>
{checkPaymentSuspension() ? (
-
+
+
+
) : (
-
+
+ })}
+ />
+
)}
diff --git a/src/pages/authentication/RegisterCustom.js b/src/pages/authentication/RegisterCustom.js
index 1e5702fa..14e14885 100644
--- a/src/pages/authentication/RegisterCustom.js
+++ b/src/pages/authentication/RegisterCustom.js
@@ -17,6 +17,7 @@ import { I_AM_SMART_PATH } from "utils/ApiPathConst";
import * as React from 'react';
import { FormattedMessage, useIntl } from "react-intl";
import usePageTitle from "components/usePageTitle";
+import SafeHtml from 'components/SafeHtml';
// ================================|| LOGIN ||================================ //
@@ -127,7 +128,7 @@ const RegisterCustom = () => {
/>
-
+
@@ -160,7 +161,7 @@ const RegisterCustom = () => {
-
+
@@ -189,7 +190,7 @@ const RegisterCustom = () => {
-
+
diff --git a/src/pages/authentication/auth-forms/BusCustomFormWizard.js b/src/pages/authentication/auth-forms/BusCustomFormWizard.js
index 77e8371c..458751ac 100644
--- a/src/pages/authentication/auth-forms/BusCustomFormWizard.js
+++ b/src/pages/authentication/auth-forms/BusCustomFormWizard.js
@@ -35,6 +35,7 @@ import { POST_PUBLIC_USER_REGISTER, POST_CAPTCHA, POST_USERNAME, POST_USER_EMAIL
import * as ComboData from "utils/ComboData";
import Loadable from 'components/Loadable';
+import SafeHtml from 'components/SafeHtml';
import { lazy } from 'react';
const UploadFileTable = Loadable(lazy(() => import('./UploadFileTable')));
const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
@@ -1618,7 +1619,7 @@ const BusCustomFormWizard = (props) => {
-
+
diff --git a/src/pages/authentication/auth-forms/CustomFormWizard.js b/src/pages/authentication/auth-forms/CustomFormWizard.js
index 5d522f84..931a5224 100644
--- a/src/pages/authentication/auth-forms/CustomFormWizard.js
+++ b/src/pages/authentication/auth-forms/CustomFormWizard.js
@@ -28,6 +28,7 @@ import { POST_USERNAME, POST_USER_EMAIL, POST_CAPTCHA, POST_PUBLIC_USER_REGISTER
import * as ComboData from "utils/ComboData";
import Loadable from 'components/Loadable';
+import SafeHtml from 'components/SafeHtml';
import { lazy } from 'react';
const UploadFileTable = Loadable(lazy(() => import('./UploadFileTable')));
const PreviewUploadFileTable = Loadable(lazy(() => import('./PreviewUploadFileTable')));
@@ -1946,7 +1947,7 @@ const CustomFormWizard = (props) => {
-
+
diff --git a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
index a84714f6..fc3919fa 100644
--- a/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
+++ b/src/pages/authentication/auth-forms/IAmSmartFormWizard.js
@@ -26,6 +26,7 @@ import { POST_IAMSMART_USER_REGISTER, POST_CAPTCHA, POST_USER_EMAIL, POST_CAPTCH
import * as ComboData from "utils/ComboData";
import Loadable from 'components/Loadable';
+import SafeHtml from 'components/SafeHtml';
import { lazy } from 'react';
const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
@@ -1055,7 +1056,7 @@ const CustomFormWizard = (props) => {
-
+
diff --git a/src/pages/dashboard/Public/index.js b/src/pages/dashboard/Public/index.js
index fad79e47..e5450461 100644
--- a/src/pages/dashboard/Public/index.js
+++ b/src/pages/dashboard/Public/index.js
@@ -29,6 +29,7 @@ const LoadingComponent = Loadable(React.lazy(() => import('../../extra-pages/Loa
import { useNavigate } from "react-router-dom";
import usePageTitle from "components/usePageTitle";
+import SafeHtml from 'components/SafeHtml';
// ==============================|| DASHBOARD - DEFAULT ||============================== //
@@ -297,7 +298,7 @@ const DashboardDefault = () => {
-
+
diff --git a/src/pages/extra-pages/AboutUs/AboutUs_cn.js b/src/pages/extra-pages/AboutUs/AboutUs_cn.js
index de5d2fd1..7f16b740 100644
--- a/src/pages/extra-pages/AboutUs/AboutUs_cn.js
+++ b/src/pages/extra-pages/AboutUs/AboutUs_cn.js
@@ -1,3 +1,4 @@
+import SafeHtml from 'components/SafeHtml';
const AboutUs = () => {
@@ -155,7 +156,7 @@ const AboutUs = () => {
`
;
- return ();
+ return ;
}
diff --git a/src/pages/extra-pages/AboutUs/AboutUs_en.js b/src/pages/extra-pages/AboutUs/AboutUs_en.js
index b026ad8a..c6a56e45 100644
--- a/src/pages/extra-pages/AboutUs/AboutUs_en.js
+++ b/src/pages/extra-pages/AboutUs/AboutUs_en.js
@@ -1,3 +1,4 @@
+import SafeHtml from 'components/SafeHtml';
const AboutUs = () => {
@@ -162,7 +163,7 @@ const AboutUs = () => {
`
;
- return ();
+ return ;
}
diff --git a/src/pages/extra-pages/AboutUs/AboutUs_zh.js b/src/pages/extra-pages/AboutUs/AboutUs_zh.js
index c7600cab..8e259c87 100644
--- a/src/pages/extra-pages/AboutUs/AboutUs_zh.js
+++ b/src/pages/extra-pages/AboutUs/AboutUs_zh.js
@@ -1,3 +1,4 @@
+import SafeHtml from 'components/SafeHtml';
const AboutUs = () => {
@@ -165,7 +166,7 @@ const AboutUs = () => {
`
;
- return ();
+ return ;
}
diff --git a/src/pages/extra-pages/ImportantNoticePage/index.js b/src/pages/extra-pages/ImportantNoticePage/index.js
index 65ee7286..2936417f 100644
--- a/src/pages/extra-pages/ImportantNoticePage/index.js
+++ b/src/pages/extra-pages/ImportantNoticePage/index.js
@@ -3,6 +3,7 @@ import { useIntl, FormattedMessage } from 'react-intl';
import usePageTitle from "components/usePageTitle";
import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png';
+import SafeHtml from 'components/SafeHtml';
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -38,7 +39,7 @@ const ImportantNotice = () => {
-
+
);
diff --git a/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_cn.js b/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_cn.js
index 6ea9c981..999a7ab0 100644
--- a/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_cn.js
+++ b/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_cn.js
@@ -1,3 +1,4 @@
+import SafeHtml from 'components/SafeHtml';
const Page = () => {
@@ -83,7 +84,7 @@ const Page = () => {
`;
- return ();
+ return ;
}
diff --git a/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_en.js b/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_en.js
index d5b94997..9dce9342 100644
--- a/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_en.js
+++ b/src/pages/extra-pages/PrivacyPolicyPage/PrivacyPolicy_en.js
@@ -1,3 +1,5 @@
+import SafeHtml from 'components/SafeHtml';
+
const Page = () => {
const content = `