diff --git a/backend/calendar.py b/backend/calendar.py index fe623f2..c2ef286 100644 --- a/backend/calendar.py +++ b/backend/calendar.py @@ -18,6 +18,8 @@ from icalendar import Calendar MAX_ICS_BYTES = 2_000_000 MAX_REDIRECTS = 3 DEFAULT_RECURRENCE_LIMIT = 10_000 +MAX_DESCRIPTION_LENGTH = 2_000 +MAX_LOCATION_LENGTH = 500 TIMEOUT_SECONDS = 10 _ALLOWED_CONTENT_TYPES = {"text/calendar", "text/plain", "application/octet-stream"} @@ -159,9 +161,13 @@ def _overlaps(start: datetime, end: datetime, window_start: datetime, window_end def _event_dict(event: Any, source_id: str, source: str, color: str, start: datetime, end: datetime, all_day: bool) -> dict: uid = str(event.get("uid") or "") title = str(event.get("summary") or "Untitled event").strip() or "Untitled event" + description = str(event.get("description") or "").strip()[:MAX_DESCRIPTION_LENGTH] + location = str(event.get("location") or "").strip()[:MAX_LOCATION_LENGTH] return { "id": f"{uid or title}:{start.isoformat()}", "title": title, + "description": description or None, + "location": location or None, "starts_at": start, "ends_at": end, "all_day": all_day, diff --git a/tests/test_calendar_subscriptions.py b/tests/test_calendar_subscriptions.py index a8219b5..1984f77 100644 --- a/tests/test_calendar_subscriptions.py +++ b/tests/test_calendar_subscriptions.py @@ -17,6 +17,8 @@ UID:one\r DTSTART:20260920T090000Z\r DTEND:20260920T100000Z\r SUMMARY:Meeting\r +DESCRIPTION:Body line one\\nBody line two\r +LOCATION:Meeting room\r END:VEVENT\r END:VCALENDAR\r """ @@ -148,6 +150,8 @@ def test_subscription_crud_refresh_events_and_stale_cache(client, monkeypatch): ) assert events.status_code == 200 assert events.json()["events"][0]["title"] == "Meeting" + assert events.json()["events"][0]["description"] == "Body line one\nBody line two" + assert events.json()["events"][0]["location"] == "Meeting room" assert events.json()["sources"][0]["stale"] is False refreshed = client.post(f"/api/v1/calendar-subscriptions/{body['id']}/refresh") @@ -166,6 +170,26 @@ def test_subscription_crud_refresh_events_and_stale_cache(client, monkeypatch): assert client.delete(f"/api/v1/calendar-subscriptions/{body['id']}").status_code == 204 +def test_parser_bounds_large_event_text_fields(): + oversized = ICS.replace( + b"DESCRIPTION:Body line one\\nBody line two", + b"DESCRIPTION:" + b"x" * 3_000, + ).replace( + b"LOCATION:Meeting room", + b"LOCATION:" + b"y" * 1_000, + ) + event = parse_ics_events( + oversized, + "Work", + "#123456", + datetime(2026, 9, 20, tzinfo=UTC), + datetime(2026, 9, 21, tzinfo=UTC), + "UTC", + )[0] + assert event["description"] == "x" * 2_000 + assert event["location"] == "y" * 500 + + def test_events_validate_window_and_disabled_sources_are_skipped(client, monkeypatch): client = initialized(client) monkeypatch.setattr(