Skip to content

Commit e3ece0f

Browse files
committed
fix: restore sanitizeHTML for XSS protection and fix Tube2tunes loading state
1 parent 30ed193 commit e3ece0f

9 files changed

Lines changed: 31 additions & 10 deletions

File tree

‎.husky/pre-commit‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/usr/bin/env sh
22
. "$(dirname -- "$0")/_/husky.sh"
33

4-
yarn pre-commit
4+
npx lint-staged

‎src/common/Testimonial/TestimonialCard.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
22
import { format } from 'date-fns';
33
import * as allLocales from 'date-fns/locale';
44
import { email2Slug } from 'common/services/string';
5+
import sanitizeHTML from 'common/utils/sanitizeHTML';
56

67
const TestimonialCard = ({ home, quote, name, avatarUrl, category, created_at, email }) => {
78
const [formattedDate] = useState(() => {
@@ -59,7 +60,7 @@ const TestimonialCard = ({ home, quote, name, avatarUrl, category, created_at, e
5960
>
6061
<p
6162
className="leading-relaxed text-gray-700"
62-
dangerouslySetInnerHTML={{ __html: replaceWithBr() }}
63+
dangerouslySetInnerHTML={{ __html: sanitizeHTML(replaceWithBr()) }}
6364
/>
6465
</blockquote>
6566
</div>

‎src/common/badges-dashboard/BadgeDetails.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Badge from './Badge';
22
import './badge.css';
3+
import sanitizeHTML from 'common/utils/sanitizeHTML';
34

45
const BadgeDetails = ({ badge, onClose }) => {
56
const makeClickableLinks = (badge) => {
@@ -9,7 +10,7 @@ const BadgeDetails = ({ badge, onClose }) => {
910
return `<a href="${url}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:underline">${name}</a>`;
1011
});
1112

12-
return <span dangerouslySetInnerHTML={{ __html: descriptionWithLinks }} />;
13+
return <span dangerouslySetInnerHTML={{ __html: sanitizeHTML(descriptionWithLinks) }} />;
1314
};
1415

1516
return (

‎src/common/utils/sanitizeHTML.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import DOMPurify from 'dompurify';
2+
3+
/**
4+
* Sanitizes an HTML string using DOMPurify to prevent XSS attacks.
5+
* Use this utility whenever you need to render dynamic HTML via dangerouslySetInnerHTML.
6+
*
7+
* @param {string} html - The raw HTML string to sanitize.
8+
* @returns {string} - A sanitized HTML string safe to use with dangerouslySetInnerHTML.
9+
*/
10+
const sanitizeHTML = (html) => DOMPurify.sanitize(html ?? '');
11+
12+
export default sanitizeHTML;

‎src/plays/devblog/Pages/Article.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import axios from 'axios';
22
import { useState, useEffect } from 'react';
33
import { useParams } from 'react-router-dom';
44
import Loading from '../components/Loading';
5+
import sanitizeHTML from 'common/utils/sanitizeHTML';
56

67
const Article = () => {
78
const [article, setArticle] = useState({});
@@ -50,7 +51,7 @@ const Article = () => {
5051

5152
<div
5253
className="mt-10 devBlog-article"
53-
dangerouslySetInnerHTML={{ __html: article.body_html }}
54+
dangerouslySetInnerHTML={{ __html: sanitizeHTML(article.body_html) }}
5455
/>
5556
</div>
5657
) : (

‎src/plays/fun-quiz/EndScreen.jsx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// vendors
22
import { Fragment, useState } from 'react';
33

4+
import sanitizeHTML from 'common/utils/sanitizeHTML';
5+
46
// css
57
import './FrontScreen.scss';
68

@@ -16,17 +18,17 @@ const EndScreen = ({ quizSummary, redirectHome }) => {
1618
<div className="question-number">Question: {currentQuestion?.qNo}</div>
1719
<li
1820
dangerouslySetInnerHTML={{
19-
__html: `${currentQuestion?.question}`
21+
__html: sanitizeHTML(`${currentQuestion?.question}`)
2022
}}
2123
/>
2224
<span
2325
dangerouslySetInnerHTML={{
24-
__html: `<br/><b>Ans</b>: ${currentQuestion?.correct_answer}<br/>`
26+
__html: sanitizeHTML(`<br/><b>Ans</b>: ${currentQuestion?.correct_answer}<br/>`)
2527
}}
2628
/>
2729
<span
2830
dangerouslySetInnerHTML={{
29-
__html: `<b>Your Answer</b>: ${currentQuestion?.your_answer}`
31+
__html: sanitizeHTML(`<b>Your Answer</b>: ${currentQuestion?.your_answer}`)
3032
}}
3133
/>
3234
</div>

‎src/plays/fun-quiz/QuizScreen.jsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect, useState, useCallback, useRef } from 'react';
22

3+
import sanitizeHTML from 'common/utils/sanitizeHTML';
34
import './QuizScreen.scss';
45

56
// assets
@@ -149,15 +150,15 @@ function QuizScreen({ category, getQuizSummary }) {
149150
<div className={`timer ${timer <= 5 && 'caution'}`}>{timer}</div>
150151
<div className="question-info">Question: {questionNumber + 1}</div>
151152
<div className="question">
152-
<h1 dangerouslySetInnerHTML={{ __html: currentQuestion?.question }} />
153+
<h1 dangerouslySetInnerHTML={{ __html: sanitizeHTML(currentQuestion?.question) }} />
153154
</div>
154155
<div className="options">
155156
{currentQuestion?.options?.map((option, index) => {
156157
return (
157158
<div className="single-opt" key={index}>
158159
<div
159160
className={itemClassDisplayController(option)}
160-
dangerouslySetInnerHTML={{ __html: option }}
161+
dangerouslySetInnerHTML={{ __html: sanitizeHTML(option) }}
161162
onClick={handleAnswerClick(option)}
162163
/>
163164
</div>

‎src/plays/text-to-speech/TextToSpeech.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState, useEffect, useRef } from 'react';
22
import { FaVolumeUp, FaStop } from 'react-icons/fa';
33
import PlayHeader from 'common/playlists/PlayHeader';
4+
import sanitizeHTML from 'common/utils/sanitizeHTML';
45
import './styles.css';
56

67
function TextToSpeech(props) {
@@ -160,7 +161,7 @@ function TextToSpeech(props) {
160161
<>
161162
<p
162163
className="tts-output-text"
163-
dangerouslySetInnerHTML={{ __html: convertedText }}
164+
dangerouslySetInnerHTML={{ __html: sanitizeHTML(convertedText) }}
164165
/>
165166

166167
<button className="tts-speaker-btn" onClick={handleSpeak}>

‎src/plays/tube2tunes/Tube2tunes.jsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ function Tube2tunes(props) {
4949
.then((res) => {
5050
if (res.data.status === 'processing') {
5151
setProcessingMsg(true);
52+
setLoading(false);
5253
} else if (res.data.status === 'fail') {
5354
setFailedMsg(true);
55+
setLoading(false);
5456
} else {
5557
setUrlResult(res.data.link);
5658
setTitle(res.data.title);

0 commit comments

Comments
 (0)