Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: added DOMPurify to prevent xss #1894

Merged
merged 8 commits into from
Jan 23, 2024
Merged

chore: added DOMPurify to prevent xss #1894

merged 8 commits into from
Jan 23, 2024

Conversation

Dhruwang
Copy link
Contributor

What does this PR do?

Fixes 1657

How should this be tested?

Test editors

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read How we Code at Formbricks
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand bits
  • Ran pnpm build
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues
  • First PR at Formbricks? Please sign the CLA! Without it we wont be able to merge it 🙏

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Formbricks Docs if changes were necessary

Copy link

vercel bot commented Jan 12, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Ignored Deployments
Name Status Preview Comments Updated (UTC)
formbricks-cloud ⬜️ Ignored (Inspect) Visit Preview Jan 23, 2024 4:56pm
formbricks-com ⬜️ Ignored (Inspect) Visit Preview Jan 23, 2024 4:56pm

Copy link
Contributor

github-actions bot commented Jan 12, 2024

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

apps/formbricks-com/components/dummyUI/HtmlBody.tsx

While the use of DOMPurify is a good step towards ensuring the security of the application, it would be beneficial to configure it to use a safe subset of HTML to further enhance security. This can be done by passing a configuration object to the sanitize function.
Create Issue
See the diff
Checkout the fix

    const clean = DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'] });
git fetch origin && git checkout -b ReviewBot/The-c-o6oaeat origin/ReviewBot/The-c-o6oaeat

packages/surveys/src/components/general/HtmlBody.tsx

While the current implementation is correct, it could be optimized by avoiding the dynamic import of DOMPurify in the useEffect hook. Dynamic imports are useful for code splitting and lazy loading, but in this case, it might be unnecessary and could lead to performance issues due to the additional network request. Consider importing DOMPurify at the top of the file instead.
Create Issue
See the diff
Checkout the fix

    import DOMPurify from 'dompurify';
git fetch origin && git checkout -b ReviewBot/The-c-ckcfpca origin/ReviewBot/The-c-ckcfpca


export default function HtmlBody({ htmlString, questionId }: { htmlString: string; questionId: string }) {
return (
<label
htmlFor={questionId}
className="fb-block fb-font-normal fb-leading-6 text-sm text-slate-500 dark:text-slate-300"
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString) }}></label>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DOMPurify.sanitize function has been configured to only allow a safe subset of HTML tags. This enhances the security of the application by preventing the execution of potentially harmful HTML code.

Suggested change
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'] }) }}

Comment on lines 1 to 27
import { useEffect, useState } from "react";

interface HtmlBodyProps {
htmlString: string | undefined;
questionId: string;
}

export default function HtmlBody({ htmlString, questionId }: HtmlBodyProps) {
const [safeHtml, setSafeHtml] = useState("");

useEffect(() => {
if (htmlString) {
import("dompurify").then((DOMPurify) => {
setSafeHtml(DOMPurify.sanitize(htmlString));
});
}
}, [htmlString]);

export default function HtmlBody({ htmlString, questionId }: { htmlString?: string; questionId: string }) {
if (!htmlString) return null;

return (
<label
htmlFor={questionId}
className="fb-htmlbody" // styles are in global.css
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: safeHtml }}></label>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic import of DOMPurify in the useEffect hook has been replaced with a static import at the top of the file. This change optimizes the code by avoiding unnecessary network requests, improving performance.

Suggested change
import { useEffect, useState } from "react";
interface HtmlBodyProps {
htmlString: string | undefined;
questionId: string;
}
export default function HtmlBody({ htmlString, questionId }: HtmlBodyProps) {
const [safeHtml, setSafeHtml] = useState("");
useEffect(() => {
if (htmlString) {
import("dompurify").then((DOMPurify) => {
setSafeHtml(DOMPurify.sanitize(htmlString));
});
}
}, [htmlString]);
export default function HtmlBody({ htmlString, questionId }: { htmlString?: string; questionId: string }) {
if (!htmlString) return null;
return (
<label
htmlFor={questionId}
className="fb-htmlbody" // styles are in global.css
dangerouslySetInnerHTML={{ __html: cleanHtml(htmlString) }}></label>
dangerouslySetInnerHTML={{ __html: safeHtml }}></label>
);
}
import DOMPurify from 'dompurify';
import { useEffect, useState } from "react";
interface HtmlBodyProps {
htmlString: string | undefined;
questionId: string;
}
export default function HtmlBody({ htmlString, questionId }: HtmlBodyProps) {
const [safeHtml, setSafeHtml] = useState("");
useEffect(() => {
if (htmlString) {
setSafeHtml(DOMPurify.sanitize(htmlString));
}
}, [htmlString]);
if (!htmlString) return null;
return (
<label
htmlFor={questionId}
className="fb-htmlbody" // styles are in global.css
dangerouslySetInnerHTML={{ __html: safeHtml }}></label>
);
}

Copy link
Member

@mattinannt mattinannt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Dhruwang thank you very much for this security fix :-) just one comment:

packages/surveys/package.json Outdated Show resolved Hide resolved
}

export default function HtmlBody({ htmlString, questionId }: HtmlBodyProps) {
const [safeHtml, setSafeHtml] = useState("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use a useMemo instead?

const safeHtml = useMemo(() => DOMPurify.sanitize(htmlString), [htmlString])

@mattinannt
Copy link
Member

@Dhruwang Did the build succeed in your testing?
I just updated the PR, merged the latest changes and fixed the merge conflicts and ran into this build error:

@formbricks/surveys:build: error during build:
@formbricks/surveys:build: Error: [vite]: Rollup failed to resolve import "dompurify" from "/Users/matthiasnannt/Developer/formbricks/packages/surveys/src/components/general/HtmlBody.tsx".
@formbricks/surveys:build: This is most likely unintended because it can break your application at runtime.
@formbricks/surveys:build: If you do want to externalize this module explicitly add it to
@formbricks/surveys:build: `build.rollupOptions.external`

Can you please take a look :-) Ideally DomPurify will be included into the bundle files of @formbricks/surveys as we will soon move on to sideload the complete package in formbricks-js

Copy link
Member

@mattinannt mattinannt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Dhruwang thank you for making the changes 😊💪

@mattinannt mattinannt added this pull request to the merge queue Jan 23, 2024
github-merge-queue bot pushed a commit that referenced this pull request Jan 23, 2024
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 23, 2024
@mattinannt mattinannt added this pull request to the merge queue Jan 23, 2024
Merged via the queue into main with commit 64db294 Jan 23, 2024
12 checks passed
@mattinannt mattinannt deleted the dompurify branch January 23, 2024 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants