Summary
Deeply nested iCalendar components can be parsed successfully, but formatting the resulting object with repr(), str(), or an f-string can raise RecursionError.
The recursive path appears to be Component.__repr__():
# src/icalendar/cal/component.py
def __repr__(self):
"""String representation of class with all of it's subcomponents."""
subs = ", ".join(str(it) for it in self.subcomponents)
return f"{self.name or type(self).__name__}({dict(self)}{', ' + subs if subs else ''})"
This can cause a denial-of-service in applications that parse untrusted .ics files and later log or format the parsed calendar object.
Reproducer
import icalendar
ics = (
"BEGIN:VCALENDAR\r\n"
"VERSION:2.0\r\n"
"PRODID:-//x//x//EN\r\n"
+ "BEGIN:VEVENT\r\n" * 500
+ "UID:t@e\r\n"
"DTSTAMP:20260101T000000Z\r\n"
+ "END:VEVENT\r\n" * 500
+ "END:VCALENDAR\r\n"
)
cal = icalendar.Calendar.from_ical(ics) # succeeds
str(cal) # RecursionError
Observed behavior
| Operation |
Result |
Calendar.from_ical(ics) |
succeeds |
repr(cal) / str(cal) / f"{cal}" |
raises RecursionError |
cal.to_ical() |
succeeds |
cal.walk() |
succeeds |
The payload is small, around 13 KB.
Impact
This is a denial-of-service / robustness issue. Example scenarios:
- a CalDAV server or calendar import service logs a parsed calendar object;
- an email invitation parser includes the object in diagnostics;
- error reporting formats local variables containing the parsed calendar.
The crash is not in parsing itself, but in later diagnostic/logging paths.
Possible fixes
Any of these should mitigate the issue:
- Reject excessive nesting during parsing, for example with a component nesting-depth limit.
- Make
__repr__ iterative or depth-limited.
- Truncate subcomponent output in
__repr__ after a safe depth.
A parser-level limit would prevent this class of issue more generally, but a bounded __repr__ may be less invasive if compatibility is a concern.
Summary
Deeply nested iCalendar components can be parsed successfully, but formatting the resulting object with
repr(),str(), or an f-string can raiseRecursionError.The recursive path appears to be
Component.__repr__():This can cause a denial-of-service in applications that parse untrusted
.icsfiles and later log or format the parsed calendar object.Reproducer
Observed behavior
Calendar.from_ical(ics)repr(cal)/str(cal)/f"{cal}"RecursionErrorcal.to_ical()cal.walk()The payload is small, around 13 KB.
Impact
This is a denial-of-service / robustness issue. Example scenarios:
The crash is not in parsing itself, but in later diagnostic/logging paths.
Possible fixes
Any of these should mitigate the issue:
__repr__iterative or depth-limited.__repr__after a safe depth.A parser-level limit would prevent this class of issue more generally, but a bounded
__repr__may be less invasive if compatibility is a concern.