Repository navigation
cap transitions of a VTIMEZONE RRULE - #2047
shamsManna wants to merge 5 commits into
Conversation
Timezones created from a VTIMEZONE expand its RRULE from DTSTART: the pytz provider lists every transition up to 2038 while parsing, and dateutil's tzical walks all transitions before a date when the zoneinfo provider computes an offset. A VTIMEZONE with RRULE:FREQ=MINUTELY took 36 s and 1.6 GB for one utcoffset() call, FREQ=SECONDLY exhausts memory. Reject an RRULE with more than MAX_TIMEZONE_TRANSITIONS_PER_YEAR (100) transitions in the year after its first transition, mirroring MAX_ALARM_REPEAT. -1 disables the cap. AI use: Claude Opus 5.5 (claude-opus-5-5). Used to search the code base for unbounded expansion of untrusted input, measure the VTIMEZONE RRULE case, and draft the fix, tests and change log entry, which I reviewed. Output: (this commit)
|
Thanks for the pull request, @shamsManna! A maintainer will review it shortly. |
Mixed activityActivity patterns show a mix of organic and automated signals. Evidence
Last 5 PRs:
This is an automated analysis by AgentScan |
|
One or more PR-quality checks did not pass. Review the PR Quality Checks summary in the workflow run for the failed checks and their details, then make any necessary updates. Checklist items may remain unchecked while work is in progress. Please tick each item as you complete it. If you used AI assistance, follow the project's Responsible AI use policy and provide the required disclosures. |
angatha
left a comment
There was a problem hiding this comment.
Technically fine. I pass this to @stevepiercy for the few doc strings.
stevepiercy
left a comment
There was a problem hiding this comment.
This looks solid. I have minor tweaks to the docstrings and news fragment, otherwise it's ready to merge. Thank you for your contribution, @shamsManna!
| """ | ||
|
|
||
| MAX_TIMEZONE_TRANSITIONS_PER_YEAR: int = 100 | ||
| """Cap on transitions a ``VTIMEZONE`` ``RRULE`` may produce within a year. |
There was a problem hiding this comment.
It's not necessary to make RFC components into inline literals. We're working on that throughout the docstrings, as the previous one shows we have a lot more work ahead of us. 😉
| """Cap on transitions a ``VTIMEZONE`` ``RRULE`` may produce within a year. | |
| """Cap the transitions that a VTIMEZONE RRULE may produce within a year. |
There was a problem hiding this comment.
this one is moot now: with the FREQ=YEARLY check from the latest review the config option is gone.
| A ``STANDARD`` or ``DAYLIGHT`` component of a ``VTIMEZONE`` usually repeats yearly. | ||
| Timezones created from a ``VTIMEZONE`` expand its ``RRULE`` from ``DTSTART``, so | ||
| an ``RRULE`` such as ``FREQ=SECONDLY`` can exhaust memory or CPU when a date | ||
| in this timezone is used. | ||
| A ``VTIMEZONE`` whose ``RRULE`` produces more transitions within a year is invalid. | ||
| Set to ``-1`` to disable the cap, which should be done only for fully trusted input. |
There was a problem hiding this comment.
Same, but keep the values of components as inline literals.
| A ``STANDARD`` or ``DAYLIGHT`` component of a ``VTIMEZONE`` usually repeats yearly. | |
| Timezones created from a ``VTIMEZONE`` expand its ``RRULE`` from ``DTSTART``, so | |
| an ``RRULE`` such as ``FREQ=SECONDLY`` can exhaust memory or CPU when a date | |
| in this timezone is used. | |
| A ``VTIMEZONE`` whose ``RRULE`` produces more transitions within a year is invalid. | |
| Set to ``-1`` to disable the cap, which should be done only for fully trusted input. | |
| A STANDARD or DAYLIGHT component of a VTIMEZONE usually repeats yearly. | |
| Timezones created from a VTIMEZONE expand its RRULE from DTSTART, so | |
| an RRULE such as ``FREQ=SECONDLY`` can exhaust memory or CPU when a date | |
| in this timezone is used. | |
| A VTIMEZONE whose RRULE produces more than the given number of transitions | |
| within a year is invalid. Set to ``-1`` to disable the cap, which should be | |
| done only for fully trusted input. |
There was a problem hiding this comment.
same here, MAX_TIMEZONE_TRANSITIONS_PER_YEAR was removed along with the transition count.
| """Check that the RRULEs of the timezone components are not too frequent. | ||
|
|
||
| Raises: | ||
| ValueError: If an ``RRULE`` produces more than |
There was a problem hiding this comment.
No need to make RFC components into inline literals.
| ValueError: If an ``RRULE`` produces more than | |
| ValueError: If an RRULE produces more than |
There was a problem hiding this comment.
the docstring got rewritten for the FREQ=YEARLY check. I kept your style there: no literals for RRULE, VTIMEZONE or DTSTART, and YEARLY and FREQ=SECONDLY as literals.
| @@ -0,0 +1 @@ | |||
| Reject a ``VTIMEZONE`` whose ``RRULE`` produces more than :data:`~icalendar.config.MAX_TIMEZONE_TRANSITIONS_PER_YEAR` transitions within a year, ``100`` by default. Timezones created from a ``VTIMEZONE`` expand its ``RRULE`` from ``DTSTART``, so a calendar with an ``RRULE`` such as ``FREQ=SECONDLY`` in a ``VTIMEZONE`` could exhaust memory and CPU when it was parsed or when a date in that timezone was used. I used AI to assist me with this change. @shamsManna | |||
There was a problem hiding this comment.
No need to quote RFC components as inline literals, but values should be.
| Reject a ``VTIMEZONE`` whose ``RRULE`` produces more than :data:`~icalendar.config.MAX_TIMEZONE_TRANSITIONS_PER_YEAR` transitions within a year, ``100`` by default. Timezones created from a ``VTIMEZONE`` expand its ``RRULE`` from ``DTSTART``, so a calendar with an ``RRULE`` such as ``FREQ=SECONDLY`` in a ``VTIMEZONE`` could exhaust memory and CPU when it was parsed or when a date in that timezone was used. I used AI to assist me with this change. @shamsManna | |
| Reject a VTIMEZONE whose RRULE produces more than :data:`~icalendar.config.MAX_TIMEZONE_TRANSITIONS_PER_YEAR` transitions within a year, ``100`` by default. Timezones created from a VTIMEZONE expand its RRULE from DTSTART, so a calendar with an RRULE such as ``FREQ=SECONDLY`` in a VTIMEZONE could exhaust memory and CPU when it was parsed or when a date in that timezone was used. I used AI to assist me with this change. @shamsManna |
There was a problem hiding this comment.
rewrote it for the FREQ=YEARLY check, following your suggestion: RFC components without literals, FREQ=YEARLY and FREQ=SECONDLY as values.
niccokunzmann
left a comment
There was a problem hiding this comment.
Timezones should only have a yearly RRULE.
Any other frequency can raise an error.
If you believe otherwise, please prove so by showing an example from the timezone database.
As such, any complicated calculations are not needed. We can raise an error when the frequency is not yearly.
Also what should be checked is that this works in iCal, jCal and xCal.
| assert timezone.to_tz(tzp, lookup_tzid=False) is not None | ||
|
|
||
|
|
||
| PR_EXAMPLE = """BEGIN:VCALENDAR |
There was a problem hiding this comment.
Such examples MUST be moved into a .ics file.
There was a problem hiding this comment.
moved it to calendars/pr_2047_vtimezone_secondly_rrule_ical.ics, with the same calendar as _jcal.jcal and _xcal.xml next to it. the other frequencies now start from timezones/pacific_fiji.ics and swap in the RRULE instead of building a VTIMEZONE from a string.
Replace the per-year transition count and MAX_TIMEZONE_TRANSITIONS_PER_YEAR with a check that every RRULE of a STANDARD or DAYLIGHT component has FREQ=YEARLY. Move the test calendar into .ics, .jcal and .xml files and check the timezone is rejected in all three formats.
|
switched to that. the example is now in .ics, .jcal and .xml files, and the tests check all three are rejected through one case the FREQ check doesn't cover is a YEARLY rule with BYHOUR/BYMINUTE/BYSECOND lists. |
Linked issue
No issue opened yet. Related to the
VALARMREPEATcap from GHSA-qjcq-q7h7-r74v, which this mirrors.Description
VTIMEZONEexpand itsRRULEfromDTSTARTwith no bound. the pytz provider lists every transition up to 2038 insideCalendar.from_ical(FREQ=HOURLYfrom 1970: 1.7 s, 245 MB at parse). with the default zoneinfo provider, dateutil'stzicalwalks all transitions before a date on each offset lookup (FREQ=MINUTELY: 36 s and 1.6 GB for a singleevent.DTSTART.utcoffset()).FREQ=SECONDLYexhausts memory, so a ~400 byte calendar is enough to take down whatever parses it and reads an event date.Timezone.to_tzandTimezone.get_transitionsnow reject anRRULEthat doesn't haveFREQ=YEARLY, raising the existingInvalid iCalendar VTIMEZONEValueError.real-world
VTIMEZONErules repeat yearly, and the full test suite passes on both providers.Repro:
Calendar.from_ical(ics).events[0].DTSTART.utcoffset()Checklist