Skip to content

ICS feed validation incorrectly requires .ics URL suffix #29286

@dennisameling

Description

@dennisameling

Issue Summary

ICS feed validation incorrectly rejects valid iCalendar feeds when the URL does not end with .ics.

The current validation logic appears to enforce a .ics suffix:

return "The URL must be a valid ICS URL (ending with .ics)";

@Matches(/\.ics$/)

This causes standards-compliant ICS/CalDAV feeds to be rejected if they are exposed through dynamic URLs or query-parameter-based export endpoints.

For example, the following URL returns a valid iCalendar feed:

https://caldav.soverin.net/calendars/MyCalendar-REDACTED?export

The endpoint responds successfully with:

  • HTTP 200
  • Content-Type: text/calendar
  • valid BEGIN:VCALENDAR content

However, it is rejected solely because the URL does not end with .ics.

The iCalendar and CalDAV specifications do not require ICS feed URLs to end with .ics. What matters is the response content and MIME type, not the URL structure.

Relevant specs:

Steps to Reproduce

  1. Open the Cal.com ICS feed integration flow/API endpoint

  2. Enter a valid ICS feed URL that does not end with .ics

  3. Example:

    https://caldav.soverin.net/calendars/MyCalendar-REDACTED?export
    
  4. Attempt to save/create the ICS feed

Additional context:

  • The endpoint returns a valid iCalendar payload
  • The response MIME type is text/calendar
  • The feed works correctly in other calendar clients/services

I consider this a bug because the implementation validates the URL shape instead of validating the actual HTTP response and calendar content.

Actual Results

  • The ICS feed is rejected
  • Validation fails because the URL does not end with .ics

Expected Results

  • Any valid iCalendar feed URL should be accepted regardless of URL suffix

  • Validation should be based on:

    • valid URL format
    • successful HTTP response
    • Content-Type: text/calendar
    • optionally presence of BEGIN:VCALENDAR

Technical details

  • Validation logic:

    @Matches(/\.ics$/)
  • Example valid feed:

    https://caldav.soverin.net/calendars/MyCalendar-REDACTED?export
    
  • Response headers:

    Content-Type: text/calendar
  • Node.js version:

    N/A (using cal.com hosted version)
    
  • Browser:

    Firefox, and also tried through the cal.com API
    

Evidence

Tested by:

  • Opening the ICS feed URL directly in the browser
  • Verifying the endpoint returns a valid iCalendar payload
  • Verifying the response Content-Type is text/calendar
  • Verifying the same feed works in other calendar clients/services
  • Attempting to add the feed in Cal.com and observing rejection due to .ics suffix validation

The cal.com UI wasn't very helpful as it only showed a very basic message:

Image

I also ran the curl command which showed explicitly that the missing .ics suffix was the culprit:

% curl --request POST \
  --url https://api.cal.com/v2/calendars/ics-feed/save \
  --header 'Authorization: REDACTED' \
  --header 'Content-Type: application/json' \
  --data '
{
  "urls": [
    "https://REDACTED:REDACTED@caldav.soverin.net/calendars/MyCalendar-REDACTED?export"
  ],
  "readOnly": true
}
'
{"status":"error","timestamp":"2026-05-07T20:01:29.624Z","path":"/v2/calendars/ics-feed/save","error":{"code":"BadRequestException","message":"Bad Request Exception","details":{"errors":[{"target":{"readOnly":true,"urls":["https://REDACTED:REDACTED@caldav.soverin.net/calendars/MyCalendar-REDACTED?export"]},"value":["REDACTED:REDACTED@caldav.soverin.net/calendars/MyCalendar-REDACTED?export"],"property":"urls","children":[],"constraints":{"IsICSUrlConstraint":"The URL must be a valid ICS URL (ending with .ics) pointing to a public host"}}]}}}%  

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions