max_vevent_rrule_occurrence: improve pre-check
This commit is contained in:
@@ -247,51 +247,51 @@ def check_and_sanitize_items(
|
||||
if hasattr(component, "rrule"):
|
||||
# workaround for vobject < 1.0.0 as it has no limiter in "getrruleset"
|
||||
logger.trace("Recurrence rule found in %s in object %r: %r", component.name, component_uid, component.rrule.value)
|
||||
# early check of maximum of COUNT to avoid DoS (semi-ugly workaround)
|
||||
pattern = re.compile('.*;COUNT=(\\d+)(;.*)?$')
|
||||
match = pattern.match(component.rrule.value)
|
||||
if match:
|
||||
rrule_count = int(match[1])
|
||||
if max_vevent_rrule_occurrence > 0 and rrule_count > max_vevent_rrule_occurrence:
|
||||
logger.error("Recurrence rule %r count in %s in object %r: %d (REJECTED/limit: %d)", component.rrule.value, component.name, component_uid, rrule_count, max_vevent_rrule_occurrence)
|
||||
raise ValueError("Too many recurrence rule entries in %s in object %r: %d (limit: %d)"
|
||||
% (component.name, component_uid, rrule_count, max_vevent_rrule_occurrence))
|
||||
if not hasattr(component, "dtstart"):
|
||||
# e.g. VTODO
|
||||
rrule = vobject.icalendar.rrule.rrulestr(component.rrule.value)
|
||||
else:
|
||||
logger.debug("Recurrence rule count in %s in object %r: %d (PASSED/limit: %d)" % (component.name, component_uid, rrule_count, max_vevent_rrule_occurrence))
|
||||
if hasattr(component, "dtstart"):
|
||||
# early check of maximum of (UNTIL-DTSTART)/interval(FREQ) to avoid DoS (ugly workaround with some guessing)
|
||||
pattern = re.compile('FREQ=([A-Z]+)(;.*)?$')
|
||||
match = pattern.match(component.rrule.value)
|
||||
if match and match[1] in RRULE_FREQUENCIES_TO_INTERVAL:
|
||||
# RRULE has known FREQ
|
||||
freq = match[1]
|
||||
pattern = re.compile('.*;UNTIL=([\\dTZ]+)(;.*)?$')
|
||||
match = pattern.match(component.rrule.value)
|
||||
dtstart = radicale_filter.date_to_datetime(component.dtstart.value)
|
||||
if match:
|
||||
# RRULE has UNTIL
|
||||
ignoretz = (
|
||||
not isinstance(dtstart, datetime.datetime)
|
||||
or dtstart.tzinfo is None
|
||||
)
|
||||
until = vobject.icalendar.rrule.rrulestr(component.rrule.value, ignoretz=ignoretz)._until
|
||||
rrule = vobject.icalendar.rrule.rrulestr(component.rrule.value, ignoretz=ignoretz)
|
||||
# early check of maximum of COUNT to avoid DoS (workaround)
|
||||
if hasattr(rrule, "_count") and rrule._count is not None:
|
||||
logger.trace("Recurrence rule %r in %s in object %r contains: COUNT=%d", component.rrule.value, component.name, component_uid, rrule._count)
|
||||
if max_vevent_rrule_occurrence > 0 and rrule._count > max_vevent_rrule_occurrence:
|
||||
logger.error("Recurrence rule %r count in %s in object %r: %d (REJECTED/limit: %d)", component.rrule.value, component.name, component_uid, rrule._count, max_vevent_rrule_occurrence)
|
||||
raise ValueError("Too many recurrence rule entries in %s in object %r: %d (limit: %d)"
|
||||
% (component.name, component_uid, rrule._count, max_vevent_rrule_occurrence))
|
||||
else:
|
||||
logger.debug("Recurrence rule count in %s in object %r: %d (PASSED/limit: %d)" % (component.name, component_uid, rrule._count, max_vevent_rrule_occurrence))
|
||||
else:
|
||||
logger.trace("Recurrence rule %r in %s in object %r doesn't contain: COUNT", component.rrule.value, component.name, component_uid)
|
||||
# early check of maximum of (UNTIL-DTSTART)/interval(FREQ) to avoid DoS (ugly workaround with some guessing)
|
||||
if hasattr(rrule, "_freq") and rrule._freq is not None and hasattr(rrule, "_until") and rrule._until is not None and hasattr(component, "dtstart"):
|
||||
if vobject.icalendar.FREQUENCIES[rrule._freq] not in RRULE_FREQUENCIES_TO_INTERVAL:
|
||||
raise ValueError("Unsupported FREQ in recurrence rule in %s in object %r: %r"
|
||||
% (component.name, component_uid, rrule._freq))
|
||||
# RRULE has known FREQ+UNTIL+DTSTART
|
||||
if dtstart.tzinfo is not None:
|
||||
until = until.astimezone(dtstart.tzinfo)
|
||||
delta = until - dtstart
|
||||
rrule._until = rrule._until.astimezone(dtstart.tzinfo)
|
||||
delta = rrule._until - dtstart
|
||||
seconds = delta.total_seconds()
|
||||
if seconds < 0:
|
||||
# UNTIL < DTSTART
|
||||
logger.error("Recurrence rule %r in %s in object %r REJECTED, UNTIL < DTSTART", component.rrule.value, component.name, component_uid)
|
||||
raise ValueError("Recurrence rule in %s in object %r has UNTIL < DTSTART"
|
||||
% (component.name, component_uid))
|
||||
rrule_entries = seconds / RRULE_FREQUENCIES_TO_INTERVAL[freq]
|
||||
logger.trace("estimated rule entries: %d", rrule_entries)
|
||||
rrule_entries = seconds / RRULE_FREQUENCIES_TO_INTERVAL[vobject.icalendar.FREQUENCIES[rrule._freq]]
|
||||
if max_vevent_rrule_occurrence > 0 and rrule_entries > max_vevent_rrule_occurrence:
|
||||
logger.warning("Recurrence rule %r entries in %s in object %r: %d (estimated/REJECTED/limit: %d)", component.rrule.value, component.name, component_uid, rrule_entries, max_vevent_rrule_occurrence)
|
||||
raise ValueError("Too many recurrence rule entries in %s in object %r: %d (limit: %d)"
|
||||
% (component.name, component_uid, rrule_entries, max_vevent_rrule_occurrence))
|
||||
else:
|
||||
logger.debug("Recurrence rule %r entries in %s in object %r: %d (estimated/PASSED/limit: %d)" % (component.rrule.value, component.name, component_uid, rrule_entries, max_vevent_rrule_occurrence))
|
||||
else:
|
||||
logger.trace("Recurrence rule %r in %s in object %r doesn't contain: FREQ+UNTIL", component.rrule.value, component.name, component_uid)
|
||||
# generic check by vobject
|
||||
try:
|
||||
rruleset = component.rruleset
|
||||
|
||||
Reference in New Issue
Block a user