Security hardening, structured logging & regression tests for 1.0#9
Merged
Conversation
…r 1.0 CSRF protection (double-submit cookie) on all 31 POST handlers, per-IP rate limiting on booking endpoints, server-side input validation, double-booking prevention via SQLite unique index + transactions, crash-proof web handlers (37 unwrap removals), graceful shutdown, and 50 structured tracing points across auth, bookings, CalDAV, admin, and email delivery. 28 new regression tests (191 → 219) covering ICS timezone/location bugs, input validation, CSRF token verification, and date range validation. Updated docs: security.md, architecture.md, deployment.md, booking-flow.md, introduction.md, README.md, CHANGELOG.md, and CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #9 +/- ##
==========================================
- Coverage 13.59% 13.49% -0.11%
==========================================
Files 16 16
Lines 5552 5930 +378
==========================================
+ Hits 755 800 +45
- Misses 4797 5130 +333 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
8 tasks
huntervcx
pushed a commit
to DYB-Corp/calrs
that referenced
this pull request
Jun 3, 2026
Blockers (from review): - Migration 050_provider_type renumbered to 054_provider_type to avoid collision with upstream main's 051-053. src/db.rs migration array + CLAUDE.md index updated. - Migration-count assertions in db.rs tests bumped from 50 to 54. Substantive review items folded in: - sync_folder_items now caps at 200 SOAP iterations; a server that never sets IncludesLastItemInRange=true returns partial results and is resumed from the latest cursor next sync. (olivierlambert#8) - synth_vcalendar emits DTSTAMP per RFC 5545; EWS doesn't expose a stable last-modified, so we use Utc::now(). (olivierlambert#6) - EWS sync_delta comment rewritten to acknowledge "EWS sources rely on full fetches" rather than claiming the cursor gets bootstrapped. (olivierlambert#3, option A) - fetch_events_since wired into sync_ews_source with the same 90-day lookback used by the CalDAV path. The EWS impl uses CalendarView (server-side window). remove_orphaned_ews_events now takes the same since_prefix scoping as the CalDAV variant so events outside the window are not flagged as orphans. (olivierlambert#4) - format_dt TODO documenting naive-local TZID drift on non-recurring items. (olivierlambert#7) - Autodiscover redirect policy now carries an explicit comment about the SSRF residual risk on intermediate Location headers. (olivierlambert#5) - 054_provider_type.sql explains the calendars.href / calendars.ctag reuse for EWS folder ItemId / ChangeKey. (olivierlambert#9) - extract_vcalendar comment corrected: the \r\n /\r\n\t replacements are RFC 5545 §3.1 line-fold unfolding, not "stray indentation". Rebase side-effects: - SSRF guard now uses upstream's CALRS_ALLOW_PRIVATE_HOSTS allowlist (hostname-scoped) instead of the previous boolean toggle. private_ host_allowlist() is exposed for the serve startup WARN log. - Sources flow (commands/source.rs, commands/sync.rs, web/mod.rs) reconciled with upstream's OAuth2 dispatch + orphan-cancel work: EWS sources go through the provider trait, CalDAV sources keep the CaldavClient path (basic-auth or OAuth2 unchanged). - new sync_ews_source/upsert_calendar_provider/upsert_provider_events helpers in commands/sync.rs to keep the EWS path off the CalDAV CaldavClient signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
huntervcx
pushed a commit
to DYB-Corp/calrs
that referenced
this pull request
Jun 3, 2026
Blockers (from review): - Migration 050_provider_type renumbered to 054_provider_type to avoid collision with upstream main's 051-053. src/db.rs migration array + CLAUDE.md index updated. - Migration-count assertions in db.rs tests bumped from 50 to 54. Substantive review items folded in: - sync_folder_items now caps at 200 SOAP iterations; a server that never sets IncludesLastItemInRange=true returns partial results and is resumed from the latest cursor next sync. (olivierlambert#8) - synth_vcalendar emits DTSTAMP per RFC 5545; EWS doesn't expose a stable last-modified, so we use Utc::now(). (olivierlambert#6) - EWS sync_delta comment rewritten to acknowledge "EWS sources rely on full fetches" rather than claiming the cursor gets bootstrapped. (olivierlambert#3, option A) - fetch_events_since wired into sync_ews_source with the same 90-day lookback used by the CalDAV path. The EWS impl uses CalendarView (server-side window). remove_orphaned_ews_events now takes the same since_prefix scoping as the CalDAV variant so events outside the window are not flagged as orphans. (olivierlambert#4) - format_dt TODO documenting naive-local TZID drift on non-recurring items. (olivierlambert#7) - Autodiscover redirect policy now carries an explicit comment about the SSRF residual risk on intermediate Location headers. (olivierlambert#5) - 054_provider_type.sql explains the calendars.href / calendars.ctag reuse for EWS folder ItemId / ChangeKey. (olivierlambert#9) - extract_vcalendar comment corrected: the \r\n /\r\n\t replacements are RFC 5545 §3.1 line-fold unfolding, not "stray indentation". Rebase side-effects: - SSRF guard now uses upstream's CALRS_ALLOW_PRIVATE_HOSTS allowlist (hostname-scoped) instead of the previous boolean toggle. private_ host_allowlist() is exposed for the serve startup WARN log. - Sources flow (commands/source.rs, commands/sync.rs, web/mod.rs) reconciled with upstream's OAuth2 dispatch + orphan-cancel work: EWS sources go through the provider trait, CalDAV sources keep the CaldavClient path (basic-auth or OAuth2 unchanged). - new sync_ews_source/upsert_calendar_provider/upsert_provider_events helpers in commands/sync.rs to keep the EWS path off the CalDAV CaldavClient signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
olivierlambert
pushed a commit
that referenced
this pull request
Jun 4, 2026
* feat(providers): add Microsoft Exchange (EWS) calendar provider
Introduces a generic CalendarProvider trait so calrs is no longer
hard-wired to CalDAV. Adds an EWS implementation targeting on-prem
Exchange 2019 (also compatible with 2016/2013) with Autodiscover,
calendar listing, full + windowed event fetch, create/delete, and
SyncFolderItems delta sync.
The CalDAV path is preserved unchanged behind a thin adapter; sync,
source management, and write-back now dispatch on a new provider_type
column (migration 050). HTTPS-only and SSRF-safe URL validation is
shared across both providers, and Autodiscover candidate URLs are
re-validated before they get hit.
Layout:
src/providers/ trait + factory + caldav adapter
src/ews/ soap, autodiscover, operations, parse, ical synth
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fixup(ews): drop unused Context import + dead summary_view, collapse
duplicate connection-check arms, rustfmt
Cargo.lock is updated by Cargo's first compile after a fresh dependency
add (async-trait); committing it so a clean checkout doesn't churn it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* wip: save ews provider work
* feat: clean sources presets filtered via backend
* fixup(ews): address review blockers + substantive items
Blockers (from review):
- Migration 050_provider_type renumbered to 054_provider_type to avoid
collision with upstream main's 051-053. src/db.rs migration array +
CLAUDE.md index updated.
- Migration-count assertions in db.rs tests bumped from 50 to 54.
Substantive review items folded in:
- sync_folder_items now caps at 200 SOAP iterations; a server that
never sets IncludesLastItemInRange=true returns partial results and
is resumed from the latest cursor next sync. (#8)
- synth_vcalendar emits DTSTAMP per RFC 5545; EWS doesn't expose a
stable last-modified, so we use Utc::now(). (#6)
- EWS sync_delta comment rewritten to acknowledge "EWS sources rely
on full fetches" rather than claiming the cursor gets bootstrapped.
(#3, option A)
- fetch_events_since wired into sync_ews_source with the same 90-day
lookback used by the CalDAV path. The EWS impl uses CalendarView
(server-side window). remove_orphaned_ews_events now takes the same
since_prefix scoping as the CalDAV variant so events outside the
window are not flagged as orphans. (#4)
- format_dt TODO documenting naive-local TZID drift on non-recurring
items. (#7)
- Autodiscover redirect policy now carries an explicit comment about
the SSRF residual risk on intermediate Location headers. (#5)
- 054_provider_type.sql explains the calendars.href / calendars.ctag
reuse for EWS folder ItemId / ChangeKey. (#9)
- extract_vcalendar comment corrected: the \r\n /\r\n\t replacements
are RFC 5545 §3.1 line-fold unfolding, not "stray indentation".
Rebase side-effects:
- SSRF guard now uses upstream's CALRS_ALLOW_PRIVATE_HOSTS allowlist
(hostname-scoped) instead of the previous boolean toggle. private_
host_allowlist() is exposed for the serve startup WARN log.
- Sources flow (commands/source.rs, commands/sync.rs, web/mod.rs)
reconciled with upstream's OAuth2 dispatch + orphan-cancel work:
EWS sources go through the provider trait, CalDAV sources keep the
CaldavClient path (basic-auth or OAuth2 unchanged).
- new sync_ews_source/upsert_calendar_provider/upsert_provider_events
helpers in commands/sync.rs to keep the EWS path off the CalDAV
CaldavClient signature.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Arthur Perrot <aperrot@dyb.fr>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
huntervcx
pushed a commit
to DYB-Corp/calrs
that referenced
this pull request
Jun 25, 2026
Security hardening, structured logging & regression tests for 1.0
huntervcx
added a commit
to DYB-Corp/calrs
that referenced
this pull request
Jun 25, 2026
…ert#103) * feat(providers): add Microsoft Exchange (EWS) calendar provider Introduces a generic CalendarProvider trait so calrs is no longer hard-wired to CalDAV. Adds an EWS implementation targeting on-prem Exchange 2019 (also compatible with 2016/2013) with Autodiscover, calendar listing, full + windowed event fetch, create/delete, and SyncFolderItems delta sync. The CalDAV path is preserved unchanged behind a thin adapter; sync, source management, and write-back now dispatch on a new provider_type column (migration 050). HTTPS-only and SSRF-safe URL validation is shared across both providers, and Autodiscover candidate URLs are re-validated before they get hit. Layout: src/providers/ trait + factory + caldav adapter src/ews/ soap, autodiscover, operations, parse, ical synth Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fixup(ews): drop unused Context import + dead summary_view, collapse duplicate connection-check arms, rustfmt Cargo.lock is updated by Cargo's first compile after a fresh dependency add (async-trait); committing it so a clean checkout doesn't churn it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * wip: save ews provider work * feat: clean sources presets filtered via backend * fixup(ews): address review blockers + substantive items Blockers (from review): - Migration 050_provider_type renumbered to 054_provider_type to avoid collision with upstream main's 051-053. src/db.rs migration array + CLAUDE.md index updated. - Migration-count assertions in db.rs tests bumped from 50 to 54. Substantive review items folded in: - sync_folder_items now caps at 200 SOAP iterations; a server that never sets IncludesLastItemInRange=true returns partial results and is resumed from the latest cursor next sync. (olivierlambert#8) - synth_vcalendar emits DTSTAMP per RFC 5545; EWS doesn't expose a stable last-modified, so we use Utc::now(). (olivierlambert#6) - EWS sync_delta comment rewritten to acknowledge "EWS sources rely on full fetches" rather than claiming the cursor gets bootstrapped. (olivierlambert#3, option A) - fetch_events_since wired into sync_ews_source with the same 90-day lookback used by the CalDAV path. The EWS impl uses CalendarView (server-side window). remove_orphaned_ews_events now takes the same since_prefix scoping as the CalDAV variant so events outside the window are not flagged as orphans. (olivierlambert#4) - format_dt TODO documenting naive-local TZID drift on non-recurring items. (olivierlambert#7) - Autodiscover redirect policy now carries an explicit comment about the SSRF residual risk on intermediate Location headers. (olivierlambert#5) - 054_provider_type.sql explains the calendars.href / calendars.ctag reuse for EWS folder ItemId / ChangeKey. (olivierlambert#9) - extract_vcalendar comment corrected: the \r\n /\r\n\t replacements are RFC 5545 §3.1 line-fold unfolding, not "stray indentation". Rebase side-effects: - SSRF guard now uses upstream's CALRS_ALLOW_PRIVATE_HOSTS allowlist (hostname-scoped) instead of the previous boolean toggle. private_ host_allowlist() is exposed for the serve startup WARN log. - Sources flow (commands/source.rs, commands/sync.rs, web/mod.rs) reconciled with upstream's OAuth2 dispatch + orphan-cancel work: EWS sources go through the provider trait, CalDAV sources keep the CaldavClient path (basic-auth or OAuth2 unchanged). - new sync_ews_source/upsert_calendar_provider/upsert_provider_events helpers in commands/sync.rs to keep the EWS path off the CalDAV CaldavClient signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Arthur Perrot <aperrot@dyb.fr> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR prepares calrs for a 1.0 release with comprehensive security hardening, production observability, reliability improvements, and regression tests.
21 files changed, +1,583 / -244 lines — tests: 191 → 219 (+28)
Security
CSRF protection (double-submit cookie pattern)
All 31 POST handlers are now protected against cross-site request forgery:
csrf_cookie_middleware) sets acalrs_csrfcookie on every responsebase.htmlreads the cookie and injects a hidden_csrffield into all formsBooking rate limiting
Per-IP rate limiter on all 4 booking endpoints (user, group, team link, legacy):
X-Forwarded-Forheader (reverse proxy aware)Server-side input validation
maxlengthattributes on form inputs (defense in depth)Double-booking prevention
016_booking_unique.sql: partial unique index on(event_type_id, start_at) WHERE status IN ('confirmed', 'pending')BEGIN IMMEDIATEtransactionReliability
Crash-proof web handlers
Replaced ~37 bare
.unwrap()calls in web handlers with proper error handling:matchreturning "Internal error" HTMLmatchreturning "Invalid date/time" responseunwrap_or_elsewith fallbackunwrap_or(NaiveTime::MIN)unwrap_or_default()Graceful shutdown
calrs servenow handles SIGINT (Ctrl+C) and SIGTERM:Error handling
No web handler can panic from user input. All paths return proper HTTP responses.
Observability
Structured logging (50 tracing points)
Replaced all
eprintln!/println!in web paths withtracingstructured logging. Addedtower-httpTraceLayerfor HTTP request tracing.Default:
RUST_LOG=calrs=info,tower_http=info. Visible injournalctl -u calrs -fordocker logs -f calrs.Tests (+28 new, 191 → 219 total)
ICS regression tests (6) — prevent the bugs fixed in v0.18.2
convert_to_utcwith Europe/Paris, UTC, and invalid timezone fallbackZ— prevents floating-time bugInput validation tests (14)
validate_booking_input: empty name, name too long, valid name, empty email, email without @, email without domain dot, valid email, notes too long, notes within limit, None notesvalidate_date_not_too_far: within range, exactly 365 days, 367 days rejected, past date passesCSRF tests (8)
Documentation updates
docs/src/security.md— Replaced "No CSRF tokens" limitation with full CSRF, rate limiting, input validation, double-booking prevention, error handling sections. Updated credential storage (now AES-256-GCM).docs/src/architecture.md— Added migrations 015-016, test count 147→219, tracing/tower-http deps, middleware table.docs/src/deployment.md— AddedRUST_LOGenv var, full Observability section with categories table and viewing instructions.docs/src/booking-flow.md— Guest self-cancellation, reminder email row, double-booking prevention note.docs/src/introduction.md— Structured logging and security hardening in feature list.README.md— New Observability section with config, categories, example output.CHANGELOG.md— v0.18.2 entry + feature table entries for all 1.0 items.CLAUDE.md— tracing/tower-http in tech stack, migration 016, security and observability sections.Files changed
src/web/mod.rssrc/auth.rssrc/email.rssrc/main.rssrc/db.rssrc/commands/sync.rsmigrations/016_booking_unique.sqltemplates/base.htmltemplates/book.htmlmaxlengthon form inputstemplates/settings.htmlmaxlengthon form inputstemplates/auth/register.htmlmaxlengthon form inputsCargo.tomldocs/src/*.mdREADME.mdCHANGELOG.mdCLAUDE.mdTest plan
cargo test— 219 tests passcargo clippy -- -D warnings— cleancargo fmt --check— cleanjournalctloutput after login + booking🤖 Generated with Claude Code