diff --git a/netbox/extras/conditions.py b/netbox/extras/conditions.py index 6e764209b..11098fe08 100644 --- a/netbox/extras/conditions.py +++ b/netbox/extras/conditions.py @@ -130,20 +130,19 @@ class ConditionSet: :param ruleset: A dictionary mapping a logical operator to a list of conditional rules """ def __init__(self, ruleset): + if type(ruleset) is not dict: raise ValueError(f"Ruleset must be a dictionary, not {type(ruleset)}.") - if len(ruleset) < 1: - raise ValueError(f"Ruleset must have exactly one logical operator (found {len(ruleset)})") self.logic = None # If logic type use it, else return the ruleset if len(ruleset) == 1: logic = list(ruleset.keys())[0] - if logic.lower() in (AND, OR): - self.logic = logic.lower() - else: - raise ValueError(f"Invalid logic type: {logic} (must be '{AND}' or '{OR}')") + if logic not in (AND, OR): + raise ValueError( + f"Invalid logic type: {logic} (must be '{AND}' or '{OR}'). Please check documentation.") + self.logic = logic.lower() # Compile the set of Conditions self.conditions = [ @@ -151,7 +150,10 @@ class ConditionSet: for rule in ruleset[self.logic] ] else: - self.conditions = [Condition(**ruleset)] + try: + self.conditions = [Condition(**ruleset)] + except TypeError: + raise ValueError(f"Incorrect key(s) informed. Please check documentation.") def eval(self, data): """ diff --git a/netbox/extras/tests/test_event_rules.py b/netbox/extras/tests/test_event_rules.py index 0b8672d8a..74161e0a5 100644 --- a/netbox/extras/tests/test_event_rules.py +++ b/netbox/extras/tests/test_event_rules.py @@ -10,6 +10,7 @@ from django.http import HttpResponse from django.urls import reverse from extras.choices import EventRuleActionChoices, ObjectChangeActionChoices from extras.events import enqueue_object, flush_events, serialize_for_event +from extras.forms import SavedFilterForm, EventRuleForm from extras.models import EventRule, Tag, Webhook from extras.webhooks import generate_signature, send_webhook from requests import Session @@ -442,3 +443,27 @@ class EventRuleTest(APITestCase): # Evaluate the conditions (status NOT in ['planned, 'staging']) self.assertTrue(event_rule.eval_conditions(data)) + + def test_event_rule_conditions_with_incorrect_key_must_return_false(self): + """ + Test Event Rule with incorrect condition (key "foo" is wrong). Must return false. + """ + + ct = ContentType.objects.get(app_label='extras', model='webhook') + site_ct = ContentType.objects.get_for_model(Site) + webhook = Webhook.objects.create(name='Webhook 100', payload_url='http://example.com/?1', http_method='POST') + form = EventRuleForm({ + "name": "Event Rule 1", + "type_create": True, + "type_update": True, + "action_object_type": ct.pk, + "action_type": "webhook", + "action_choice": webhook.pk, + "content_types": [site_ct.pk], + "conditions": { + "foo": "status.value", + "value": "active" + } + }) + + self.assertFalse(form.is_valid())