Repository navigation
feat(calendar): groundwork for CalDAV calendar integration (1/4) - #49
ArtyomSavchenko merged 3 commits into
Conversation
Preparation for a CalDAV client integration in pod-calendar, with no user-visible change for existing deployments. - Add @hcengineering/safe-fetch, an SSRF-guarded fetch for services that request user-supplied URLs: DNS resolution with blocked private and reserved ranges, pinned connections, per-hop redirect validation, Authorization stripping across origins, timeouts and body size caps. Unit tested with stubbed DNS and local HTTP servers. - Make the Google Calendar module in pod-calendar optional: the service starts without Credentials and WATCH_URL, logs that Google is disabled, keeps /event available and answers the Google endpoints with 501. - Add the caldav-calendar integration kind and the CalDavCalendar mixin on ExternalCalendar, so provider-specific code can tell CalDAV calendars apart from Google ones. No data migration is needed. - Restrict the Google sync and outbound paths to calendars without that mixin. Signed-off-by: UncleDoomVSSP <uncledoom@pm.me> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Hi @UncleDoomVSSP Should fix before merge1.
|
|
Thanks for the careful review, @ArtyomSavchenko. All eleven points are addressed in the follow-up commit; details below. Should fix
Low severity
Nits
safe-fetch tests: 101 passing (was 87). Full validation of |
Follow-up to review comments on Platform-Collective#49. pod-calendar: - /event returns immediately when the Google module is disabled, instead of taking the workspace lock and looking up Google secrets per event. - GoogleEnabled is excluded from the envMap type; no placeholder entry. - Revert the duplicate blank-value check in getGoogleClient. safe-fetch: - Pinned addresses are reference-counted and released once a request has connected, so the map no longer grows with the hosts contacted. - A Request input contributes method, headers, body and signal; init wins. - Body limiting skips HEAD and null-body statuses, wraps the limited stream in a native Response so formData() and clone() work, and keeps url and redirected. Redirects without Location are returned readable. - A timeout during the body read surfaces as SafeFetchError('TIMEOUT'). - allowlist and blockedRanges are validated once in createSafeFetch. - Block 64:ff9b:1::/48 and check the IPv4-translated ::ffff:0:a.b.c.d form through its embedded address. - Drop Proxy-Authorization on cross-origin redirects. - Tests for each change; 101 in total. models/calendar: - Distinct embedded labels for the CalDavCalendar mixin fields. Signed-off-by: UncleDoomVSSP <uncledoom@pm.me> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: UncleDoomVSSP <uncledoom@pm.me> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
First of four PRs adding a CalDAV client integration to the calendar service, so users can sync calendars from Nextcloud, iCloud, Fastmail, Radicale and Baikal alongside Google Calendar. This PR is groundwork only: it contains no CalDAV behaviour and no user-visible change for existing deployments.
Planned sequence:
CalendarEventCUDqueue topic, with reconciliation.Changes
packages/safe-fetch(@hcengineering/safe-fetch): afetchwrapper for services that request user-supplied URLs. It resolves every DNS record and rejects private, loopback, link-local, CGNAT, multicast and reserved ranges in both IP families, pins the socket to the checked address so DNS rebinding cannot redirect the connection, validates every redirect hop, dropsAuthorizationandCookieacross origins, switches to GET where the HTTP specification requires it, and enforces a timeout and a body size cap. Operators can allowlist hosts or CIDR ranges for servers on private networks. Dependencies:undiciandipaddr.js, both MIT. 87 unit tests, including end-to-end runs against local HTTP servers with an injected resolver.pod-calendar: Google module becomes optional.CredentialsandWATCH_URLare no longer required at startup. When either is missing or blank, the service logs that Google is disabled, skips the push handler, watch controller and calendar controller, keeps/eventavailable, and answers/signinand/signoutwith501 { error: 'google-disabled' }. Deployments that set both variables see no change.plugins/calendar,models/calendar: new integration kindcaldav-calendar(distinct from the existingcaldavkind, which is the platform acting as a CalDAV server) and aCalDavCalendarmixin onExternalCalendarwith hiddenaccountKey,hrefandctagfields. Google calendars never carry the mixin, so no migration is needed.ExternalCalendar:IncomingSyncManager.getMyCalendars,WorkspaceClient.init, andgetTokenByEventin the outbound client. Each now ignores calendars carrying the mixin.rush.jsonentry for the new package and the matching lockfile update.Verification
rush validate --to @hcengineering/pod-calendar --to @hcengineering/model-calendarthrough the full upstream chain.rushx _phase:validateforsafe-fetchrun twice, to confirm the incremental pass is stable.jestforsafe-fetch: 87 passed. ESLint clean on every touched file. Formatting viarush fast-format.--config auto) on the 24 changed files, both clean.Notes for reviewers
safe-fetchare namedsafe-url.tsandsafe-fetch-types.tsrather thanurl.tsandtypes.tson purpose. The rig's validate step adds a package's owntypes/directory to the compiler type roots, and@types/nodeimports the bare specifierurl, so a file namedurl.tsmakes the second validate pass fail with TS5055.develop; the formatter rewrapped only the lines this PR added.AGENTS.md.🤖 Generated with Claude Code