From b331bb3e0eb66e9d671ebf0cae90a5b8a8d3db0e Mon Sep 17 00:00:00 2001 From: Peter Date: Sat, 8 Dec 2018 06:36:29 +0100 Subject: [PATCH] Fix divison-by-zero when adding requirement to dynamic challenge (#782) * Fixing a bug where prerequisites could not be set for dynamic challenges due to a division by zero error where defaults were being set unnecessarily. * Creating unit test for adding requirement to dynamic challenges --- CTFd/plugins/dynamic_challenges/__init__.py | 13 ++--- tests/challenges/test_dynamic.py | 57 +++++++++++++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/CTFd/plugins/dynamic_challenges/__init__.py b/CTFd/plugins/dynamic_challenges/__init__.py index 05c76914..a7dce7c9 100644 --- a/CTFd/plugins/dynamic_challenges/__init__.py +++ b/CTFd/plugins/dynamic_challenges/__init__.py @@ -87,10 +87,11 @@ class DynamicValueChallenge(BaseChallenge): :return: """ data = request.form or request.get_json() - data['initial'] = float(data.get('initial', 0)) - data['minimum'] = float(data.get('minimum', 0)) - data['decay'] = float(data.get('decay', 0)) + for attr, value in data.items(): + # We need to set these to floats so that the next operations don't operate on strings + if attr in ('initial', 'minimum', 'decay'): + value = float(value) setattr(challenge, attr, value) Model = get_model() @@ -228,9 +229,9 @@ class DynamicValueChallenge(BaseChallenge): class DynamicChallenge(Challenges): __mapper_args__ = {'polymorphic_identity': 'dynamic'} id = db.Column(None, db.ForeignKey('challenges.id'), primary_key=True) - initial = db.Column(db.Integer) - minimum = db.Column(db.Integer) - decay = db.Column(db.Integer) + initial = db.Column(db.Integer, default=0) + minimum = db.Column(db.Integer, default=0) + decay = db.Column(db.Integer, default=0) def __init__(self, *args, **kwargs): super(DynamicChallenge, self).__init__(**kwargs) diff --git a/tests/challenges/test_dynamic.py b/tests/challenges/test_dynamic.py index f3135fec..b02691e2 100644 --- a/tests/challenges/test_dynamic.py +++ b/tests/challenges/test_dynamic.py @@ -85,6 +85,63 @@ def test_can_update_dynamic_challenge(): destroy_ctfd(app) +def test_can_add_requirement_dynamic_challenge(): + """Test that requirements can be added to dynamic challenges""" + app = create_ctfd(enable_plugins=True) + with app.app_context(): + challenge_data = { + "name": "name", + "category": "category", + "description": "description", + "value": 100, + "decay": 20, + "minimum": 1, + "state": "hidden", + "type": "dynamic" + } + req = FakeRequest(form=challenge_data) + challenge = DynamicValueChallenge.create(req) + + assert challenge.value == 100 + assert challenge.initial == 100 + assert challenge.decay == 20 + assert challenge.minimum == 1 + + challenge_data = { + "name": "second_name", + "category": "category", + "description": "new_description", + "value": "200", + "initial": "200", + "decay": "40", + "minimum": "5", + "max_attempts": "0", + "state": "visible" + } + + req = FakeRequest(form=challenge_data) + challenge = DynamicValueChallenge.create(req) + + assert challenge.name == 'second_name' + assert challenge.description == "new_description" + assert challenge.value == 200 + assert challenge.initial == 200 + assert challenge.decay == 40 + assert challenge.minimum == 5 + assert challenge.state == "visible" + + challenge_data = { + "requirements": [1] + } + + req = FakeRequest(form=challenge_data) + challenge = DynamicValueChallenge.update(challenge, req) + + assert challenge.requirements == [1] + + destroy_ctfd(app) + + def test_can_delete_dynamic_challenge(): app = create_ctfd(enable_plugins=True) with app.app_context():