From 6f0c9372362ce9150c384a0d7a4c0910901338d4 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Fri, 26 Jan 2018 14:24:54 -0500 Subject: [PATCH] don't allow distant DTSTART values for schedules; it's slow see: https://github.com/ansible/ansible-tower/issues/7869 --- awx/api/serializers.py | 4 ++-- awx/main/models/schedules.py | 9 +++++++++ awx/main/tests/functional/api/test_schedules.py | 5 +++-- awx/main/tests/functional/models/test_schedule.py | 13 +++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 0acdd2b34b..d1cdcd8a90 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -3927,8 +3927,8 @@ class SchedulePreviewSerializer(BaseSerializer): raise serializers.ValidationError(_("COUNT > 999 is unsupported.")) try: Schedule.rrulestr(rrule_value) - except Exception: - raise serializers.ValidationError(_("rrule parsing failed validation.")) + except Exception as e: + raise serializers.ValidationError(_("rrule parsing failed validation: {}").format(e)) return value diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index fcfc3ce88f..3dc9ac2814 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -166,6 +166,15 @@ class Schedule(CommonModel, LaunchTimeConfig): rrule = rrule.replace(match_until.group('until'), 'UNTIL={}'.format(utc)) kwargs['tzinfos']['TZI'] = timezone x = dateutil.rrule.rrulestr(rrule, **kwargs) + + try: + first_event = x[0] + if first_event < now() - datetime.timedelta(days=365 * 5): + # For older DTSTART values, if there are more than 1000 recurrences... + if len(x[:1001]) > 1000: + raise ValueError('RRULE values that yield more than 1000 events are not allowed.') + except IndexError: + pass return x def __unicode__(self): diff --git a/awx/main/tests/functional/api/test_schedules.py b/awx/main/tests/functional/api/test_schedules.py index f63bf37c65..c6cfc5d91a 100644 --- a/awx/main/tests/functional/api/test_schedules.py +++ b/awx/main/tests/functional/api/test_schedules.py @@ -59,9 +59,10 @@ def test_valid_survey_answer(post, admin_user, project, inventory, survey_spec_f ("DTSTART:20300308T050000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYYEARDAY=100", "BYYEARDAY not supported"), # noqa ("DTSTART:20300308T050000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYWEEKNO=20", "BYWEEKNO not supported"), ("DTSTART:20300308T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2000", "COUNT > 999 is unsupported"), # noqa - ("DTSTART:20300308T050000Z RRULE:FREQ=REGULARLY;INTERVAL=1", "rrule parsing failed validation"), # noqa - ("DTSTART;TZID=America/New_York:30200308T050000Z RRULE:FREQ=DAILY;INTERVAL=1", "rrule parsing failed validation"), + ("DTSTART:20300308T050000Z RRULE:FREQ=REGULARLY;INTERVAL=1", "rrule parsing failed validation: invalid 'FREQ': REGULARLY"), # noqa + ("DTSTART;TZID=America/New_York:20300308T050000Z RRULE:FREQ=DAILY;INTERVAL=1", "rrule parsing failed validation"), ("DTSTART:20300308T050000 RRULE:FREQ=DAILY;INTERVAL=1", "DTSTART cannot be a naive datetime"), + ("DTSTART:19700101T000000Z RRULE:FREQ=MINUTELY;INTERVAL=1", "more than 1000 events are not allowed"), # noqa ]) def test_invalid_rrules(post, admin_user, project, inventory, rrule, error): job_template = JobTemplate.objects.create( diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py index 2df0e479e3..c4c6019503 100644 --- a/awx/main/tests/functional/models/test_schedule.py +++ b/awx/main/tests/functional/models/test_schedule.py @@ -192,3 +192,16 @@ def test_dst_phantom_hour(job_template): # 3/10/30 @ 2:30AM is skipped because it _doesn't exist_ assert str(s.next_run) == '2030-03-17 06:30:00+00:00' + + +@pytest.mark.django_db +def test_beginning_of_time(job_template): + # ensure that really large generators don't have performance issues + rrule = 'DTSTART:19700101T000000Z RRULE:FREQ=MINUTELY;INTERVAL=1' + s = Schedule( + name='Some Schedule', + rrule=rrule, + unified_job_template=job_template + ) + with pytest.raises(ValueError): + s.save()