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-07-06 - XSS Vulnerability in Email Previews via dangerouslySetInnerHTML
**Vulnerability:** The `PppSavingsPanel` component uses `dangerouslySetInnerHTML` to render email previews. Even though `DOMPurify.sanitize` is used, displaying user-controlled or external HTML directly into the DOM can still pose subtle XSS risks or styling leakage, especially if `DOMPurify` configuration is not sufficiently restrictive.
**Learning:** Using `dangerouslySetInnerHTML` combined with a sanitizer is not always sufficient for rendering complex or untrusted HTML like email templates. It is safer to isolate the rendering context entirely to prevent both XSS and CSS injection that could affect the main application layout.
**Prevention:** Prioritize using a sandboxed `<iframe>` with the `srcDoc` attribute instead of `dangerouslySetInnerHTML` for rendering dynamic or untrusted HTML. Use `sandbox="allow-popups allow-popups-to-escape-sandbox"` to permit link clicking while omitting `allow-scripts` and `allow-same-origin`.
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="min-h-[24rem] w-full bg-white p-0 text-sm text-black flex flex-col">
<iframe
title="Email Preview"
srcDoc={`<!DOCTYPE html><html><head><style>body { margin: 0; padding: 1.5rem; font-family: ui-sans-serif, system-ui, sans-serif; font-size: 0.875rem; color: #000; }</style></head><body>${sanitizedEmailHtml}</body></html>`}
sandbox="allow-popups allow-popups-to-escape-sandbox"
className="w-full flex-grow border-0"
/>
</div>
</div>
</div>
Expand Down
160 changes: 160 additions & 0 deletions dev_server.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@

> map-app@0.1.0 dev
> next dev

▲ Next.js 16.3.0-canary.36 (Turbopack)
- Local: http://localhost:3000
- Network: http://192.168.0.2:3000
- Environments: .env
✓ Ready in 637ms
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry


○ Compiling / ...
GET / 307 in 6.7s (next.js: 6.2s, application-code: 499ms)
GET /login 200 in 1112ms (next.js: 872ms, application-code: 241ms)
GET / 307 in 206ms (next.js: 6ms, application-code: 200ms)
GET /login?org=fraternitees 200 in 326ms (next.js: 25ms, application-code: 301ms)
GET /login 200 in 209ms (next.js: 5ms, application-code: 204ms)
○ Compiling /api/tenant-session ...
⨯ {
message: 'TypeError: fetch failed',
details: 'TypeError: fetch failed\n' +
'\n' +
'Caused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\n' +
'Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n' +
' at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n' +
' at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)',
hint: '',
code: ''
}
POST /api/tenant-session 500 in 9.2s (next.js: 2.1s, application-code: 7.2s)
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 11.4s (next.js: 3.2s, application-code: 8.3s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ TypeError: fetch failed
at async fetchWithRetry (lib/application/runtime/runtime-rest.ts:44:24)
at async runtimeRestRequest (lib/application/runtime/runtime-rest.ts:63:20)
at async findRuntimeOrganization (lib/application/runtime/runtime-rest.ts:128:20)
at async getTerritoryRuntimeDashboard (lib/application/runtime/territory-service.ts:349:24)
at async getRepTodaySummary (lib/application/runtime/rep-today-service.ts:58:53)
at async TodayPage (app/today/page.tsx:15:19)
42 | for (let attempt = 1; attempt <= REST_FETCH_ATTEMPTS; attempt += 1) {
43 | try {
> 44 | const response = await fetch(input, init);
| ^
45 | if (!shouldRetryStatus(response.status) || attempt === REST_FETCH_ATTEMPTS) {
46 | return response;
47 | } {
digest: '1719308871',
[cause]: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co
at ignore-listed frames {
errno: -3008,
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'your-project-ref.supabase.co'
}
}
GET /today?org=fraternitees 500 in 1983ms (next.js: 995ms, application-code: 989ms)
[browser] Uncaught TypeError: fetch failed
at fetchWithRetry (lib/application/runtime/runtime-rest.ts:44:24)
at runtimeRestRequest (lib/application/runtime/runtime-rest.ts:63:20)
at findRuntimeOrganization (lib/application/runtime/runtime-rest.ts:128:20)
at getTerritoryRuntimeDashboard (lib/application/runtime/territory-service.ts:349:24)
at Function.all (<anonymous>:1:21)
at getRepTodaySummary (lib/application/runtime/rep-today-service.ts:58:53)
at TodayPage (app/today/page.tsx:15:19)
42 | for (let attempt = 1; attempt <= REST_FETCH_ATTEMPTS; attempt += 1) {
43 | try {
> 44 | const response = await fetch(input, init);
| ^
45 | if (!shouldRetryStatus(response.status) || attempt === REST_FETCH_ATTEMPTS) {
46 | return response;
47 | }
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 7.6s (next.js: 4ms, application-code: 7.6s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ TypeError: fetch failed
at async fetchWithRetry (lib/application/runtime/runtime-rest.ts:44:24)
at async runtimeRestRequest (lib/application/runtime/runtime-rest.ts:63:20)
at async findRuntimeOrganization (lib/application/runtime/runtime-rest.ts:128:20)
at async getTerritoryRuntimeDashboard (lib/application/runtime/territory-service.ts:349:24)
at async getRepTodaySummary (lib/application/runtime/rep-today-service.ts:58:53)
at async TodayPage (app/today/page.tsx:15:19)
42 | for (let attempt = 1; attempt <= REST_FETCH_ATTEMPTS; attempt += 1) {
43 | try {
> 44 | const response = await fetch(input, init);
| ^
45 | if (!shouldRetryStatus(response.status) || attempt === REST_FETCH_ATTEMPTS) {
46 | return response;
47 | } {
digest: '1719308871',
[cause]: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co
at ignore-listed frames {
errno: -3008,
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'your-project-ref.supabase.co'
}
}
GET /today?org=dynalites 500 in 416ms (next.js: 5ms, application-code: 412ms)
[browser] Uncaught TypeError: fetch failed
at fetchWithRetry (lib/application/runtime/runtime-rest.ts:44:24)
at runtimeRestRequest (lib/application/runtime/runtime-rest.ts:63:20)
at findRuntimeOrganization (lib/application/runtime/runtime-rest.ts:128:20)
at getTerritoryRuntimeDashboard (lib/application/runtime/territory-service.ts:349:24)
at Function.all (<anonymous>:1:21)
at getRepTodaySummary (lib/application/runtime/rep-today-service.ts:58:53)
at TodayPage (app/today/page.tsx:15:19)
42 | for (let attempt = 1; attempt <= REST_FETCH_ATTEMPTS; attempt += 1) {
43 | try {
> 44 | const response = await fetch(input, init);
| ^
45 | if (!shouldRetryStatus(response.status) || attempt === REST_FETCH_ATTEMPTS) {
46 | return response;
47 | }
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 7.6s (next.js: 7ms, application-code: 7.6s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 7.7s (next.js: 3ms, application-code: 7.7s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 7.7s (next.js: 7ms, application-code: 7.6s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /territory?org=fraternitees 500 in 8.6s (next.js: 838ms, application-code: 7.8s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /accounts?org=fraternitees 500 in 7.6s (next.js: 4ms, application-code: 7.6s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
⨯ Error: {"message":"TypeError: fetch failed","details":"TypeError: fetch failed\n\nCaused by: Error: getaddrinfo ENOTFOUND your-project-ref.supabase.co (ENOTFOUND)\nError: getaddrinfo ENOTFOUND your-project-ref.supabase.co\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26)\n at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17)","hint":"","code":""}
at ignore-listed frames {
digest: '3183108395@E394'
}
GET /integrations?org=fraternitees 500 in 8.6s (next.js: 805ms, application-code: 7.8s)
[browser] Uncaught Error: {message: ..., details: ..., hint: "", code: ...}
Loading