The next security issue to be concerned with input validation & code sanitization.
With input validation using types can help, but data that is correctly of type string can still contain malicious code.
This guy Theo on YouTube was the first person that I heard speak about Zod and subsequently tRPC. I think I’m going to give tRPC a try. Recently Zoom made an announcement that they were retooling with tRPC so I take it as a cue.
I have a ticket to leverage it in my app.
Comparatively speaking, the lower hanging fruit is data sanitization (depending on how large your app is). And in a React application, the use of dangerouslySetInnerHTML
is another security issue to look out for.
As the name of the property suggests, it can be dangerous to use
dangerouslySetInnerHTML
because it makes your code vulnerable to cross-site scripting (XSS) attacks. This becomes an issue, especially if you are fetching data from a third-party source or rendering content submitted by users.
A quick search through my application’s codebase yielded 2 instances of dangerouslySetInnerHTML
in the codebase.
How can we (meaning me/I) avoid using it?
After exploring some solutions, using dompurify
seems to be the way to go.

Great so now that we have a path, let’s start securing this application now.
First install the tools:
- DomPurify: https://www.npmjs.com/package/dompurify
- JsDOM: https://www.npmjs.com/package/jsdom
npm i dompurify
npm i jsdom
// or
npm i dompurify jsdom
Import these where necessary:
import createDOMPurify from 'dompurify';
const { JSDOM } = require('jsdom');
Instead of setting the element’s __html
parameter with the exploitable code, we just wrap it like this:
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
const VECTOR_OF_ATTACK = '<el>Something I can not create within my JSX component for some reason</el>';
const sanitized = DOMPurify.sanitize(VECTOR_OF_ATTACK);
<Element
dangerouslySetInnerHTML={{
__html: sanitized,
}}
/>
The bit of code I had to edit:
return (
<>
{/* SCHEMA JSON-LD MARKUP FOR GOOGLE */}
<Script
type="application/ld+json"
id={`json-ld-article-${article.slug}`}
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
mainEntityOfPage: {
"@type": "WebPage",
"@id": `https://${config.domainName}/blog/${article.slug}`,
},
name: article.title,
headline: article.title,
description: article.description,
image: `https://${config.domainName}${article.image.urlRelative}`,
datePublished: article.publishedAt,
dateModified: article.publishedAt,
author: {
"@type": "Person",
name: article.author.name,
},
}),
}}
/>
</>
);
The update:
const jsonLD = JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
mainEntityOfPage: {
"@type": "WebPage",
"@id": `https://${config.domainName}/blog/${article.slug}`,
},
name: article.title,
headline: article.title,
description: article.description,
image: `https://${config.domainName}${article.image.urlRelative}`,
datePublished: article.publishedAt,
dateModified: article.publishedAt,
author: {
"@type": "Person",
name: article.author.name,
},
})
const sanitizedContent = DOMPurify.sanitize(jsonLD);
return (
<>
{/* SCHEMA JSON-LD MARKUP FOR GOOGLE */}
<Script
type="application/ld+json"
id={`json-ld-article-${article.slug}`}
dangerouslySetInnerHTML={{
__html: sanitizedContent,
}}
/>
</>
);
With this, the application is a bit more secure.
Next I look at environment variables, which I’m already using in my app and is a process I tend to employ at inception of application.