Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2024-05-18 - Replacing dangerouslySetInnerHTML with Sandboxed Iframes
**Vulnerability:** XSS vulnerability in dynamically generated email preview rendered using dangerouslySetInnerHTML, even with DOMPurify.
**Learning:** When displaying dynamic or user-generated HTML in React, dangerouslySetInnerHTML should be avoided entirely in favor of an isolated browsing context. Applying external CSS classes (like Tailwind) to an iframe does not style the inner document; styles must be injected inline via a <style> tag within the srcDoc.
**Prevention:** Always use a sandboxed iframe (<iframe sandbox="allow-popups allow-popups-to-escape-sandbox" srcDoc={html}>) instead of dangerouslySetInnerHTML when previewing rich text content.
9 changes: 7 additions & 2 deletions components/accounts/ppp-savings-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,13 @@ export function PppSavingsPanel({ orgSlug, accountId }: PppSavingsPanelProps) {
)}
</div>
</div>
<div className="min-h-[24rem] w-full overflow-auto bg-white p-6 text-sm text-black">
<div dangerouslySetInnerHTML={{ __html: sanitizedEmailHtml }} />
<div className="flex min-h-[24rem] w-full flex-col overflow-auto bg-white p-6 text-sm text-black">
<iframe
title="Email preview"
sandbox="allow-popups allow-popups-to-escape-sandbox"
srcDoc={`<style>body { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.875rem; line-height: 1.25rem; margin: 0; padding: 0; color: #000; background: transparent; }</style>${sanitizedEmailHtml}`}
className="h-full min-h-[24rem] w-full flex-1 border-0"
/>
</div>
</div>
</div>
Expand Down
Loading