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.
New features: folder management (#44), mail multi-selection (#43).
Bug fixes: health endpoint (#41), identity deletion (#42), inline CID
images, email list flicker, dependency updates.
Thanks to @wrenix, @ClemaX, @freddij, @dlecourtaltimafr, and @capitanroy
for reporting issues and contributing to this release.
Fixes: CSRF bypass on Server Actions, HTTP request smuggling in
rewrites, unbounded image disk cache, postponed resume DoS, dev
HMR websocket CSRF bypass.
Remove jmap-jam (unused), @types/dompurify (bundled in dompurify 3.x),
eslint-config-next (not used in flat config), and lint-staged (unreferenced).
Add @eslint/js and eslint-plugin-react-hooks as explicit devDependencies
since they were phantom deps that would break without their former hosts.
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 comprehensive spam management allowing users to report spam emails and restore false positives. Emails are moved to/from the Junk folder with toast notifications and undo support.
Key features:
- Mark as spam: moves email to Junk folder with 5-second undo
- Mark as not spam: restores email from Junk to inbox
- Batch spam operations via context menu
- Smart toggle based on current folder (Junk vs other folders)
- Keyboard shortcut (Shift+!) for quick spam reporting
- Full i18n support (EN/FR)
- Shared mailbox compatibility
UX improvements:
- Toast notifications with undo action using sonner library
- Auto-deselect email after spam action
- Visual distinction with red destructive styling
- Desktop button + context menu + keyboard shortcuts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Implement thread grouping by threadId in email list
- Add ThreadListItem with collapsed/expanded states for desktop
- Create ThreadConversationView for full-screen mobile experience
- Add thread utility functions for grouping and sorting
- Extend JMAP client with getThread() and getThreadEmails() methods
- Add keyboard shortcut 'x' to expand/collapse threads
- Add thread expansion state management in email store
- Fix React hooks order in login page
- Add .env.example for environment configuration
- Add husky for git hooks
- Implement error boundaries with graceful fallbacks for all major components
(sidebar, email list, email viewer, composer)
- Add push notifications via EventSource for real-time email updates
- Show connection status indicator in sidebar footer
- Play notification sound and show toast for new emails
- Add global error handler and error reporting utilities
- Configure ESLint with modern flat config
- Various fixes: unused variable warnings, proper cleanup on disconnect
- Update translations for error messages (EN/FR)
- showPreview: Conditionally render email preview in list based on setting
- externalContentPolicy: Control external content (ask/block/allow modes)
- deleteAction: Move to trash or permanently delete based on preference
- debugMode: Add conditional logging via new lib/debug.ts utility
Also includes:
- Add moveToTrash method to JMAP client for trash-based deletion
- Update TODO.md with verified implementation status
- Migrate middleware.ts to proxy.ts (Next.js deprecation)
- Add next-intl for internationalization
- Create English and French translation files
- Implement language switcher component in sidebar
- Configure automatic browser language detection
- Update all components to use translation keys
- Set up locale-aware routing with [locale] directory structure
- Add i18n guidelines to CLAUDE.md for future development
- Store user language preference in localStorage
The app now supports:
- English (en) and French (fr) languages
- Automatic browser language detection on first visit
- User language preference persistence
- Real-time language switching without page reload
- Complete translation coverage for all UI elements
- Set up Next.js 15.5 with TypeScript and Turbopack
- Configured Tailwind CSS v4 with minimalist design system
- Created core email components: list, viewer, composer
- Implemented sidebar navigation with mailbox folders
- Added JMAP client wrapper using jmap-jam library
- Set up Zustand for state management
- Created responsive layout with three-pane view
- Added mock data for development