Say that I am getting text from a database that includes #hashtags. I want to add this text to the inner content of a <Post>
element. However, I would like to process this text so that the hashtags are wrapped in an <a/>
element, so it is not as simple as just including all the plaintext content as a child of Post like <Post>my text here</Post>
.
Currently what I am doing is:
function Post(props: Post) {
// ...
return(
<div>
{textPreprocessor(props.text)}
</div>
)}
//...
const textPreprocessor = (content : string) : React.ReactNode => {
// adds hashtags
let words = content.split(" ");
let htmlString = ""
words.forEach(word => {
if(word[0] === "#") {
htmlString = `<a href='hashtag/:${word}'/>${word}</a> `
}
else{
htmlString = `${word} `
}
})
return <div dangerouslySetInnerHTML={{__html: htmlString}} />;
}
Now this "works", but it's also extremely dangerous. People can just write whatever HTML/JS they want into the database and it will be executed in the frontend. Is there a safer way to do this?
CodePudding user response:
There are some libraries for that, like dompurify. Those libraries add some more security for this, but you should always consider this to be dangerous and try to avoid such things.
CodePudding user response:
A preliminary safer solution based off @Oleg's answer, which does not use any 3rd party libraries, is that I created a new function:
const sanitizeHTML = (unsanitized : string) : string => {
const el : HTMLDivElement = document.createElement("div");
el.innerText = unsanitized;
return el.innerHTML;
}
and then made a slight modification to textPreprocessor
const textPreprocessor = (content : string) : React.ReactNode => {
// adds hashtags
const sanitized = sanitizeHTML(content)
const words = content.split(" ");
let htmlString = ""
words.forEach(word => {
if(word[0] === "#") {
htmlString = `<a href='hashtag/:${word}'/>${word}</a> `
}
else{
htmlString = `${word} `
}
})
return <div dangerouslySetInnerHTML={{__html: htmlString}} />;
}