Contact loading silently returned empty when the address book held
more entries than the server's maxObjectsInGet (Stalwart defaults to
500). Replace the single back-reference get with a two-step flow:
query IDs first, then pack a batched get into one JMAP request. All
batches ride a single HTTP roundtrip via multiple method calls.
Address books at or under the server cap still get a single method
call, so no behavior change for small lists.
Adds getMaxObjectsInGet() capability helper that mirrors the existing
getMaxCallsInRequest() / getMaxSizeUpload() shape. 83 new test cases
cover single-batch, exact-boundary, over-cap, and direct-ids paths.
Closes#45. Credits #46 (@capitanroy).
Defense-in-depth. All current callers pass hardcoded tailwind class
strings, so this is not exploitable today, but a future caller that
forwarded a user-controlled value would get HTML injection through the
class attribute. Run the value through escapeHtml() and add a test
covering the attribute-escape case.
Plain-text email bodies were escaped for <, >, & but not " or ', and the
URL linkifier regex captured every non-whitespace character up to the
next <. A URL containing a double or single quote broke out of the
href attribute in the rendered anchor, allowing arbitrary event handlers
to be injected into otherwise plain-text mail. Reported by @rathlinus.
Extract a shared plainTextToSafeHtml helper in lib/email-sanitization.ts
that escapes all five HTML-significant characters in the correct order
before linkification, and route both email-viewer and thread view
through it. Add tests that parse the output and assert no onmouseover
attribute lands on the anchor element.
Also bump dependencies flagged by npm audit: next 16.2.4 (DoS in Server
Components), next-intl 4.9.1 (open redirect), dompurify 3.4.0
(FORBID_TAGS bypass); picomatch/vite/brace-expansion resolve
transitively. npm audit is clean.
Bump version to 1.4.1.
heapTotal is V8's current allocation, not the max. A 35MB process with
37MB allocated reads as 95% when the real limit is ~2GB, causing
container restarts. Also fix stale calendar-participants test expectations.
P0 Core:
- Sandboxed iframe rendering for rich HTML emails (CSS isolation)
- API retry with exponential backoff for transient JMAP failures
- Mobile bottom action bar with touch-friendly email actions
- Long-press context menu + tap-to-expand submenus on touch devices
P1 Polish:
- Tag counts sidebar section with JMAP batch queries
- Empty folder option for Junk/Trash with batch delete
- Extra-compact density option (28px rows, 44px on touch)
- SPF/DKIM/DMARC security tooltips with plain-language explanations
- Resizable sidebars with drag, touch, and keyboard support
- Expandable sender info panel in email viewer
All 8 locales updated (en/fr/ja/es/it/de/nl/pt).
Logout now revokes the refresh token and redirects to the IdP's
end_session_endpoint so the SSO session is fully terminated. The
end_session_url is validated server-side (HTTPS only) before being
returned to the client, preventing open redirect attacks.
Also adds 14 unit tests covering PKCE (including RFC 7636 test vector)
and OAuth discovery (fallback, caching, required field validation).
Add organizer/attendee UI with RSVP buttons (accept/maybe/decline),
contact autocomplete in participant input, scheduling message support
via sendSchedulingMessages flag, and DnD notification for participant
events. Add inline calendar invitation banner in email viewer that
auto-detects .ics attachments, parses event details, and allows RSVP
or import to calendar. Includes cancellation display and 51 tests.
Dark mode color transform had 3 bugs preventing it from working:
- Hook only ran when external content was blocked, not for all emails
- Checked `theme` ('system') instead of `resolvedTheme` ('dark')
- Only transformed CSS style attrs, missing <font color> and bgcolor
Replaced invert+boost algorithm with blend-toward-white for text
(preserves hue, brighter results) and added background darkening
for light email backgrounds. Also handles color/bgcolor HTML attrs.
Fixed long email subjects pushing action buttons off-screen by adding
min-w-0 to the email viewer flex container in the page layout.
Evaluate JMAP CalendarEventAlert triggers client-side and display toast
notifications when alert times are reached. Extracts notification sound
into shared utility and mounts ToastContainer globally (fixes silent
toast failures across all components).
- Pure alert utilities: offset parsing, fire time computation (start/end),
effective alerts resolution with useDefaultAlerts, pending alert filtering
- Zustand persist store for acknowledged alert deduplication (24h retention)
- Global hook with 60s interval check, proactive 24h event fetch via ref
(isolated from calendar store), stale closure protection
- Settings: calendarNotificationsEnabled, calendarNotificationSound toggles
- Toast icon customization, CalendarAlertProvider in layout
- i18n: all 8 locales (EN/FR/JA/ES/IT/DE/NL/PT)
- 44 tests (36 alert utils + 8 notification store), 558 total passing
Contacts:
- Contact groups/lists with JMAP members map and composer expansion
- vCard import/export with RFC 6350 parser and duplicate detection
- Bulk operations (multi-select, delete, group add, export)
Search:
- Advanced search panel with JMAP filter fields
- Search chips for active filters visualization
- Debounced inputs with AbortController deduplication
Vacation:
- JMAP VacationResponse singleton management
- Settings tab with date range and message configuration
- Sidebar indicator when vacation responder is active
Auth:
- TOTP 2FA support with Stalwart-compatible password$totp format
Infrastructure:
- Docker multi-stage build with standalone Next.js output
- Structured server-side logger with text/JSON format
- CSP Report-Only and security headers via proxy middleware
- Layout refactoring (HTML structure in root layout)
- Playwright E2E framework setup
Testing: 450+ tests (identity, contacts, vCard, threads, headers, components)
i18n: All new strings added to all 8 locales
Implement RFC 2369 List-Unsubscribe support with security validation (protocol whitelist, XSS prevention), two-step confirmation UI, and localStorage persistence. Add intelligent color transformation for dark mode to fix unreadable inline email styles using WCAG 2.0 luminance calculation. Complete batch spam undo operations for context menu multi-select. Update documentation with new features and comprehensive test coverage (97 tests).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>