| @@ -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", | |||
| @@ -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", | |||
| @@ -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 <Component {...rest} dangerouslySetInnerHTML={{ __html: sanitizeHtml(html) }} />; | |||
| }; | |||
| SafeHtml.propTypes = { | |||
| html: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | |||
| component: PropTypes.elementType | |||
| }; | |||
| export default SafeHtml; | |||
| @@ -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 = () => { | |||
| </Typography> | |||
| <Typography sx={{p:1}} align="justify">{DateUtils.datetimeStr(record?.sentDate)}</Typography> | |||
| <Typography component="div" variant="body1" sx={{ p: 4, textAlign: "left", bgcolor: "#f8f8f8" }} align="justify"> | |||
| <div dangerouslySetInnerHTML={{ __html: record?.content }} /> | |||
| <SafeHtml html={record?.content} /> | |||
| </Typography> | |||
| <Typography component="h3" variant="h4" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | |||
| @@ -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) => { | |||
| <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography> | |||
| <DialogContent style={{ display: 'flex', }}> | |||
| <Stack direction="column" justifyContent="space-between"> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} /> | |||
| </Stack> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -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 }) => { | |||
| </Typography> | |||
| <Typography variant="h4" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.payMsg1' }, { appNo: record.appNo }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.payMsg1' }, { appNo: record.appNo })} /> | |||
| <br /> | |||
| <FormattedMessage id="MSG.payMsg2_1" /> | |||
| <span style={{ color: "red" }}> | |||
| @@ -116,14 +117,12 @@ const Index = ({ record }) => { | |||
| <FormattedMessage id="MSG.payMsg2_2" /> | |||
| <br /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage({ id: 'MSG.payMsg3' }, | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.payMsg3' }, | |||
| { | |||
| issueYear: record?.issueYear, | |||
| issueVolume: record?.issueVolume, | |||
| issueNo: record?.issueNo, | |||
| }) | |||
| }} /> | |||
| })} /> | |||
| </Typography> | |||
| <Typography variant="h4" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | |||
| @@ -213,7 +212,7 @@ const Index = ({ record }) => { | |||
| <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography> | |||
| <DialogContent style={{ display: 'flex', }}> | |||
| <Stack direction="column" justifyContent="space-between"> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} /> | |||
| </Stack> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -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(); | |||
| )} | |||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_demandNote' | |||
| }, | |||
| { | |||
| appNo: record?.appNo, | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_demandNote2' | |||
| }, | |||
| @@ -96,27 +94,22 @@ const isAfter = checkIsOnlyOnlinePayment(); | |||
| closingDateOff: DateUtils.dateFormat(record?.closingDateOff, intl.formatMessage({id: "dateStrFormat"})), | |||
| email: record?.mail, | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_demandNote3' | |||
| }, | |||
| { | |||
| paymentDeadline: DateUtils.dateFormat(record?.closingDate, intl.formatMessage({id: "dateStrFormat"})), | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_demandNote4' | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| </Typography> | |||
| @@ -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(); | |||
| <Typography variant="h5" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "left" }}> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office' | |||
| }, | |||
| { | |||
| appNo: record?.appNo, | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office2' | |||
| }, | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office3' | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office4' | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office5' | |||
| }, | |||
| { | |||
| paymentDeadline: DateUtils.dateFormat(record?.closingDate, intl.formatMessage({id: "dateStrFormat"})), | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_office6' | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| </Typography> | |||
| @@ -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 = () => { | |||
| )} | |||
| <Typography variant="h5" sx={bodySx}> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_online' | |||
| }, | |||
| { | |||
| appNo: record?.appNo, | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_online2' | |||
| }, | |||
| { | |||
| paymentDeadline: DateUtils.formatDateForLocale(record?.expiryDate, intl, locale), | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| {checkPaymentSuspension()? | |||
| <div> | |||
| <Typography style={{ textAlign: "flex-start", color: "red" }}> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "suspensionMessageText" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "suspensionMessageText" })} /> | |||
| </Typography> | |||
| <br /> | |||
| </div>:null | |||
| @@ -272,13 +269,11 @@ const Index = () => { | |||
| } | |||
| <Typography variant="h4" sx={bodySx}> | |||
| <div dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage( | |||
| <SafeHtml html={intl.formatMessage( | |||
| { | |||
| id: 'proofPaymentBody_online3' | |||
| } | |||
| ) | |||
| }} /> | |||
| )} /> | |||
| <br /> | |||
| </Typography> | |||
| @@ -338,7 +333,7 @@ const Index = () => { | |||
| <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography> | |||
| <DialogContent style={{ display: 'flex', }}> | |||
| <Stack direction="column" justifyContent="space-between"> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} /> | |||
| </Stack> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -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 }) => { | |||
| <li><FormattedMessage id="post" /></li> | |||
| </ul> | |||
| <Typography variant="h6" component="span"> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofNote" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "proofNote" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </> | |||
| ); | |||
| @@ -468,7 +469,7 @@ const FormPanel = ({ formData }) => { | |||
| <FormattedMessage id="payOnline" /> | |||
| {checkPaymentSuspension()? | |||
| <Typography style={{ padding: '16px', color: "red" }}> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "suspensionMessageText" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "suspensionMessageText" })} /> | |||
| </Typography>:null | |||
| } | |||
| <br /><a href="#payOnlineDetails" color='#fff' onClick={() => { | |||
| @@ -536,7 +537,7 @@ const FormPanel = ({ formData }) => { | |||
| <li><FormattedMessage id="post" /></li> | |||
| </ul> | |||
| <Typography variant="h6" component="span"> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofNote" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "proofNote" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </> | |||
| ); | |||
| @@ -599,13 +600,13 @@ const FormPanel = ({ formData }) => { | |||
| } | |||
| {/* <Grid item xs={12}> | |||
| <Typography variant="h6" component="span" height="100%" > | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofNote" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "proofNote" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </Grid> */} | |||
| {!isOnlyOnlinePayment? | |||
| <Grid item xs={12}> | |||
| <Typography variant="h6" component="span" height="100%" > | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofImportant" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "proofImportant" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </Grid>:null | |||
| } | |||
| @@ -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 | |||
| }} | |||
| > | |||
| <span | |||
| dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage({ | |||
| id: "suspensionMessageText", | |||
| defaultMessage: "" | |||
| }) | |||
| }} | |||
| <SafeHtml | |||
| component="span" | |||
| html={intl.formatMessage({ | |||
| id: "suspensionMessageText", | |||
| defaultMessage: "" | |||
| })} | |||
| /> | |||
| </Typography> | |||
| )} | |||
| @@ -459,7 +459,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| <li><FormattedMessage id="post" /></li> | |||
| </ul> | |||
| <Typography variant="h6" component="span"> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofNote" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "proofNote" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </> | |||
| ); | |||
| @@ -603,14 +603,14 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| <Grid item xs={12} mr={1} mb={2}> | |||
| <Typography variant="pnspsFormParagraphBold"> | |||
| <span style={{ textAlign: 'justify', }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr0" }) }} /> | |||
| <SafeHtml component="span" style={{ textAlign: 'justify', }} html={intl.formatMessage({ id: "applyTickUnderStr0" })} /> | |||
| </Typography> | |||
| <Typography display="inline" variant="subtitle1" component="span" sx={{ color: 'primary.primary' }} > | |||
| <ol style={{ textAlign: 'justify', }}> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr1" }) }} /> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr2" }) }} /> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickUnderStr3" }) }} /> | |||
| <li dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "tradeMarkFootnote" }) }} /> | |||
| <SafeHtml component="li" html={intl.formatMessage({ id: "applyTickUnderStr1" })} /> | |||
| <SafeHtml component="li" html={intl.formatMessage({ id: "applyTickUnderStr2" })} /> | |||
| <SafeHtml component="li" html={intl.formatMessage({ id: "applyTickUnderStr3" })} /> | |||
| <SafeHtml component="li" html={intl.formatMessage({ id: "tradeMarkFootnote" })} /> | |||
| </ol> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -630,7 +630,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| }} | |||
| /> | |||
| <Typography variant="h6" component="span" height="100%" > | |||
| <div style={{ padding: 12, textAlign: 'justify' }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickStr" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "applyTickStr" })} style={{ padding: 12, textAlign: 'justify' }} /> | |||
| </Typography> | |||
| </Stack> | |||
| </Grid> | |||
| @@ -653,7 +653,7 @@ const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => | |||
| <Grid item xs={12}> | |||
| <Typography variant="h6" component="span" height="100%" > | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyPublicNoticeText" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "applyPublicNoticeText" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </Grid> | |||
| @@ -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 = ( | |||
| <Typography variant="h4" component="span" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography> | |||
| <DialogContent style={{ display: 'flex', }}> | |||
| <Stack direction="column" justifyContent="space-between"> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} /> | |||
| </Stack> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -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 }) { | |||
| <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography> | |||
| <DialogContent style={{ display: 'flex', }}> | |||
| <Stack direction="column" justifyContent="space-between"> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText })} /> | |||
| </Stack> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -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 <div dangerouslySetInnerHTML={{__html: params.value}} /> | |||
| return <SafeHtml html={params.value} /> | |||
| } | |||
| }, | |||
| ]; | |||
| @@ -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 } | |||
| }} | |||
| > | |||
| <Typography | |||
| component="div" | |||
| sx={{ fontSize: 18, lineHeight: 1.8 }} | |||
| dangerouslySetInnerHTML={{ __html: popupHtml }} | |||
| /> | |||
| <Typography component="div" sx={{ fontSize: 18, lineHeight: 1.8 }}> | |||
| <SafeHtml html={popupHtml} /> | |||
| </Typography> | |||
| </DialogContent> | |||
| </Dialog> | |||
| @@ -207,24 +206,18 @@ const AuthWrapper = ({ children }) => { | |||
| > | |||
| <Grid item xs={12} sx={{ px: { xs: 2 }, pl: { md: 4 }, pt: { xs: 4, sm: 2, md: 1.5 }, pb: { xs: 4, sm: 0 } }}> | |||
| {checkPaymentSuspension() ? ( | |||
| <Typography | |||
| component="div" | |||
| sx={{ color: 'error.main', textAlign: 'start', p: 1.5 }} | |||
| dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage({ id: 'suspensionMessageText', defaultMessage: '' }) | |||
| }} | |||
| /> | |||
| <Typography component="div" sx={{ color: 'error.main', textAlign: 'start', p: 1.5 }}> | |||
| <SafeHtml html={intl.formatMessage({ id: 'suspensionMessageText', defaultMessage: '' })} /> | |||
| </Typography> | |||
| ) : ( | |||
| <Typography | |||
| component="div" | |||
| sx={{ textAlign: 'start', p: 1.5 }} | |||
| dangerouslySetInnerHTML={{ | |||
| __html: intl.formatMessage({ | |||
| <Typography component="div" sx={{ textAlign: 'start', p: 1.5 }}> | |||
| <SafeHtml | |||
| html={intl.formatMessage({ | |||
| id: isOnlyOnline ? 'landingMessage' : 'homePageHeaderMessage', | |||
| defaultMessage: '' | |||
| }) | |||
| }} | |||
| /> | |||
| })} | |||
| /> | |||
| </Typography> | |||
| )} | |||
| </Grid> | |||
| @@ -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 = () => { | |||
| /> | |||
| <Box mt={4} ml={2} mr={2} bgcolor="grey.100" p={1.5} > | |||
| <Typography textAlign='justify' variant="body1" display="block" gutterBottom> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.registerIAmSmart' }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.registerIAmSmart' })} /> | |||
| </Typography> | |||
| <Link href={intl.formatMessage({ id: "iamsmartLink" })}> | |||
| <FormattedMessage id="learnMore" /> | |||
| @@ -160,7 +161,7 @@ const RegisterCustom = () => { | |||
| </Button> | |||
| <Typography ml={4} mr={4} mt={4} variant="body1" display="block" sx={{ fontWeight: 'bold' }} gutterBottom> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.registerPersonal' }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.registerPersonal' })} /> | |||
| </Typography> | |||
| </Grid> | |||
| <Grid item xs={12} md={6} sx={{ borderLeft: 1, borderColor: 'grey.500' }}> | |||
| @@ -189,7 +190,7 @@ const RegisterCustom = () => { | |||
| </Typography> | |||
| </Button> | |||
| <Typography ml={4} mr={4} mt={4} variant="body1" display="block" sx={{ fontWeight: 'bold' }} gutterBottom> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.registerOrg' }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'MSG.registerOrg' })} /> | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| @@ -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) => { | |||
| <Grid container> | |||
| <Grid item xs={12} md={12}> | |||
| <Typography component="span" variant="h6" height="100%" sx={{ textAlign: "left", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e", display: "block" }}> | |||
| <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | |||
| <SafeHtml html={intl.formatMessage({id: "termsAndCon"})} style={{padding: 12}} /> | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| @@ -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) => { | |||
| <Grid container> | |||
| <Grid item xs={12} md={12}> | |||
| <Typography height="100%" component="span" sx={{ textAlign: "left", display: "block", /*overflow: "scroll",*/ borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||
| <div style={{padding: 12}} dangerouslySetInnerHTML={{__html: intl.formatMessage({id: "termsAndCon"})}} /> | |||
| <SafeHtml html={intl.formatMessage({id: "termsAndCon"})} style={{padding: 12}} /> | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| @@ -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) => { | |||
| <Grid item xs={12} md={12}> | |||
| <Typography component="span" sx={{ textAlign: "left", display: "block", borderRadius: "inherit", borderStyle: "solid", borderWidth: "1px", borderColor: "#0c489e" }}> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "termsAndCon" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "termsAndCon" })} style={{ padding: 12 }} /> | |||
| </Typography> | |||
| </Grid> | |||
| </Grid> | |||
| @@ -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 = () => { | |||
| </DialogTitle> | |||
| <DialogContent style={{ color: '#B00020', display: 'flex', }}> | |||
| <Typography variant="h5" style={{ padding: '16px' }}> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "suspensionMessageText" }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: "suspensionMessageText" })} /> | |||
| </Typography> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| @@ -1,3 +1,4 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const AboutUs = () => { | |||
| @@ -155,7 +156,7 @@ const AboutUs = () => { | |||
| ` | |||
| ; | |||
| return (<div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />); | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const AboutUs = () => { | |||
| @@ -162,7 +163,7 @@ const AboutUs = () => { | |||
| </div>` | |||
| ; | |||
| return (<div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />); | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const AboutUs = () => { | |||
| @@ -165,7 +166,7 @@ const AboutUs = () => { | |||
| ` | |||
| ; | |||
| return (<div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />); | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| } | |||
| @@ -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 = () => { | |||
| </div> | |||
| </Grid> | |||
| <Grid item xs={10} md={8} lg={6}> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: htmlContent }} /> | |||
| <SafeHtml html={htmlContent} style={{ padding: 12 }} /> | |||
| </Grid> | |||
| </Grid> | |||
| ); | |||
| @@ -1,3 +1,4 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const Page = () => { | |||
| @@ -83,7 +84,7 @@ const Page = () => { | |||
| </div> | |||
| `; | |||
| return (<div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />); | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| } | |||
| @@ -1,3 +1,5 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const Page = () => { | |||
| const content = ` | |||
| <style> | |||
| @@ -125,7 +127,7 @@ const Page = () => { | |||
| </div> | |||
| `; | |||
| return <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />; | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| }; | |||
| export default Page; | |||
| @@ -1,3 +1,5 @@ | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| const Page = () => { | |||
| const content = ` | |||
| <style> | |||
| @@ -98,7 +100,7 @@ const Page = () => { | |||
| </div> | |||
| `; | |||
| return <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: content }} />; | |||
| return <SafeHtml html={content} style={{ padding: 12 }} />; | |||
| }; | |||
| export default Page; | |||
| @@ -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 PrivacyPolicy = () => { | |||
| </div> | |||
| </Grid> | |||
| <Grid item xs={10} md={8} lg={6}> | |||
| <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: htmlContent }} /> | |||
| <SafeHtml html={htmlContent} style={{ padding: 12 }} /> | |||
| </Grid> | |||
| </Grid> | |||
| ); | |||
| @@ -12,6 +12,7 @@ import { useNavigate } from "react-router-dom"; | |||
| import Loadable from 'components/Loadable'; | |||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | |||
| import AccountCircleIcon from '@mui/icons-material/AccountCircle'; | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | |||
| const BackgroundHead = { | |||
| @@ -66,7 +67,7 @@ const Index = () => { | |||
| <AccountCircleIcon color="secondary" sx={{ width: "200px", height: "200px" }} /> | |||
| <Grid item xs={12} md={12} > | |||
| <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}> | |||
| <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'pleaseLloginMessage2' }) }} /> | |||
| <SafeHtml html={intl.formatMessage({ id: 'pleaseLloginMessage2' })} /> | |||
| </Typography> | |||
| </Grid> | |||
| </center> | |||
| @@ -12,6 +12,7 @@ import { useNavigate } from "react-router-dom"; | |||
| import Loadable from 'components/Loadable'; | |||
| const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); | |||
| import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; | |||
| import SafeHtml from 'components/SafeHtml'; | |||
| import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' | |||
| const BackgroundHead = { | |||
| @@ -69,8 +70,9 @@ const Index = () => { | |||
| component="div" | |||
| variant="h3" | |||
| sx={{ mt: 4, maxWidth: 900, textAlign: 'center' }} | |||
| dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'loginSuccessMessage2' }) }} | |||
| /> | |||
| > | |||
| <SafeHtml html={intl.formatMessage({ id: 'loginSuccessMessage2' })} /> | |||
| </Typography> | |||
| ) : ( | |||
| <Typography component="div" variant="h3" sx={{ mt: 4, maxWidth: 900, textAlign: 'center' }}> | |||
| <FormattedMessage id="iAmSmartLoginSuccessHeading" /> | |||
| @@ -0,0 +1,78 @@ | |||
| import DOMPurify from 'dompurify'; | |||
| const ALLOWED_TAGS = [ | |||
| 'a', | |||
| 'b', | |||
| 'blockquote', | |||
| 'br', | |||
| 'caption', | |||
| 'col', | |||
| 'colgroup', | |||
| 'div', | |||
| 'em', | |||
| 'h1', | |||
| 'h2', | |||
| 'h3', | |||
| 'h4', | |||
| 'h5', | |||
| 'h6', | |||
| 'hr', | |||
| 'i', | |||
| 'img', | |||
| 'li', | |||
| 'ol', | |||
| 'p', | |||
| 'span', | |||
| 'strong', | |||
| 'style', | |||
| 'sub', | |||
| 'sup', | |||
| 'table', | |||
| 'tbody', | |||
| 'td', | |||
| 'tfoot', | |||
| 'th', | |||
| 'thead', | |||
| 'tr', | |||
| 'u', | |||
| 'ul' | |||
| ]; | |||
| const ALLOWED_ATTR = [ | |||
| 'href', | |||
| 'title', | |||
| 'target', | |||
| 'rel', | |||
| 'src', | |||
| 'alt', | |||
| 'width', | |||
| 'height', | |||
| 'colspan', | |||
| 'rowspan', | |||
| 'scope', | |||
| 'class', | |||
| 'style' | |||
| ]; | |||
| if (typeof window !== 'undefined') { | |||
| DOMPurify.addHook('afterSanitizeAttributes', (node) => { | |||
| if (node.tagName === 'A' && node.getAttribute('target') === '_blank') { | |||
| node.setAttribute('rel', 'noopener noreferrer'); | |||
| } | |||
| }); | |||
| } | |||
| /** | |||
| * Strict allow-list sanitize for API / i18n HTML before innerHTML. | |||
| * Scripts, event handlers, iframes, and javascript: URLs are dropped. | |||
| */ | |||
| export function sanitizeHtml(dirty) { | |||
| if (dirty == null) { | |||
| return ''; | |||
| } | |||
| return DOMPurify.sanitize(String(dirty), { | |||
| ALLOWED_TAGS, | |||
| ALLOWED_ATTR, | |||
| ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i | |||
| }); | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| import { sanitizeHtml } from './sanitizeHtml'; | |||
| describe('sanitizeHtml', () => { | |||
| test('strips script tags and event handlers', () => { | |||
| const clean = sanitizeHtml('<p onclick="alert(1)">Hi<script>alert(1)</script></p>'); | |||
| expect(clean).toContain('Hi'); | |||
| expect(clean.toLowerCase()).not.toContain('script'); | |||
| expect(clean.toLowerCase()).not.toContain('onclick'); | |||
| expect(clean).not.toContain('alert(1)'); | |||
| }); | |||
| test('strips javascript URLs and iframes', () => { | |||
| const clean = sanitizeHtml( | |||
| '<a href="javascript:alert(1)">x</a><iframe src="https://evil.example"></iframe>' | |||
| ); | |||
| expect(clean.toLowerCase()).not.toContain('javascript'); | |||
| expect(clean.toLowerCase()).not.toContain('iframe'); | |||
| expect(clean).not.toContain('evil.example'); | |||
| }); | |||
| test('keeps safe formatting, https links, and tables', () => { | |||
| const clean = sanitizeHtml( | |||
| '<p>Hello <strong>world</strong></p><a href="https://pnsps.gld.gov.hk/">PNSPS</a><table><tr><td>cell</td></tr></table>' | |||
| ); | |||
| expect(clean).toContain('<p>'); | |||
| expect(clean).toMatch(/<(strong|b)>world<\/(strong|b)>/); | |||
| expect(clean).toContain('https://pnsps.gld.gov.hk/'); | |||
| expect(clean).toContain('cell'); | |||
| }); | |||
| test('keeps mailto links', () => { | |||
| const clean = sanitizeHtml('<a href="mailto:pd_public_notices@gld.gov.hk">mail</a>'); | |||
| expect(clean).toContain('mailto:pd_public_notices@gld.gov.hk'); | |||
| }); | |||
| test('adds rel on target=_blank links', () => { | |||
| const clean = sanitizeHtml('<a href="https://egazette.gld.gov.hk/en" target="_blank">Gazette</a>'); | |||
| expect(clean).toContain('target="_blank"'); | |||
| expect(clean).toContain('noopener'); | |||
| }); | |||
| test('null and empty become empty string', () => { | |||
| expect(sanitizeHtml(null)).toBe(''); | |||
| expect(sanitizeHtml(undefined)).toBe(''); | |||
| expect(sanitizeHtml('')).toBe(''); | |||
| }); | |||
| test('img onerror is dropped', () => { | |||
| const clean = sanitizeHtml('<img src="https://example.com/a.png" onerror="alert(1)">'); | |||
| expect(clean.toLowerCase()).not.toContain('onerror'); | |||
| expect(clean).not.toContain('alert(1)'); | |||
| }); | |||
| }); | |||
| @@ -2748,7 +2748,7 @@ | |||
| dependencies: | |||
| "@types/jest" "*" | |||
| "@types/trusted-types@^2.0.2": | |||
| "@types/trusted-types@^2.0.2", "@types/trusted-types@^2.0.7": | |||
| version "2.0.7" | |||
| resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" | |||
| integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== | |||
| @@ -4680,6 +4680,13 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: | |||
| dependencies: | |||
| domelementtype "^2.2.0" | |||
| dompurify@^3.4.15: | |||
| version "3.4.15" | |||
| resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.4.15.tgz" | |||
| integrity sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw== | |||
| optionalDependencies: | |||
| "@types/trusted-types" "^2.0.7" | |||
| domutils@^1.7.0: | |||
| version "1.7.0" | |||
| resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" | |||
| @@ -5718,6 +5725,11 @@ fs.realpath@^1.0.0: | |||
| resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" | |||
| integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== | |||
| fsevents@^2.3.2, fsevents@~2.3.2: | |||
| version "2.3.3" | |||
| resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" | |||
| integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== | |||
| function-bind@^1.1.2: | |||
| version "1.1.2" | |||
| resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" | |||