From e7a9f889d182d568a13c3130e01533ce2f64f7e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Falconnier?= Date: Wed, 24 Apr 2024 12:02:28 +0200 Subject: [PATCH] Fix issues with uncommon Santa target IDs - Relax Signing ID verification - Fix Signing ID search - Fix Team ID search - Fix rule creation form --- tests/santa/test_rule_engine.py | 1 + tests/santa/test_setup_views.py | 51 +++++++++++++++++++++++++++++++++ zentral/contrib/santa/forms.py | 12 +++++--- zentral/contrib/santa/models.py | 9 ++++-- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/tests/santa/test_rule_engine.py b/tests/santa/test_rule_engine.py index 1dcbfa4141..3d831a86c4 100644 --- a/tests/santa/test_rule_engine.py +++ b/tests/santa/test_rule_engine.py @@ -111,6 +111,7 @@ def test_sining_id_identifier(self): ("EQHXZ8M8AV:com.google.Chrome", True), ("EQHXZ8M8AV:chrome_crashpad_handler", True), ("EQHXZ8M8AV:not-a-thing", True), + ("94KV3E626L:Frameworks[]Electron Framework", True), ("EQHXZ8M8AV", False)): self.assertEqual(test_signing_id_identifier(identifier), result) diff --git a/tests/santa/test_setup_views.py b/tests/santa/test_setup_views.py index 2237ddee9a..e0a78ad52d 100644 --- a/tests/santa/test_setup_views.py +++ b/tests/santa/test_setup_views.py @@ -2,6 +2,7 @@ from functools import reduce import operator import plistlib +import urllib.parse from accounts.models import User from unittest.mock import patch from django.contrib.auth.models import Group, Permission @@ -611,6 +612,36 @@ def test_create_configuration_team_id_rule_error(self): self.assertTemplateUsed(response, "santa/rule_form.html") self.assertFormError(response.context["form"], "target_identifier", "Invalid Team ID") + def test_create_configuration_team_id_rule_preselected_ok(self): + configuration = self._force_configuration() + self._login("santa.add_rule", "santa.view_rule") + response = self.client.post(reverse("santa:create_configuration_rule", args=(configuration.pk,)) + + "?" + urllib.parse.urlencode({"tea": "JQ525L2MZD"}), + {"policy": Rule.ALLOWLIST}, + follow=True) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "santa/configuration_rules.html") + self.assertContains(response, "JQ525L2MZD") + rule = response.context["object_list"][0] + self.assertEqual(rule.configuration, configuration) + self.assertEqual(rule.target.identifier, "JQ525L2MZD") + self.assertEqual(rule.target.type, Target.TEAM_ID) + + def test_create_configuration_team_id_rule_preselected_error(self): + # This is a test for when a team ID is picked from the list of target + # to create a rule, but … this is not a valid team ID. + # This should never happen! + configuration = self._force_configuration() + self._login("santa.add_rule", "santa.view_rule") + response = self.client.post(reverse("santa:create_configuration_rule", args=(configuration.pk,)) + + "?" + urllib.parse.urlencode({"tea": "NOT_A_TEAM_ID"}), + {"policy": Rule.ALLOWLIST}, + follow=True) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "santa/rule_form.html") + self.assertFormError(response.context["form"], + None, "Invalid Team ID") + def test_create_conflict_configuration_rule(self): self._login("santa.add_configuration", "santa.view_configuration", "santa.add_rule", "santa.view_rule") @@ -841,6 +872,16 @@ def test_pick_rule_team_id(self): self.assertEqual(len(team_ids), 1) self.assertEqual(team_ids[0][0].organizational_unit, self.file_team_id) + def test_pick_rule_team_id_special_chars(self): + configuration = self._force_configuration() + self._login("santa.add_rule") + response = self.client.get(reverse("santa:pick_rule_team_id", args=(configuration.pk,)), + {"query": "[]"}) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "santa/pick_rule_team_id.html") + team_ids = response.context["team_ids"] + self.assertEqual(len(team_ids), 0) + def test_pick_rule_signing_id_access_denied(self): configuration = self._force_configuration() self._login("santa.add_configuration", "santa.view_configuration") @@ -859,6 +900,16 @@ def test_pick_rule_signing_id(self): self.assertEqual(len(signing_ids), 1) self.assertEqual(signing_ids[0][0].signing_id, self.file_signing_id) + def test_pick_rule_signing_id_special_chars(self): + configuration = self._force_configuration() + self._login("santa.add_rule") + response = self.client.get(reverse("santa:pick_rule_signing_id", args=(configuration.pk,)), + {"query": "94KV3E626L:Frameworks[]Electron Framework"}) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "santa/pick_rule_signing_id.html") + signing_ids = response.context["signing_ids"] + self.assertEqual(len(signing_ids), 0) + # terraform export def test_terraform_export_redirect(self): diff --git a/zentral/contrib/santa/forms.py b/zentral/contrib/santa/forms.py index e22defc581..42eab78fb3 100644 --- a/zentral/contrib/santa/forms.py +++ b/zentral/contrib/santa/forms.py @@ -209,7 +209,7 @@ def test_signing_id_identifier(identifier): team_id, signing_id = identifier.split(":", 1) if not test_team_id(team_id) and team_id != "platform": return False - return re.match(r'^([0-9a-zA-Z_\-]+\.)*([0-9a-zA-Z_\-]+)\Z', signing_id) is not None + return True def test_team_id(team_id): @@ -279,17 +279,21 @@ def clean(self): # identifier if target_identifier: + if "target_identifier" in self.fields: + error_field = "target_identifier" + else: + error_field = None if target_type == Target.SIGNING_ID: if not test_signing_id_identifier(target_identifier): - self.add_error("target_identifier", "Invalid Signing ID target identifier") + self.add_error(error_field, "Invalid Signing ID target identifier") elif target_type == Target.TEAM_ID: target_identifier = target_identifier.upper() if not test_team_id(target_identifier): - self.add_error("target_identifier", "Invalid Team ID") + self.add_error(error_field, "Invalid Team ID") else: target_identifier = target_identifier.lower() if not test_sha256(target_identifier): - self.add_error("target_identifier", "Invalid sha256") + self.add_error(error_field, "Invalid sha256") # policy try: diff --git a/zentral/contrib/santa/models.py b/zentral/contrib/santa/models.py index 6cefe1c9c6..910cc3d608 100644 --- a/zentral/contrib/santa/models.py +++ b/zentral/contrib/santa/models.py @@ -531,13 +531,17 @@ def search_teamid_objects(self, **kwargs): q = kwargs.get("query") if not q: return [] + q = "%{}%".format(connection.ops.prep_for_like_query(q)) query = ( "select c.organizational_unit, c.organization " "from inventory_certificate as c " "join inventory_file as f on (f.signed_by_id = c.id) " "join inventory_source as s on (s.id = f.source_id) " "where s.module = 'zentral.contrib.santa' and s.name = 'Santa events' " - "and (c.organizational_unit ~* %s or c.organization ~* %s)" + "and (" + " upper(c.organizational_unit) like upper(%s)" + " or upper(c.organization) like upper(%s)" + ") " "group by c.organizational_unit, c.organization " "order by c.organization, c.organizational_unit" ) @@ -565,12 +569,13 @@ def search_signingid_objects(self, **kwargs): q = kwargs.get("query") if not q: return [] + q = "%{}%".format(connection.ops.prep_for_like_query(q)) query = ( "select f.signing_id " "from inventory_file as f " "join inventory_source as s on (s.id = f.source_id) " "where s.module = 'zentral.contrib.santa' and s.name = 'Santa events' " - "and f.signing_id ~* %s " + "and upper(f.signing_id) like upper(%s) " "group by f.signing_id " "order by f.signing_id" )