diff --git a/news/interval-validation.bugfix b/news/interval-validation.bugfix new file mode 100644 index 0000000..5bf7b91 --- /dev/null +++ b/news/interval-validation.bugfix @@ -0,0 +1 @@ +Prevent infinite loop when INTERVAL=0 is used in recurrence rules by adding server-side validation. INTERVAL=0 is invalid per RFC 5545 and now returns a proper error message instead of causing the server to hang. diff --git a/src/plone/formwidget/recurrence/browser/json_recurrence.py b/src/plone/formwidget/recurrence/browser/json_recurrence.py index 7c81271..a859a27 100644 --- a/src/plone/formwidget/recurrence/browser/json_recurrence.py +++ b/src/plone/formwidget/recurrence/browser/json_recurrence.py @@ -6,6 +6,7 @@ from plone.base.i18nl10n import name_formatvariables from plone.base.i18nl10n import weekdayname_msgid from plone.base.i18nl10n import weekdayname_msgid_abbr +from plone.formwidget.recurrence import _ from Products.Five import BrowserView from zope.i18n import interpolate from zope.i18n import translate @@ -65,7 +66,24 @@ def json_string(self): start_date = datetime.datetime( int(data["year"]), int(data["month"]), int(data["day"]) ) - rule = rrule.rrulestr(data["rrule"], dtstart=start_date) + # Validate the RRULE string to prevent invalid INTERVAL values + # INTERVAL=0 is invalid per RFC5545 and causes an infinite loop in + # python-dateutil when iterating the rule. Reject it early with a + # user-friendly, translated error message. + rrule_str = data["rrule"] + m = re.search(r"(?i)(?:^|;)INTERVAL\s*=\s*([-+]?[0-9]+)(?:;|$)", rrule_str) + if m: + try: + interval_val = int(m.group(1)) + except Exception: + interval_val = None + if interval_val is not None and interval_val <= 0: + # Bad request: INTERVAL must be a positive integer + self.request.response.setStatus(400) + msg = translate(_("no_repeat_every"), context=self.request) + return {"error": msg} + + rule = rrule.rrulestr(rrule_str, dtstart=start_date) iterator = iter(rule) if "batch_size" in data: