From 4440027699eaf6f7ad99e86069b2ec0b1f675172 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Fri, 18 Sep 2020 10:13:27 +0300
Subject: [PATCH] Backend ScoutSuite backend code, which handles ScoutSuite
 data reception, parsing and storing

---
 .../common/common_consts/zero_trust_consts.py | 19 +++++-
 monkey/common/utils/code_utils.py             | 17 +++++
 .../cc/models/zero_trust/finding.py           | 30 +++++----
 .../models/zero_trust/scoutsuite_data_json.py | 19 ++++++
 .../models/zero_trust/scoutsuite_finding.py   | 17 -----
 .../zero_trust/scoutsuite_finding_details.py  | 16 ++---
 .../cc/models/zero_trust/scoutsuite_rule.py   | 26 ++++++++
 .../cc/resources/reporting/report.py          | 14 +++-
 .../telemetry/processing/scoutsuite.py        | 21 +++++-
 .../cc/services/zero_trust/events_service.py  |  8 ++-
 .../cc/services/zero_trust/finding_service.py | 14 +++-
 .../zero_trust/monkey_finding_service.py      | 13 +---
 .../zero_trust/scoutsuite/consts/__init__.py  |  0
 .../zero_trust/scoutsuite/consts/findings.py  | 19 ++++++
 .../scoutsuite/consts/findings_list.py        |  3 +
 .../scoutsuite/consts/rule_consts.py          |  4 ++
 .../scoutsuite/consts/service_consts.py       |  9 +++
 .../scoutsuite/scoutsuite_finding_service.py  | 65 +++++++++++++++++++
 .../scoutsuite/scoutsuite_rule_service.py     | 30 +++++++++
 .../zero_trust/scoutsuite_finding_service.py  |  3 -
 20 files changed, 286 insertions(+), 61 deletions(-)
 create mode 100644 monkey/monkey_island/cc/models/zero_trust/scoutsuite_data_json.py
 delete mode 100644 monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding.py
 create mode 100644 monkey/monkey_island/cc/models/zero_trust/scoutsuite_rule.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/__init__.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings_list.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_consts.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/service_consts.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_finding_service.py
 create mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_rule_service.py
 delete mode 100644 monkey/monkey_island/cc/services/zero_trust/scoutsuite_finding_service.py

diff --git a/monkey/common/common_consts/zero_trust_consts.py b/monkey/common/common_consts/zero_trust_consts.py
index 8d55bc320..edc40d0f2 100644
--- a/monkey/common/common_consts/zero_trust_consts.py
+++ b/monkey/common/common_consts/zero_trust_consts.py
@@ -22,6 +22,11 @@ STATUS_FAILED = "Failed"
 # Don't change order! The statuses are ordered by importance/severity.
 ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
 
+MONKEY_FINDING = "monkey_finding"
+SCOUTSUITE_FINDING = "scoutsuite_finding"
+FINDING_TYPES = [MONKEY_FINDING, SCOUTSUITE_FINDING]
+
+
 TEST_DATA_ENDPOINT_ELASTIC = "unencrypted_data_endpoint_elastic"
 TEST_DATA_ENDPOINT_HTTP = "unencrypted_data_endpoint_http"
 TEST_MACHINE_EXPLOITED = "machine_exploited"
@@ -31,6 +36,7 @@ TEST_MALICIOUS_ACTIVITY_TIMELINE = "malicious_activity_timeline"
 TEST_SEGMENTATION = "segmentation"
 TEST_TUNNELING = "tunneling"
 TEST_COMMUNICATE_AS_NEW_USER = "communicate_as_new_user"
+TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES = "scoutsuite_permissive_firewall_rules"
 TESTS = (
     TEST_SEGMENTATION,
     TEST_MALICIOUS_ACTIVITY_TIMELINE,
@@ -40,7 +46,8 @@ TESTS = (
     TEST_DATA_ENDPOINT_HTTP,
     TEST_DATA_ENDPOINT_ELASTIC,
     TEST_TUNNELING,
-    TEST_COMMUNICATE_AS_NEW_USER
+    TEST_COMMUNICATE_AS_NEW_USER,
+    TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES
 )
 
 PRINCIPLE_DATA_TRANSIT = "data_transit"
@@ -165,6 +172,16 @@ TESTS_MAP = {
         PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
+    TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES: {
+        TEST_EXPLANATION_KEY: "ScoutSuite assessed cloud firewall rules and settings.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "ScoutSuite found overly permissive firewall rules.",
+            STATUS_PASSED: "ScoutSuite found no problems with cloud firewall rules."
+        },
+        PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
+        PILLARS_KEY: [NETWORKS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
 }
 
 EVENT_TYPE_MONKEY_NETWORK = "monkey_network"
diff --git a/monkey/common/utils/code_utils.py b/monkey/common/utils/code_utils.py
index 214e6d108..32f972a76 100644
--- a/monkey/common/utils/code_utils.py
+++ b/monkey/common/utils/code_utils.py
@@ -1,5 +1,10 @@
 # abstract, static method decorator
 # noinspection PyPep8Naming
+import operator
+from functools import reduce
+from typing import List
+
+
 class abstractstatic(staticmethod):
     __slots__ = ()
 
@@ -8,3 +13,15 @@ class abstractstatic(staticmethod):
         function.__isabstractmethod__ = True
 
     __isabstractmethod__ = True
+
+
+def _get_value_by_path(data, path: List[str]):
+    return reduce(operator.getitem, path, data)
+
+
+def get_object_value_by_path(data_object: object, path: List[str]):
+    return _get_value_by_path(data_object, path)
+
+
+def get_dict_value_by_path(data_dict: dict, path: List[str]):
+    return _get_value_by_path(data_dict, path)
diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py
index 8895a7fdb..6d0827e39 100644
--- a/monkey/monkey_island/cc/models/zero_trust/finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/finding.py
@@ -2,16 +2,15 @@
 """
 Define a Document Schema for Zero Trust findings.
 """
-from typing import List
+from typing import Union
 
 from mongoengine import Document, StringField, GenericLazyReferenceField
 
 import common.common_consts.zero_trust_consts as zero_trust_consts
 # Dummy import for mongoengine.
 # noinspection PyUnresolvedReferences
-from monkey_island.cc.models.zero_trust.event import Event
 from monkey_island.cc.models.zero_trust.monkey_finding_details import MonkeyFindingDetails
-from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutsuiteFindingDetails
+from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutSuiteFindingDetails
 
 
 class Finding(Document):
@@ -35,7 +34,8 @@ class Finding(Document):
     # SCHEMA
     test = StringField(required=True, choices=zero_trust_consts.TESTS)
     status = StringField(required=True, choices=zero_trust_consts.ORDERED_TEST_STATUSES)
-    details = GenericLazyReferenceField(choices=[MonkeyFindingDetails, ScoutsuiteFindingDetails], required=True)
+    type = StringField(required=True, choices=zero_trust_consts.FINDING_TYPES)
+    details = GenericLazyReferenceField(choices=[MonkeyFindingDetails, ScoutSuiteFindingDetails], required=True)
     # http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance
     meta = {'allow_inheritance': True}
 
@@ -48,11 +48,19 @@ class Finding(Document):
 
     # Creation methods
     @staticmethod
-    def save_finding(test: str, status: str, detail_ref):
-        finding = Finding(test=test,
-                          status=status,
-                          details=detail_ref)
+    def save_finding(test: str,
+                     status: str,
+                     detail_ref: Union[MonkeyFindingDetails, ScoutSuiteFindingDetails]):
+        temp_finding = Finding(test=test,
+                               status=status,
+                               details=detail_ref,
+                               type=Finding._get_finding_type_by_details(detail_ref))
+        temp_finding.save()
+        return temp_finding
 
-        finding.save()
-
-        return finding
+    @staticmethod
+    def _get_finding_type_by_details(details: Union[MonkeyFindingDetails, ScoutSuiteFindingDetails]) -> str:
+        if type(details) == MonkeyFindingDetails:
+            return zero_trust_consts.MONKEY_FINDING
+        else:
+            return zero_trust_consts.SCOUTSUITE_FINDING
diff --git a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_data_json.py b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_data_json.py
new file mode 100644
index 000000000..4b493e878
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_data_json.py
@@ -0,0 +1,19 @@
+from mongoengine import Document, DynamicField
+
+
+class ScoutSuiteDataJson(Document):
+    """
+    This model is a container for ScoutSuite report data dump.
+    """
+
+    # SCHEMA
+    scoutsuite_data = DynamicField(required=True)
+
+    # LOGIC
+    @staticmethod
+    def add_scoutsuite_data(scoutsuite_data: str) -> None:
+        current_data = ScoutSuiteDataJson.objects()
+        if not current_data:
+            current_data = ScoutSuiteDataJson()
+        current_data.scoutsuite_data = scoutsuite_data
+        current_data.save()
diff --git a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding.py b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding.py
deleted file mode 100644
index f8d0f5042..000000000
--- a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from datetime import datetime
-
-from mongoengine import DateTimeField, EmbeddedDocument, StringField
-
-
-class ScoutsuiteFinding(EmbeddedDocument):
-    # SCHEMA
-    temp = StringField(required=True)
-
-    # LOGIC
-    @staticmethod
-    def create_scoutsuite_finding(title, message, event_type, timestamp=None):
-        scoutsuite_finding = ScoutsuiteFinding()
-
-        scoutsuite_finding.temp = "temp"
-
-        return scoutsuite_finding
diff --git a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py
index ed05f6003..52aa09d17 100644
--- a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py
+++ b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py
@@ -1,11 +1,9 @@
-from typing import List
+from mongoengine import Document, EmbeddedDocumentListField
 
-from mongoengine import DateTimeField, Document, StringField, EmbeddedDocumentListField
-
-from monkey_island.cc.models.zero_trust.scoutsuite_finding import ScoutsuiteFinding
+from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
 
 
-class ScoutsuiteFindingDetails(Document):
+class ScoutSuiteFindingDetails(Document):
     """
     This model represents additional information about monkey finding:
     Events if monkey finding
@@ -13,7 +11,9 @@ class ScoutsuiteFindingDetails(Document):
     """
 
     # SCHEMA
-    scoutsuite_findings = EmbeddedDocumentListField(document_type=ScoutsuiteFinding, required=False)
+    scoutsuite_rules = EmbeddedDocumentListField(document_type=ScoutSuiteRule, required=False)
 
-    def add_scoutsuite_findings(self, scoutsuite_findings: List[ScoutsuiteFinding]) -> None:
-        self.update(push_all__scoutsuite_findings=scoutsuite_findings)
+    def add_rule(self, rule: ScoutSuiteRule) -> None:
+        if rule not in self.scoutsuite_rules:
+            self.scoutsuite_rules.append(rule)
+            self.save()
diff --git a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_rule.py b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_rule.py
new file mode 100644
index 000000000..316a5402e
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_rule.py
@@ -0,0 +1,26 @@
+from mongoengine import StringField, EmbeddedDocument, ListField, \
+    IntField
+
+from monkey_island.cc.services.zero_trust.scoutsuite.consts import rule_consts
+
+
+class ScoutSuiteRule(EmbeddedDocument):
+    """
+    This model represents additional information about monkey finding:
+    Events if monkey finding
+    Scoutsuite findings if scoutsuite finding
+    """
+
+    # SCHEMA
+    description = StringField(required=True)
+    path = StringField(required=True)
+    level = StringField(required=True, options=rule_consts.RULE_LEVELS)
+    items = ListField()
+    dashboard_name = StringField(required=True)
+    checked_items = IntField(min_value=0)
+    flagged_items = IntField(min_value=0)
+    service = StringField(required=True)
+    rationale = StringField(required=True)
+    remediation = StringField(required=False)
+    compliance = StringField(required=False)
+    references = ListField(required=False)
diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py
index 546f944a3..5273ca810 100644
--- a/monkey/monkey_island/cc/resources/reporting/report.py
+++ b/monkey/monkey_island/cc/resources/reporting/report.py
@@ -1,11 +1,12 @@
 import http.client
 
 import flask_restful
-from flask import jsonify
+from flask import jsonify, Response
 
+from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteDataJson
 from monkey_island.cc.resources.auth.auth import jwt_required
 from monkey_island.cc.services.reporting.report import ReportService
-from monkey_island.cc.services.zero_trust.monkey_finding_service import MonkeyFindingService
+from monkey_island.cc.services.zero_trust.finding_service import FindingService
 from monkey_island.cc.services.zero_trust.zero_trust_service import \
     ZeroTrustService
 
@@ -16,6 +17,7 @@ REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE]
 REPORT_DATA_PILLARS = "pillars"
 REPORT_DATA_FINDINGS = "findings"
 REPORT_DATA_PRINCIPLES_STATUS = "principles"
+REPORT_DATA_SCOUTSUITE = "scoutsuite"
 
 __author__ = ["itay.mizeretz", "shay.nehmad"]
 
@@ -36,6 +38,12 @@ class Report(flask_restful.Resource):
             elif report_data == REPORT_DATA_PRINCIPLES_STATUS:
                 return jsonify(ZeroTrustService.get_principles_status())
             elif report_data == REPORT_DATA_FINDINGS:
-                return jsonify(MonkeyFindingService.get_all_monkey_findings())
+                return jsonify(FindingService.get_all_findings())
+            elif report_data == REPORT_DATA_SCOUTSUITE:
+                try:
+                    data = ScoutSuiteDataJson.objects.get().scoutsuite_data
+                except Exception:
+                    data = {}
+                return Response(data, mimetype='application/json')
 
         flask_restful.abort(http.client.NOT_FOUND)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/scoutsuite.py b/monkey/monkey_island/cc/services/telemetry/processing/scoutsuite.py
index d0e25aebc..c29320535 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/scoutsuite.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/scoutsuite.py
@@ -1,15 +1,32 @@
 import json
 
 from monkey_island.cc.database import mongo
+from monkey_island.cc.models.zero_trust.scoutsuite_data_json import ScoutSuiteDataJson
+
+from monkey_island.cc.services.zero_trust.scoutsuite.consts.findings_list import SCOUTSUITE_FINDINGS
+from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_parsing import RuleParser
+from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_finding_service import ScoutSuiteFindingService
+from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService
 
 
 def process_scoutsuite_telemetry(telemetry_json):
     # Encode data to json, because mongo can't save it as document (invalid document keys)
     telemetry_json['data'] = json.dumps(telemetry_json['data'])
+    ScoutSuiteDataJson.add_scoutsuite_data(telemetry_json['data'])
+    scoutsuite_data = json.loads(telemetry_json['data'])['data']
+    create_scoutsuite_findings(scoutsuite_data)
     update_data(telemetry_json)
 
 
+def create_scoutsuite_findings(scoutsuite_data):
+    for finding in SCOUTSUITE_FINDINGS:
+        for rule in finding.rules:
+            rule_data = RuleParser.get_rule_data(scoutsuite_data, rule)
+            rule = ScoutSuiteRuleService.get_rule_from_rule_data(rule_data)
+            ScoutSuiteFindingService.process_rule(finding, rule)
+
+
 def update_data(telemetry_json):
-    mongo.db.scoutsuite.update(
+    mongo.db.scoutsuite.insert_one(
         {'guid': telemetry_json['monkey_guid']},
-        {'$push': {'results': telemetry_json['data']}})
+        {'results': telemetry_json['data']})
diff --git a/monkey/monkey_island/cc/services/zero_trust/events_service.py b/monkey/monkey_island/cc/services/zero_trust/events_service.py
index 5cccee7f3..7f4f9e496 100644
--- a/monkey/monkey_island/cc/services/zero_trust/events_service.py
+++ b/monkey/monkey_island/cc/services/zero_trust/events_service.py
@@ -18,9 +18,11 @@ class EventsService:
                                     'latest_events': {'$slice': ['$events', -1 * EVENT_FETCH_CNT]},
                                     'event_count': {'$size': '$events'}}},
                     {'$unset': ['events']}]
-        details = MonkeyFindingDetails.objects.aggregate(*pipeline).next()
-        details['latest_events'] = EventsService._get_events_without_overlap(details['event_count'],
-                                                                             details['latest_events'])
+        details = list(MonkeyFindingDetails.objects.aggregate(*pipeline))
+        if details:
+            details = details[0]
+            details['latest_events'] = EventsService._get_events_without_overlap(details['event_count'],
+                                                                                 details['latest_events'])
         return details
 
     @staticmethod
diff --git a/monkey/monkey_island/cc/services/zero_trust/finding_service.py b/monkey/monkey_island/cc/services/zero_trust/finding_service.py
index 2feb02cce..f6ab4b39a 100644
--- a/monkey/monkey_island/cc/services/zero_trust/finding_service.py
+++ b/monkey/monkey_island/cc/services/zero_trust/finding_service.py
@@ -2,13 +2,24 @@ from typing import List
 
 from common.common_consts import zero_trust_consts
 from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.services.zero_trust.events_service import EventsService
 
 
 class FindingService:
 
     @staticmethod
     def get_all_findings() -> List[Finding]:
-        return list(Finding.objects)
+        findings = list(Finding.objects)
+        details = []
+        for i in range(len(findings)):
+            if findings[i].type == zero_trust_consts.MONKEY_FINDING:
+                details = EventsService.fetch_events_for_display(findings[i].details.id)
+            elif findings[i].type == zero_trust_consts.SCOUTSUITE_FINDING:
+                details = findings[i].details.fetch().to_mongo()
+            findings[i] = findings[i].to_mongo()
+            findings[i] = FindingService.get_enriched_finding(findings[i])
+            findings[i]['details'] = details
+        return findings
 
     @staticmethod
     def get_enriched_finding(finding):
@@ -19,5 +30,6 @@ class FindingService:
             'test_key': finding['test'],
             'pillars': test_info[zero_trust_consts.PILLARS_KEY],
             'status': finding['status'],
+            'type': finding['type']
         }
         return enriched_finding
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 a1e731b14..1ee60b117 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,7 +6,6 @@ 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.services.zero_trust.finding_service import FindingService
 
 
 class MonkeyFindingService:
@@ -38,17 +37,7 @@ class MonkeyFindingService:
 
     @staticmethod
     def add_events(finding: Finding, events: List[Event]):
-        finding.details.fetch().add_events(events)
-
-    @staticmethod
-    def get_all_monkey_findings():
-        findings = FindingService.get_all_findings()
-        for i in range(len(findings)):
-            details = MonkeyFindingService.fetch_events_for_display(findings[i].details.id)
-            findings[i] = findings[i].to_mongo()
-            findings[i] = FindingService.get_enriched_finding(findings[i])
-            findings[i]['details'] = details
-        return findings
+        finding.details.fetch().add_events(events).save()
 
     @staticmethod
     def get_events_by_finding(finding_id: str) -> List[object]:
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/__init__.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings.py
new file mode 100644
index 000000000..792c92e80
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings.py
@@ -0,0 +1,19 @@
+from common.common_consts import zero_trust_consts
+from common.common_consts.zero_trust_consts import NETWORKS
+from monkey_island.cc.services.zero_trust.scoutsuite.consts.ec2_rules import EC2Rules
+
+
+class PERMISSIVE_FIREWALL_RULES:
+    rules = [EC2Rules.SECURITY_GROUP_ALL_PORTS_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_TCP_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_UDP_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_RDP_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_SSH_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_MYSQL_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_MSSQL_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_MONGODB_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_ORACLE_DB_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_POSTGRESQL_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_NFS_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_SMTP_PORT_TO_ALL,
+             EC2Rules.SECURITY_GROUP_OPENS_DNS_PORT_TO_ALL, EC2Rules.SECURITY_GROUP_OPENS_ALL_PORTS_TO_SELF,
+             EC2Rules.SECURITY_GROUP_OPENS_ALL_PORTS, EC2Rules.SECURITY_GROUP_OPENS_PLAINTEXT_PORT_FTP,
+             EC2Rules.SECURITY_GROUP_OPENS_PLAINTEXT_PORT_TELNET, EC2Rules.SECURITY_GROUP_OPENS_PORT_RANGE]
+
+    pillars = [NETWORKS]
+
+    test = zero_trust_consts.TEST_SCOUTSUITE_PERMISSIVE_FIREWALL_RULES
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings_list.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings_list.py
new file mode 100644
index 000000000..bf54ac8ce
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/findings_list.py
@@ -0,0 +1,3 @@
+from monkey_island.cc.services.zero_trust.scoutsuite.consts.findings import PERMISSIVE_FIREWALL_RULES
+
+SCOUTSUITE_FINDINGS = [PERMISSIVE_FIREWALL_RULES]
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_consts.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_consts.py
new file mode 100644
index 000000000..732852174
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_consts.py
@@ -0,0 +1,4 @@
+RULE_LEVEL_DANGER = 'danger'
+RULE_LEVEL_WARNING = 'warning'
+
+RULE_LEVELS = (RULE_LEVEL_DANGER, RULE_LEVEL_WARNING)
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/service_consts.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/service_consts.py
new file mode 100644
index 000000000..5c0338c26
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/service_consts.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+
+SERVICES = 'services'
+FINDINGS = 'findings'
+
+
+class SERVICE_TYPES(Enum):
+    EC2 = 'ec2'
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_finding_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_finding_service.py
new file mode 100644
index 000000000..6f8e64e87
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_finding_service.py
@@ -0,0 +1,65 @@
+from typing import List
+
+from common.common_consts import zero_trust_consts
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.models.zero_trust.scoutsuite_finding_details import ScoutSuiteFindingDetails
+from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
+from monkey_island.cc.services.zero_trust.scoutsuite.scoutsuite_rule_service import ScoutSuiteRuleService
+
+
+class ScoutSuiteFindingService:
+
+    @staticmethod
+    # TODO add type hinting like finding: Union[SCOUTSUITE_FINDINGS]?
+    def process_rule(finding, rule: ScoutSuiteRule):
+        existing_findings = Finding.objects(test=finding.test, type=zero_trust_consts.SCOUTSUITE_FINDING)
+        assert (len(existing_findings) < 2), "More than one finding exists for {}".format(finding.test)
+
+        if len(existing_findings) == 0:
+            ScoutSuiteFindingService.create_new_finding_from_rule(finding, rule)
+        else:
+            ScoutSuiteFindingService.add_rule(existing_findings[0], rule)
+
+    @staticmethod
+    def create_new_finding_from_rule(finding, rule: ScoutSuiteRule):
+        details = ScoutSuiteFindingDetails()
+        details.scoutsuite_rules = [rule]
+        details.save()
+        status = ScoutSuiteFindingService.get_finding_status_from_rules(details.scoutsuite_rules)
+        Finding.save_finding(finding.test, status, details)
+
+    @staticmethod
+    def get_finding_status_from_rules(rules: List[ScoutSuiteRule]) -> str:
+        if len(rules) == 0:
+            return zero_trust_consts.STATUS_UNEXECUTED
+        elif filter(lambda x: ScoutSuiteRuleService.is_rule_dangerous(x), rules):
+            return zero_trust_consts.STATUS_FAILED
+        elif filter(lambda x: ScoutSuiteRuleService.is_rule_warning(x), rules):
+            return zero_trust_consts.STATUS_VERIFY
+        else:
+            return zero_trust_consts.STATUS_PASSED
+
+    @staticmethod
+    def add_rule(finding: Finding, rule: ScoutSuiteRule):
+        ScoutSuiteFindingService.change_finding_status_by_rule(finding, rule)
+        finding.save()
+        finding.details.fetch().add_rule(rule)
+
+    @staticmethod
+    def change_finding_status_by_rule(finding: Finding, rule: ScoutSuiteRule):
+        rule_status = ScoutSuiteFindingService.get_finding_status_from_rules([rule])
+        finding_status = finding.status
+        new_finding_status = ScoutSuiteFindingService.get_finding_status_from_rule_status(finding_status, rule_status)
+        if finding_status != new_finding_status:
+            finding.status = new_finding_status
+
+    @staticmethod
+    def get_finding_status_from_rule_status(finding_status: str, rule_status: str) -> str:
+        if finding_status == zero_trust_consts.STATUS_FAILED or rule_status == zero_trust_consts.STATUS_FAILED:
+            return zero_trust_consts.STATUS_FAILED
+        elif finding_status == zero_trust_consts.STATUS_VERIFY or rule_status == zero_trust_consts.STATUS_VERIFY:
+            return zero_trust_consts.STATUS_VERIFY
+        elif finding_status == zero_trust_consts.STATUS_PASSED or rule_status == zero_trust_consts.STATUS_PASSED:
+            return zero_trust_consts.STATUS_PASSED
+        else:
+            return zero_trust_consts.STATUS_UNEXECUTED
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_rule_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_rule_service.py
new file mode 100644
index 000000000..3b76194af
--- /dev/null
+++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/scoutsuite_rule_service.py
@@ -0,0 +1,30 @@
+from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule
+from monkey_island.cc.services.zero_trust.scoutsuite.consts import rule_consts
+
+
+class ScoutSuiteRuleService:
+
+    @staticmethod
+    def get_rule_from_rule_data(rule_data: dict) -> ScoutSuiteRule:
+        rule = ScoutSuiteRule()
+        rule.description = rule_data['description']
+        rule.path = rule_data['path']
+        rule.level = rule_data['level']
+        rule.items = rule_data['items']
+        rule.dashboard_name = rule_data['dashboard_name']
+        rule.checked_items = rule_data['checked_items']
+        rule.flagged_items = rule_data['flagged_items']
+        rule.service = rule_data['service']
+        rule.rationale = rule_data['rationale']
+        rule.remediation = rule_data['remediation']
+        rule.compliance = rule_data['compliance']
+        rule.references = rule_data['references']
+        return rule
+
+    @staticmethod
+    def is_rule_dangerous(rule: ScoutSuiteRule):
+        return rule.level == rule_consts.RULE_LEVEL_DANGER and len(rule.items) != 0
+
+    @staticmethod
+    def is_rule_warning(rule: ScoutSuiteRule):
+        return rule.level == rule_consts.RULE_LEVEL_WARNING and len(rule.items) != 0
diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite_finding_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite_finding_service.py
deleted file mode 100644
index 12ab2743b..000000000
--- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite_finding_service.py
+++ /dev/null
@@ -1,3 +0,0 @@
-
-class ScoutsuiteFindingService:
-    pass