Security audit follow-ups, CalDAV compatibility with non-standard ports, ICS RFC-5545 compliance, and a reschedule-flow correctness fix.
Security
- OIDC account takeover via email-based linking (audit from #43) —
find_or_create_oidc_userfell through to matching by email without checking the ID token'semail_verifiedclaim. Any IdP that allows unverified registrations (Keycloak's default) let an attacker attach theiroidc_subjectto any existing local account by registering with the target's email. Now gated onemail_verified=truefor both the email-link and auto-register branches. Missing claim treated asfalse. - Stored XSS via backslash injection in inline onclick handlers (#43) — three dashboard templates (event types, sources, team settings) embedded user-controlled strings inside
onclick="… '\{{var}}\'…". MiniJinja doesn't HTML-escape backslashes, so a crafted payload could break out of the JS string and execute arbitrary script. Fix moves the value into adata-confirmattribute read viathis.dataset.confirm. Thanks @marcotama. - CSRF cookie missing Secure flag (audit from #43) — one-line fix;
HttpOnlyintentionally stays off for the double-submit pattern.
Fixed
- CalDAV sync fails with non-standard port (#42) — origin resolver stripped the port, breaking Nextcloud on
:8080. BlueMind unaffected (absolute URLs bypass the resolver). - Missing DTSTAMP in generated ICS (#49) — required by RFC 5545 §3.6.1. Strict clients (RustiCal) rejected invites. Thanks @Handfish.
- Pending bookings auto-cancelled on reschedule approval (#44).
Internal
- 16 new regression tests across the fixes (537 total, up from 521 in 1.5.0)
- Empirically verified that a fourth audit finding (email header injection via guest name) is a false positive — lettre's typed builder rejects or RFC-2047-encodes all tested CRLF payloads; pinned with tests rather than adding a redundant sanitizer
Full changelog: https://github.com/olivierlambert/calrs/blob/v1.6.0/CHANGELOG.md#160---2026-04-23
Thanks to @marcotama and @Handfish for their contributions this cycle.