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