Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/interval-validation.bugfix
Original file line number Diff line number Diff line change
@@ -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.
20 changes: 19 additions & 1 deletion src/plone/formwidget/recurrence/browser/json_recurrence.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down