def test_update_machine_statuses_status_default_time_update(self): cc1 = self._force_compliance_check() serial_number = get_random_string() first_status_time = datetime(2014, 10, 26) ms = MachineStatus.objects.create(compliance_check=cc1, compliance_check_version=cc1.version, serial_number=serial_number, status=Status.OK.value, status_time=first_status_time) update_machine_statuses(serial_number, [(cc1, Status.OK, None)]) ms.refresh_from_db() self.assertTrue(ms.status_time > first_status_time)
def test_update_machine_statuses_update_two_no_changes(self): cc1 = self._force_compliance_check() cc2 = self._force_compliance_check() serial_number = get_random_string() update_machine_statuses(serial_number, [(cc1, Status.OK, None), (cc2, Status.FAILED, datetime.utcnow())]) result = update_machine_statuses(serial_number, [(cc1, Status.OK, None), (cc2, Status.FAILED, None)]) self.assertEqual(len(result), 2) self.assertEqual(result[0], (cc1.pk, Status.OK.value, Status.OK.value)) self.assertEqual(result[1], (cc2.pk, Status.FAILED.value, Status.FAILED.value)) self.assertEqual( MachineStatus.objects.filter(serial_number=serial_number).count(), 2)
def test_update_machine_statuses_create_one_then_another_one(self): cc1 = self._force_compliance_check() serial_number = get_random_string() result = update_machine_statuses(serial_number, [(cc1, Status.OK, datetime.utcnow())]) self.assertEqual(len(result), 1) self.assertEqual(result[0], (cc1.pk, Status.OK.value, None)) self.assertEqual( MachineStatus.objects.filter(serial_number=serial_number).count(), 1) serial_number = get_random_string() result = update_machine_statuses(serial_number, [(cc1, Status.OK, datetime.utcnow())]) self.assertEqual(len(result), 1) self.assertEqual(result[0], (cc1.pk, Status.OK.value, None)) self.assertEqual( MachineStatus.objects.filter(serial_number=serial_number).count(), 1)
def test_update_machine_statuses_version_update(self): cc1 = self._force_compliance_check() self.assertEqual(cc1.version, 1) serial_number = get_random_string() result = update_machine_statuses(serial_number, [(cc1, Status.OK, datetime.utcnow())]) self.assertEqual(len(result), 1) self.assertEqual(result[0], (cc1.pk, Status.OK.value, None)) ms_qs = MachineStatus.objects.filter(serial_number=serial_number) self.assertEqual(ms_qs.count(), 1) ms = ms_qs.first() self.assertEqual(ms.compliance_check_version, cc1.version) cc1.version = 2 cc1.save() result = update_machine_statuses(serial_number, [(cc1, Status.OK, datetime.utcnow())]) self.assertEqual(len(result), 1) self.assertEqual(result[0], (cc1.pk, Status.OK.value, Status.OK.value)) ms.refresh_from_db() self.assertEqual(ms.compliance_check_version, cc1.version)
def process_tree(self, tree, last_seen): machine_tag_set = None compliance_check_statuses = [] serial_number = tree["serial_number"] source_name = tree["source"]["name"] platform = tree.get("platform") if not platform: logger.warning("Cannot process %s %s tree: missing platform", source_name, serial_number) return for check_tag_set, jmespath_parsed_expr, jmespath_check in self._get_source_platform_checks( source_name, platform): if check_tag_set: if machine_tag_set is None: # TODO cache? machine_tag_set = set( MachineTag.objects.filter( serial_number=serial_number).values_list( "tag_id", flat=True)) if not check_tag_set.intersection(machine_tag_set): # tags mismatch continue # default to unknown status status = Status.UNKNOWN try: result = jmespath_parsed_expr.search(tree) except Exception: logger.exception("Could not evaluate JMESPath check %s", jmespath_check.pk) else: if result is True: status = Status.OK elif result is False: status = Status.FAILED else: logger.warning("JMESPath check %s result is not a boolean", jmespath_check.pk) compliance_check_statuses.append( (jmespath_check.compliance_check, status, last_seen)) if not compliance_check_statuses: # nothing to update, no events return status_updates = update_machine_statuses(serial_number, compliance_check_statuses) for compliance_check_pk, status_value, previous_status_value in status_updates: if status_value == previous_status_value: # status not updated, no event continue yield JMESPathCheckStatusUpdated.build_from_object_serial_number_and_statuses( self._checks[compliance_check_pk], serial_number, Status(status_value), Status(previous_status_value) if previous_status_value is not None else None)
def test_update_machine_statuses_to_old_noop(self): cc1 = self._force_compliance_check() serial_number = get_random_string() status_time = datetime.utcnow() ms = MachineStatus.objects.create(compliance_check=cc1, compliance_check_version=cc1.version, serial_number=serial_number, status=Status.OK.value, status_time=status_time) result = update_machine_statuses( serial_number, [(cc1, Status.FAILED, datetime(1871, 3, 18))]) # noop self.assertEqual(len(result), 0) ms_qs = MachineStatus.objects.filter(serial_number=serial_number) self.assertEqual(ms_qs.count(), 1) self.assertEqual(ms_qs.first(), ms) ms.refresh_from_db() self.assertEqual(ms.status, Status.OK.value) self.assertEqual(ms.status_time, status_time)
def commit(self): if not self.cc_statuses: return compliance_check_statuses = [] checks = {} for query in (Query.objects.select_related( "compliance_check").prefetch_related("packquery__pack").filter( pk__in=self.cc_statuses.keys(), compliance_check__isnull=False)): query_version, status, status_time, distributed_query_pk = self.cc_statuses[ query.pk] if query.version != query_version: # outdated status continue compliance_check_statuses.append( (query.compliance_check, status, status_time)) checks[query.compliance_check.pk] = (query, status_time, distributed_query_pk) status_updates = update_machine_statuses(self.serial_number, compliance_check_statuses) event_cls = event_cls_from_type( "osquery_check_status_updated") # import cycle with osquery.events for compliance_check_pk, status_value, previous_status_value in status_updates: if status_value == previous_status_value: # status not updated, no event continue query, status_time, distributed_query_pk = checks[ compliance_check_pk] yield event_cls.build_from_query_serial_number_and_statuses( query, distributed_query_pk, self.serial_number, Status(status_value), status_time, Status(previous_status_value) if previous_status_value is not None else None, )