diff --git a/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py b/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py index 84ca0f7fc..d05cbc171 100644 --- a/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py +++ b/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py @@ -4,7 +4,6 @@ from typing import List from mongoengine import Document, EmbeddedDocumentListField from monkey_island.cc.models.zero_trust.event import Event -from monkey_island.cc.models.zero_trust.subnet_pair import SubnetPair class MonkeyFindingDetails(Document): @@ -15,16 +14,8 @@ class MonkeyFindingDetails(Document): # SCHEMA events = EmbeddedDocumentListField(document_type=Event, required=False) - checked_subnet_pairs = EmbeddedDocumentListField(document_type=SubnetPair, required=False) # LOGIC def add_events(self, events: List[Event]) -> MonkeyFindingDetails: self.update(push_all__events=events) return self - - def add_checked_subnet_pair(self, subnet_pair: SubnetPair) -> MonkeyFindingDetails: - self.update(push__checked_subnet_pairs=subnet_pair) - return self - - def is_with_subnet_pair(self, subnet_pair: SubnetPair): - return subnet_pair in self.checked_subnet_pairs diff --git a/monkey/monkey_island/cc/models/zero_trust/subnet_pair.py b/monkey/monkey_island/cc/models/zero_trust/subnet_pair.py deleted file mode 100644 index 8d3de96e4..000000000 --- a/monkey/monkey_island/cc/models/zero_trust/subnet_pair.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import List - -from mongoengine import EmbeddedDocument, StringField - - -class SubnetPair(EmbeddedDocument): - """ - This model represents a pair of subnets. It is meant to hold details about cross-segmentation check between two - subnets. - """ - # SCHEMA - first_subnet = StringField() - second_subnet = StringField() - - # LOGIC - @staticmethod - def create_subnet_pair(subnets: List[str]): - subnets.sort() - return SubnetPair(first_subnet=subnets[0], second_subnet=subnets[1]) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py index a5eb865a7..1c43e2863 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py @@ -28,7 +28,6 @@ def check_segmentation_violation(current_monkey, target_ip): event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) MonkeyFindingService.create_or_add_to_existing( test=zero_trust_consts.TEST_SEGMENTATION, - subnet_pairs=[[source_subnet, target_subnet]], status=zero_trust_consts.STATUS_FAILED, events=[event] ) @@ -92,7 +91,6 @@ def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): for subnet_pair in all_subnets_pairs_for_this_monkey: MonkeyFindingService.create_or_add_to_existing( - subnet_pairs=[list(subnet_pair)], status=zero_trust_consts.STATUS_PASSED, events=[get_segmentation_done_event(current_monkey, subnet_pair)], test=zero_trust_consts.TEST_SEGMENTATION diff --git a/monkey/monkey_island/cc/services/zero_trust/monkey_details_service.py b/monkey/monkey_island/cc/services/zero_trust/monkey_details_service.py new file mode 100644 index 000000000..623285ddd --- /dev/null +++ b/monkey/monkey_island/cc/services/zero_trust/monkey_details_service.py @@ -0,0 +1,36 @@ +from typing import List + +from bson import ObjectId + +from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails + +# How many events of a single finding to return to UI. +# 50 will return 50 latest and 50 oldest events from a finding +EVENT_FETCH_CNT = 50 + + +class MonkeyDetailsService: + + @staticmethod + def fetch_details_for_display(finding_id: ObjectId): + pipeline = [{'$match': {'_id': finding_id}}, + {'$addFields': {'oldest_events': {'$slice': ['$events', EVENT_FETCH_CNT]}, + 'latest_events': {'$slice': ['$events', -1 * EVENT_FETCH_CNT]}, + 'event_count': {'$size': '$events'}}}, + {'$unset': ['events', 'checked_subnet_pairs']}] + details = list(MonkeyFindingDetails.objects.aggregate(*pipeline)) + if details: + details = details[0] + details['latest_events'] = MonkeyDetailsService._get_events_without_overlap(details['event_count'], + details['latest_events']) + return details + + @staticmethod + def _get_events_without_overlap(event_count: int, events: List[object]) -> List[object]: + overlap_count = event_count - EVENT_FETCH_CNT + if overlap_count >= EVENT_FETCH_CNT: + return events + elif overlap_count <= 0: + return [] + else: + return events[-1 * overlap_count:] diff --git a/monkey/monkey_island/cc/services/zero_trust/monkey_finding_service.py b/monkey/monkey_island/cc/services/zero_trust/monkey_finding_service.py index fbc477953..46c3137bf 100644 --- a/monkey/monkey_island/cc/services/zero_trust/monkey_finding_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/monkey_finding_service.py @@ -6,13 +6,12 @@ from common.common_consts import zero_trust_consts from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails -from monkey_island.cc.models.zero_trust.subnet_pair import SubnetPair class MonkeyFindingService: @staticmethod - def create_or_add_to_existing(test, status, events, subnet_pairs=None): + def create_or_add_to_existing(test, status, events): """ Create a new finding or add the events to an existing one if it's the same (same meaning same status and same test). @@ -24,18 +23,15 @@ class MonkeyFindingService: assert (len(existing_findings) < 2), "More than one finding exists for {}:{}".format(test, status) if len(existing_findings) == 0: - MonkeyFindingService.create_new_finding(test, status, events, subnet_pairs) + MonkeyFindingService.create_new_finding(test, status, events) else: # Now we know for sure this is the only one MonkeyFindingService.add_events(existing_findings[0], events) - if subnet_pairs: - MonkeyFindingService.add_subnet_pairs(existing_findings[0], subnet_pairs) @staticmethod - def create_new_finding(test: str, status: str, events: List[Event], subnet_pairs: List[SubnetPair]): + def create_new_finding(test: str, status: str, events: List[Event]): details = MonkeyFindingDetails() details.events = events - details.subnet_pairs = subnet_pairs details.save() Finding.save_finding(test, status, details) @@ -43,20 +39,6 @@ class MonkeyFindingService: def add_events(finding: Finding, events: List[Event]): finding.details.fetch().add_events(events).save() - @staticmethod - def add_subnet_pairs(finding: Finding, subnet_pairs: List[List[str]]): - finding_details = finding.details.fetch() - for subnet_pair in subnet_pairs: - subnet_pair_document = SubnetPair.create_subnet_pair(subnet_pair) - if not MonkeyFindingService.is_subnet_pair_in_finding(finding, subnet_pair_document): - finding_details.add_checked_subnet_pair(subnet_pair_document) - finding_details.save() - - @staticmethod - def is_subnet_pair_in_finding(finding: Finding, subnet_pair: SubnetPair): - details = finding.details.fetch() - return details.is_with_subnet_pair(subnet_pair) - @staticmethod def get_events_by_finding(finding_id: str) -> List[object]: finding = Finding.objects.get(id=finding_id)