diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3351f7..aec3e5b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.7.8.dev * Fix: sharing/proppatch: reject in case of write-access but 'p' is in permissions * Fix: sharing/by-map: catch collection path without trailing / (supporting "pimsync") +* Add: [report] max_expand_occurrence option to separate from max_freebusy_occurrence ## 3.7.7 * Fix: web plugin helpers httputils.serve_resource/serve_folder ignored their mimetypes and fallback_mimetype parameters and always used the built-in mapping, so custom web plugins could not serve additional file types with a correct Content-Type diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 7bfa9e72..40b5f605 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -2191,6 +2191,19 @@ This is an automated message. Please do not reply. #### [reporting] +##### max_expand_occurrence + +_(>= 3.7.8)_ + +When returning an expanded report, a list of occurrences are +generated based on a given time frame. Large time frames could +generate a lot of occurrences based on the time frame supplied. This +setting limits the lookup to prevent potential denial of service +attacks on large time frames. If the limit is reached, an HTTP error +is thrown instead of returning the results. + +Default: 10000 + ##### max_freebusy_occurrence _(>= 3.2.3)_ diff --git a/config b/config index 2bd39383..9f513301 100644 --- a/config +++ b/config @@ -499,6 +499,10 @@ Content-Security-Policy = default-src 'self'; object-src 'none' [reporting] +# When returning an expanded report, limit the number of returned +# occurences per event to prevent DoS attacks. +#max_expand_occurrence = 10000 + # When returning a free-busy report, limit the number of returned # occurences per event to prevent DoS attacks. #max_freebusy_occurrence = 10000 diff --git a/radicale/app/report.py b/radicale/app/report.py index fab2e445..ed8a147d 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -895,9 +895,9 @@ class ApplicationPartReport(ApplicationBase): assert item.collection is not None collection = item.collection - max_occurrence = self.configuration.get("reporting", "max_freebusy_occurrence") if xml_content is not None and \ xml_content.tag == xmlutils.make_clark("C:free-busy-query"): + max_occurrence = self.configuration.get("reporting", "max_freebusy_occurrence") try: status, body = free_busy_report( base_prefix, path, xml_content, collection, self._encoding, @@ -909,6 +909,7 @@ class ApplicationPartReport(ApplicationBase): headers = {"Content-Type": "text/calendar; charset=%s" % self._encoding} return status, headers, str(body), xmlutils.pretty_xml(xml_content) else: + max_occurrence = self.configuration.get("reporting", "max_expand_occurrence") try: status, xml_answer = xml_report( base_prefix, path, xml_content, collection, self._encoding, diff --git a/radicale/config.py b/radicale/config.py index 9554e4ff..197eb2fc 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -813,9 +813,13 @@ This is an automated message. Please do not reply.""", ("headers", OrderedDict([ ("_allow_extra", str)])), ("reporting", OrderedDict([ + ("max_expand_occurrence", { + "value": "10000", + "help": "number of expand occurrences per event when reporting", + "type": positive_int}), ("max_freebusy_occurrence", { "value": "10000", - "help": "number of occurrences per event when reporting", + "help": "number of free-busy occurrences per event when reporting", "type": positive_int})])) ]) diff --git a/radicale/tests/test_expand.py b/radicale/tests/test_expand.py index d4bf0b72..62178e40 100644 --- a/radicale/tests/test_expand.py +++ b/radicale/tests/test_expand.py @@ -326,7 +326,7 @@ permissions: RrWw""") def test_report_with_expand_property_max_occur(self) -> None: """Test report with expand property too many vevents""" - self.configure({"reporting": {"max_freebusy_occurrence": 100}}) + self.configure({"reporting": {"max_expand_occurrence": 100}}) self._test_expand_max( "event_daily_rrule_forever", "20060103T000000Z", @@ -336,7 +336,7 @@ permissions: RrWw""") def test_report_with_max_occur(self) -> None: """Test report with too many vevents""" - self.configure({"reporting": {"max_freebusy_occurrence": 10}}) + self.configure({"reporting": {"max_expand_occurrence": 10}}) uid = "event_multiple_too_many" start = "20130901T000000Z"