diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index ab3251694..453e09beb 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -38,7 +38,7 @@ def __init__(self, profile_json=None, profile_format=None): self.risk = None self._validate(profile_json, profile_format) - self._update_risk(profile_format) + self.update_risk(profile_format) # Load a profile without modifying the created date # but still validate the profile @@ -51,7 +51,7 @@ def load(self, profile_json, profile_format): self.status = None self._validate(profile_json, profile_format) - self._update_risk(profile_format) + self.update_risk(profile_format) return self @@ -70,17 +70,25 @@ def update(self, profile_json, profile_format): self.questions = new_profile.questions self.status = new_profile.status + self.risk = new_profile.risk + def get_file_path(self): return os.path.join(PROFILES_PATH, self.name + '.json') def _validate(self, profile_json, profile_format): if self._valid(profile_json, profile_format): - self.status = 'Expired' if self._expired() else 'Valid' + if self._expired(): + self.status = 'Expired' + # User only wants to save a draft + elif 'status' in profile_json and profile_json['status'] == 'Draft': + self.status = 'Draft' + else: + self.status = 'Valid' else: self.status = 'Draft' - def _update_risk(self, profile_format): + def update_risk(self, profile_format): if self.status == 'Valid': @@ -103,8 +111,8 @@ def _update_risk(self, profile_format): # We only want to check the select or select-multiple # questions for now if format_q['type'] in ['select', 'select-multiple']: - answer = question['answer'] + answer = question['answer'] question_risk = 'Limited' # The answer is a single string (select) @@ -143,11 +151,10 @@ def _update_risk(self, profile_format): if 'risk' in question and question['risk'] == 'High': risk = 'High' - self.risk = risk - else: # Remove risk risk = None + self.risk = risk def _get_format_question(self, question, profile_format): @@ -224,6 +231,19 @@ def _valid(self, profile_json, profile_format): LOGGER.error('Missing answer for question: ' + profile_question.get('question')) all_questions_answered = False + + answer = profile_question.get('answer') + + # Check if a multi-select answer has been completed + if isinstance(answer, list): + if len(answer) == 0: + all_questions_answered = False + + # Check if string answer has a length greater than 0 + elif isinstance(answer, str): + if required and len(answer) == 0: + all_questions_answered = False + elif required: LOGGER.error('Missing question: ' + format_question.get('question')) all_questions_present = False diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index 736f625a4..4e8511a57 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -501,23 +501,21 @@ def update_profile(self, profile_json): else: + # Update the profile + risk_profile.update(profile_json, profile_format=self._profile_format) + # Check if name has changed if 'rename' in profile_json: - new_name = profile_json.get('rename') + old_name = profile_json.get('name') # Delete the original file - os.remove(os.path.join(PROFILES_DIR, risk_profile.name + '.json')) - - risk_profile.name = new_name - - # Update questions and answers - risk_profile.questions = profile_json.get('questions') + os.remove(os.path.join(PROFILES_DIR, old_name + '.json')) # Write file to disk with open(os.path.join(PROFILES_DIR, risk_profile.name + '.json'), 'w', encoding='utf-8') as f: - f.write(json.dumps(risk_profile.to_json())) + f.write(risk_profile.to_json()) return risk_profile diff --git a/modules/test/services/python/requirements.txt b/modules/test/services/python/requirements.txt index 42669b12c..a3fdd1857 100644 --- a/modules/test/services/python/requirements.txt +++ b/modules/test/services/python/requirements.txt @@ -1 +1 @@ -xmltodict \ No newline at end of file +xmltodict==0.13.0 \ No newline at end of file diff --git a/testing/unit/build.sh b/testing/unit/build.sh index 71f45f109..db84e0299 100644 --- a/testing/unit/build.sh +++ b/testing/unit/build.sh @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -docker build -f testing/unit/unit_test.Dockerfile -t test-run/unit-test . \ No newline at end of file +sudo docker build -f testing/unit/unit_test.Dockerfile -t testrun/unit-test . \ No newline at end of file diff --git a/testing/unit/ntp/ntp_module_test.py b/testing/unit/ntp/ntp_module_test.py index dec66f239..20dd88ef1 100644 --- a/testing/unit/ntp/ntp_module_test.py +++ b/testing/unit/ntp/ntp_module_test.py @@ -63,7 +63,7 @@ def ntp_module_report_test(self): formatted_report = self.add_formatting(report_out) # Write back the new formatted_report value - out_report_path = os.path.join(OUTPUT_DIR,'ntp_report_with_ntp.html') + out_report_path = os.path.join(OUTPUT_DIR, 'ntp_report_with_ntp.html') with open(out_report_path, 'w', encoding='utf-8') as file: file.write(formatted_report) diff --git a/testing/unit/report/report_test.py b/testing/unit/report/report_test.py index 180fabb9b..f92666b2c 100644 --- a/testing/unit/report/report_test.py +++ b/testing/unit/report/report_test.py @@ -43,7 +43,7 @@ def create_report(self, results_file_path): reports_md = [] #reports_md.append(self.get_module_html_report('tls')) reports_md.append(self.get_module_html_report('dns')) - reports_md.append(self.get_module_html_report('nmap')) + reports_md.append(self.get_module_html_report('services')) reports_md.append(self.get_module_html_report('ntp')) report.add_module_reports(reports_md) diff --git a/testing/unit/risk_profile/profiles/risk_profile_draft.json b/testing/unit/risk_profile/profiles/risk_profile_draft.json index 315ec3417..de3e9a46d 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_draft.json +++ b/testing/unit/risk_profile/profiles/risk_profile_draft.json @@ -1,144 +1,56 @@ { "name": "Primary profile", - "status": "Valid", - "created": "2024-05-23", - "version": "v1.3", + "version": "1.3-alpha", + "created": "2024-07-01", + "status": "Draft", "questions": [ { "question": "What type of device is this?", - "type": "select", - "options": [ - "IoT Sensor", - "IoT Controller", - "Smart Device", - "Something else" - ], - "answer": "IoT Sensor", - "validation": { - "required": true - } + "answer": "Sensor - Lighting" }, { "question": "How will this device be used at Google?", - "type": "text-long", - "answer": "Installed in a building", - "validation": { - "max": "128", - "required": true - } - }, - { - "question": "What is the email of the device owner(s)?", - "type": "email-multiple", - "answer": "boddey@google.com, cmeredith@google.com", - "validation": { - "required": true, - "max": "128" - } + "answer": "sakjdhaskjdh" }, { "question": "Is this device going to be managed by Google or a third party?", - "type": "select", - "options": [ - "Google", - "Third Party" - ], - "answer": "Google", - "validation": { - "required": true - } + "answer": "Google" }, { "question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", - "type": "select", - "options": [ - "Yes", - "No", - "N/A" - ], - "default": "N/A", - "answer": "Yes", - "validation": { - "required": true - } + "answer": "N/A" + }, + { + "question": "Are any of the following statements true about your device?", + "answer": [ + 3 + ] }, { - "category": "Data Transmission", "question": "Which of the following statements are true about this device?", - "description": "This tells us about the types of data that are transmitted from this device and how the transmission is performed from a technical standpoint.", - "type": "select-multiple", "answer": [ - 0, - 1, 5 - ], - "options": [ - "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", - "Data transmission occurs across less-trusted networks (e.g. the internet).", - "A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)", - "A confidentiality breach during transmission would have a substantial negative impact", - "The device encrypts data during transmission", - "The device network protocol is well-established and currently used by Google" - ], - "validation": { - "required": true - } + ] }, { - "category": "Data Transmission", "question": "Does the network protocol assure server-to-client identity verification?", - "type": "select", - "answer": "Yes", - "options": [ - "Yes", - "No", - "I don't know" - ], - "validation": { - "required": true - } + "answer": "Yes" }, { - "category": "Remote Operation", "question": "Click the statements that best describe the characteristics of this device.", - "description": "This tells us about how this device is managed remotely.", - "type": "select-multiple", "answer": [ - 0, - 1, - 2 - ], - "options": [ - "PII/PHI, or confidential business data is accessible from the device without authentication", - "Unrecoverable actions (e.g. disk wipe) can be performed remotely", - "Authentication is required for remote access", - "The management interface is accessible from the public internet", - "Static credentials are used for administration" - ], - "validation": { - "required": true - } + 5 + ] }, { - "category": "Operating Environment", "question": "Are any of the following statements true about this device?", - "description": "This informs us about what other systems and processes this device is a part of.", - "type": "select-multiple", "answer": [ - 2, - 3 - ], - "options": [ - "The device monitors an environment for active risks to human life.", - "The device is used to convey people, or critical property.", - "The device controls robotics in human-accessible spaces.", - "The device controls physical access systems.", - "The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)", - "The device's failure would cause faults in other high-criticality processes." - ], - "validation": { - "required": true - } + 6 + ] + }, + { + "question": "Comments", + "answer": "" } ] } \ No newline at end of file diff --git a/testing/unit/risk_profile/profiles/risk_profile_expired.json b/testing/unit/risk_profile/profiles/risk_profile_expired.json index 5bcedab07..7ce4d1053 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_expired.json +++ b/testing/unit/risk_profile/profiles/risk_profile_expired.json @@ -1,162 +1,56 @@ { "name": "Primary profile", + "version": "1.3-alpha", + "created": "2023-06-01", "status": "Valid", - "created": "2022-05-23", - "version": "v1.3", "questions": [ { "question": "What type of device is this?", - "type": "select", - "options": [ - "IoT Sensor", - "IoT Controller", - "Smart Device", - "Something else" - ], - "answer": "IoT Sensor", - "validation": { - "required": true - } + "answer": "Sensor - Lighting" }, { "question": "How will this device be used at Google?", - "type": "text-long", - "answer": "Installed in a building", - "validation": { - "max": "128", - "required": true - } - }, - { - "question": "What is the email of the device owner(s)?", - "type": "email-multiple", - "answer": "boddey@google.com, cmeredith@google.com", - "validation": { - "required": true, - "max": "128" - } + "answer": "sakjdhaskjdh" }, { "question": "Is this device going to be managed by Google or a third party?", - "type": "select", - "options": [ - "Google", - "Third Party" - ], - "answer": "Google", - "validation": { - "required": true - } + "answer": "Google" }, { "question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", - "type": "select", - "options": [ - "Yes", - "No", - "N/A" - ], - "default": "N/A", - "answer": "Yes", - "validation": { - "required": true - } + "answer": "N/A" }, { - "category": "Data Collection", "question": "Are any of the following statements true about your device?", - "description": "This tells us about the data your device will collect", - "type": "select-multiple", "answer": [ - 0, - 2 - ], - "options": [ - "The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)", - "The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets", - "The device stream confidential business data in real-time (seconds)?" - ], - "validation": { - "required": true - } + 3 + ] }, { - "category": "Data Transmission", "question": "Which of the following statements are true about this device?", - "description": "This tells us about the types of data that are transmitted from this device and how the transmission is performed from a technical standpoint.", - "type": "select-multiple", "answer": [ - 0, - 1, 5 - ], - "options": [ - "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", - "Data transmission occurs across less-trusted networks (e.g. the internet).", - "A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)", - "A confidentiality breach during transmission would have a substantial negative impact", - "The device encrypts data during transmission", - "The device network protocol is well-established and currently used by Google" - ], - "validation": { - "required": true - } + ] }, { - "category": "Data Transmission", "question": "Does the network protocol assure server-to-client identity verification?", - "type": "select", - "answer": "Yes", - "options": [ - "Yes", - "No", - "I don't know" - ], - "validation": { - "required": true - } + "answer": "Yes" }, { - "category": "Remote Operation", "question": "Click the statements that best describe the characteristics of this device.", - "description": "This tells us about how this device is managed remotely.", - "type": "select-multiple", "answer": [ - 0, - 1, - 2 - ], - "options": [ - "PII/PHI, or confidential business data is accessible from the device without authentication", - "Unrecoverable actions (e.g. disk wipe) can be performed remotely", - "Authentication is required for remote access", - "The management interface is accessible from the public internet", - "Static credentials are used for administration" - ], - "validation": { - "required": true - } + 5 + ] }, { - "category": "Operating Environment", "question": "Are any of the following statements true about this device?", - "description": "This informs us about what other systems and processes this device is a part of.", - "type": "select-multiple", "answer": [ - 2, - 3 - ], - "options": [ - "The device monitors an environment for active risks to human life.", - "The device is used to convey people, or critical property.", - "The device controls robotics in human-accessible spaces.", - "The device controls physical access systems.", - "The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)", - "The device's failure would cause faults in other high-criticality processes." - ], - "validation": { - "required": true - } + 6 + ] + }, + { + "question": "Comments", + "answer": "" } ] } \ No newline at end of file diff --git a/testing/unit/risk_profile/profiles/risk_profile_valid_high.json b/testing/unit/risk_profile/profiles/risk_profile_valid_high.json index 78ed9a5fa..338c91abb 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_valid_high.json +++ b/testing/unit/risk_profile/profiles/risk_profile_valid_high.json @@ -1,162 +1,56 @@ { "name": "Primary profile", + "version": "1.3-alpha", + "created": "2024-07-01", "status": "Valid", - "created": "2024-05-23", - "version": "v1.3", "questions": [ { "question": "What type of device is this?", - "type": "select", - "options": [ - "IoT Sensor", - "IoT Controller", - "Smart Device", - "Something else" - ], - "answer": "IoT Sensor", - "validation": { - "required": true - } + "answer": "IoT Gateway" }, { "question": "How will this device be used at Google?", - "type": "text-long", - "answer": "Installed in a building", - "validation": { - "max": "128", - "required": true - } - }, - { - "question": "What is the email of the device owner(s)?", - "type": "email-multiple", - "answer": "boddey@google.com, cmeredith@google.com", - "validation": { - "required": true, - "max": "128" - } + "answer": "sakjdhaskjdh" }, { "question": "Is this device going to be managed by Google or a third party?", - "type": "select", - "options": [ - "Google", - "Third Party" - ], - "answer": "Google", - "validation": { - "required": true - } + "answer": "Google" }, { "question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", - "type": "select", - "options": [ - "Yes", - "No", - "N/A" - ], - "default": "N/A", - "answer": "Yes", - "validation": { - "required": true - } + "answer": "N/A" }, { - "category": "Data Collection", "question": "Are any of the following statements true about your device?", - "description": "This tells us about the data your device will collect", - "type": "select-multiple", "answer": [ - 0, - 2 - ], - "options": [ - "The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)", - "The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets", - "The device stream confidential business data in real-time (seconds)?" - ], - "validation": { - "required": true - } + 3 + ] }, { - "category": "Data Transmission", "question": "Which of the following statements are true about this device?", - "description": "This tells us about the types of data that are transmitted from this device and how the transmission is performed from a technical standpoint.", - "type": "select-multiple", "answer": [ - 0, - 1, 5 - ], - "options": [ - "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", - "Data transmission occurs across less-trusted networks (e.g. the internet).", - "A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)", - "A confidentiality breach during transmission would have a substantial negative impact", - "The device encrypts data during transmission", - "The device network protocol is well-established and currently used by Google" - ], - "validation": { - "required": true - } + ] }, { - "category": "Data Transmission", "question": "Does the network protocol assure server-to-client identity verification?", - "type": "select", - "answer": "Yes", - "options": [ - "Yes", - "No", - "I don't know" - ], - "validation": { - "required": true - } + "answer": "Yes" }, { - "category": "Remote Operation", "question": "Click the statements that best describe the characteristics of this device.", - "description": "This tells us about how this device is managed remotely.", - "type": "select-multiple", "answer": [ - 0, - 1, - 2 - ], - "options": [ - "PII/PHI, or confidential business data is accessible from the device without authentication", - "Unrecoverable actions (e.g. disk wipe) can be performed remotely", - "Authentication is required for remote access", - "The management interface is accessible from the public internet", - "Static credentials are used for administration" - ], - "validation": { - "required": true - } + 5 + ] }, { - "category": "Operating Environment", "question": "Are any of the following statements true about this device?", - "description": "This informs us about what other systems and processes this device is a part of.", - "type": "select-multiple", "answer": [ - 2, - 3 - ], - "options": [ - "The device monitors an environment for active risks to human life.", - "The device is used to convey people, or critical property.", - "The device controls robotics in human-accessible spaces.", - "The device controls physical access systems.", - "The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)", - "The device's failure would cause faults in other high-criticality processes." - ], - "validation": { - "required": true - } + 6 + ] + }, + { + "question": "Comments", + "answer": "" } ] } \ No newline at end of file diff --git a/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json b/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json index aa6c73297..fba02d4ba 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json +++ b/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json @@ -1,148 +1,56 @@ { "name": "Primary profile", + "version": "1.3-alpha", + "created": "2024-07-01", "status": "Valid", - "created": "2024-05-23", - "version": "v1.3", "questions": [ { "question": "What type of device is this?", - "type": "select", - "options": [ - "IoT Sensor", - "IoT Controller", - "Smart Device", - "Something else" - ], - "answer": "IoT Sensor", - "validation": { - "required": true - } + "answer": "Sensor - Lighting" }, { "question": "How will this device be used at Google?", - "type": "text-long", - "answer": "Installed in a building", - "validation": { - "max": "128", - "required": true - } - }, - { - "question": "What is the email of the device owner(s)?", - "type": "email-multiple", - "answer": "boddey@google.com, cmeredith@google.com", - "validation": { - "required": true, - "max": "128" - } + "answer": "sakjdhaskjdh" }, { "question": "Is this device going to be managed by Google or a third party?", - "type": "select", - "options": [ - "Google", - "Third Party" - ], - "answer": "Google", - "validation": { - "required": true - } + "answer": "Google" }, { "question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", - "type": "select", - "options": [ - "Yes", - "No", - "N/A" - ], - "default": "N/A", - "answer": "Yes", - "validation": { - "required": true - } + "answer": "N/A" }, { - "category": "Data Collection", "question": "Are any of the following statements true about your device?", - "description": "This tells us about the data your device will collect", - "type": "select-multiple", - "answer": [], - "options": [ - "The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)", - "The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets", - "The device stream confidential business data in real-time (seconds)?" - ], - "validation": { - "required": true - } + "answer": [ + 3 + ] }, { - "category": "Data Transmission", "question": "Which of the following statements are true about this device?", - "description": "This tells us about the types of data that are transmitted from this device and how the transmission is performed from a technical standpoint.", - "type": "select-multiple", - "answer": [], - "options": [ - "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", - "Data transmission occurs across less-trusted networks (e.g. the internet).", - "A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)", - "A confidentiality breach during transmission would have a substantial negative impact", - "The device encrypts data during transmission", - "The device network protocol is well-established and currently used by Google" - ], - "validation": { - "required": true - } + "answer": [ + 5 + ] }, { - "category": "Data Transmission", "question": "Does the network protocol assure server-to-client identity verification?", - "type": "select", - "answer": "Yes", - "options": [ - "Yes", - "No", - "I don't know" - ], - "validation": { - "required": true - } + "answer": "Yes" }, { - "category": "Remote Operation", "question": "Click the statements that best describe the characteristics of this device.", - "description": "This tells us about how this device is managed remotely.", - "type": "select-multiple", - "answer": [], - "options": [ - "PII/PHI, or confidential business data is accessible from the device without authentication", - "Unrecoverable actions (e.g. disk wipe) can be performed remotely", - "Authentication is required for remote access", - "The management interface is accessible from the public internet", - "Static credentials are used for administration" - ], - "validation": { - "required": true - } + "answer": [ + 5 + ] }, { - "category": "Operating Environment", "question": "Are any of the following statements true about this device?", - "description": "This informs us about what other systems and processes this device is a part of.", - "type": "select-multiple", - "answer": [], - "options": [ - "The device monitors an environment for active risks to human life.", - "The device is used to convey people, or critical property.", - "The device controls robotics in human-accessible spaces.", - "The device controls physical access systems.", - "The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)", - "The device's failure would cause faults in other high-criticality processes." - ], - "validation": { - "required": true - } + "answer": [ + 6 + ] + }, + { + "question": "Comments", + "answer": "" } ] } \ No newline at end of file diff --git a/testing/unit/risk_profile/risk_profile_test.py b/testing/unit/risk_profile/risk_profile_test.py index c4c80c1e4..23cce43d3 100644 --- a/testing/unit/risk_profile/risk_profile_test.py +++ b/testing/unit/risk_profile/risk_profile_test.py @@ -35,7 +35,9 @@ class RiskProfileTest(unittest.TestCase): def setUpClass(cls): # Create the output directories and ignore errors if it already exists os.makedirs(OUTPUT_DIR, exist_ok=True) - with open('resources/risk_assessment.json', 'r', encoding='utf-8') as file: + with open(os.path.join('resources', + 'risk_assessment.json'), + 'r', encoding='utf-8') as file: cls.profile_format = json.loads(file.read()) def risk_profile_high_test(self): @@ -126,6 +128,36 @@ def risk_profile_expired_test(self): self.assertEqual(risk_profile.status, 'Expired') + def risk_profile_update_risk_test(self): + # Read the risk profile json file + risk_profile_path = os.path.join(PROFILE_DIR, + 'risk_profile_valid_high.json') + with open(risk_profile_path, 'r', encoding='utf-8') as file: + risk_profile_json = json.loads(file.read()) + + # Load the initial risk profile + risk_profile = RiskProfile().load(risk_profile_json, self.profile_format) + + # Check risk profile risk is high + self.assertEqual(risk_profile.risk, 'High') + + # Load updated risk profile path + risk_profile_path = os.path.join(PROFILE_DIR, + 'risk_profile_valid_limited.json') + + # Read the JSON for the updated profile + with open(risk_profile_path, 'r', encoding='utf-8') as file: + risk_profile_json = json.loads(file.read()) + + # Update the risk profile + risk_profile.update(risk_profile_json, self.profile_format) + + # Refresh the risk + risk_profile.update_risk(self.profile_format) + + # Risk should now be limited after update + self.assertEqual(risk_profile.risk, 'Limited') + if __name__ == '__main__': suite = unittest.TestSuite() @@ -134,6 +166,7 @@ def risk_profile_expired_test(self): suite.addTest(RiskProfileTest('risk_profile_rename_test')) suite.addTest(RiskProfileTest('risk_profile_draft_test')) suite.addTest(RiskProfileTest('risk_profile_expired_test')) + suite.addTest(RiskProfileTest('risk_profile_update_risk_test')) runner = unittest.TextTestRunner() runner.run(suite) diff --git a/testing/unit/run.sh b/testing/unit/run.sh index 1ae4d6287..72ca9dcb0 100644 --- a/testing/unit/run.sh +++ b/testing/unit/run.sh @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -docker run --rm -it --name unit-test test-run/unit-test /bin/bash ./run_tests.sh \ No newline at end of file +sudo docker run --rm -it --name unit-test testrun/unit-test /bin/bash ./run_tests.sh \ No newline at end of file diff --git a/testing/unit/run_tests.sh b/testing/unit/run_tests.sh index cec5452fd..48c667934 100644 --- a/testing/unit/run_tests.sh +++ b/testing/unit/run_tests.sh @@ -1,69 +1,69 @@ -#!/bin/bash -e - -# Copyright 2023 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script should be run from within the unit_test directory. If -# it is run outside this directory, paths will not be resolved correctly. - -# Move into the root directory of test-run -pushd ../../ >/dev/null 2>&1 - -echo "Root dir: $PWD" - -# Add the framework sources -PYTHONPATH="$PWD/framework/python/src:$PWD/framework/python/src/common" - -# Add the test module sources -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/base/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/conn/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/tls/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/dns/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/services/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/ntp/python/src" -PYTHONPATH="$PYTHONPATH:$PWD/modules/test/protocol/python/src" - - -# Set the python path with all sources -export PYTHONPATH - -# Run the DHCP Unit tests -python3 -u $PWD/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py -python3 -u $PWD/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py - -# Run the Conn Module Unit Tests -python3 -u $PWD/testing/unit/conn/conn_module_test.py - -# Run the TLS Module Unit Tests -python3 -u $PWD/testing/unit/tls/tls_module_test.py - -# Run the DNS Module Unit Tests -python3 -u $PWD/testing/unit/dns/dns_module_test.py - -# Run the NMAP Module Unit Tests -python3 -u $PWD/testing/unit/services/services_module_test.py - -# Run the NTP Module Unit Tests -python3 -u $PWD/testing/unit/ntp/ntp_module_test.py - -# Run the Report Unit Tests -python3 -u $PWD/testing/unit/report/report_test.py - -# Run the RiskProfile Unit Tests -python3 -u $PWD/testing/unit/risk_profile/risk_profile_test.py - -# Run the RiskProfile Unit Tests -python3 -u $PWD/testing/unit/protocol/protocol_module_test.py - -popd >/dev/null 2>&1 \ No newline at end of file +#!/bin/bash -e + +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script should be run from within the unit_test directory. If +# it is run outside this directory, paths will not be resolved correctly. + +# Move into the root directory of test-run +pushd ../../ >/dev/null 2>&1 + +echo "Root dir: $PWD" + +# Add the framework sources +PYTHONPATH="$PWD/framework/python/src:$PWD/framework/python/src/common" + +# Add the test module sources +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/base/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/conn/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/tls/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/dns/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/services/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/ntp/python/src" +PYTHONPATH="$PYTHONPATH:$PWD/modules/test/protocol/python/src" + + +# Set the python path with all sources +export PYTHONPATH + +# Run the DHCP Unit tests +python3 -u $PWD/modules/network/dhcp-1/python/src/grpc_server/dhcp_config_test.py +python3 -u $PWD/modules/network/dhcp-2/python/src/grpc_server/dhcp_config_test.py + +# Run the Conn Module Unit Tests +python3 -u $PWD/testing/unit/conn/conn_module_test.py + +# Run the TLS Module Unit Tests +python3 -u $PWD/testing/unit/tls/tls_module_test.py + +# Run the DNS Module Unit Tests +python3 -u $PWD/testing/unit/dns/dns_module_test.py + +# Run the NMAP Module Unit Tests +python3 -u $PWD/testing/unit/services/services_module_test.py + +# Run the NTP Module Unit Tests +python3 -u $PWD/testing/unit/ntp/ntp_module_test.py + +# Run the Report Unit Tests +python3 -u $PWD/testing/unit/report/report_test.py + +# Run the RiskProfile Unit Tests +python3 -u $PWD/testing/unit/risk_profile/risk_profile_test.py + +# Run the RiskProfile Unit Tests +python3 -u $PWD/testing/unit/protocol/protocol_module_test.py + +popd >/dev/null 2>&1 diff --git a/testing/unit/services/reports/nmap_report_all_closed_local.html b/testing/unit/services/reports/services_report_all_closed_local.html similarity index 100% rename from testing/unit/services/reports/nmap_report_all_closed_local.html rename to testing/unit/services/reports/services_report_all_closed_local.html diff --git a/testing/unit/services/reports/nmap_report_local.html b/testing/unit/services/reports/services_report_local.html similarity index 100% rename from testing/unit/services/reports/nmap_report_local.html rename to testing/unit/services/reports/services_report_local.html diff --git a/testing/unit/services/services_module_test.py b/testing/unit/services/services_module_test.py index a24a21d50..30c4928bf 100644 --- a/testing/unit/services/services_module_test.py +++ b/testing/unit/services/services_module_test.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Module run all the DNS related unit tests""" +"""Module run all the services related unit tests""" from services_module import ServicesModule import unittest import os @@ -26,13 +26,13 @@ REPORTS_DIR = os.path.join(TEST_FILES_DIR, 'reports/') RESULTS_DIR = os.path.join(TEST_FILES_DIR, 'results/') -LOCAL_REPORT = os.path.join(REPORTS_DIR, 'nmap_report_local.html') +LOCAL_REPORT = os.path.join(REPORTS_DIR, 'services_report_local.html') LOCAL_REPORT_ALL_CLOSED = os.path.join(REPORTS_DIR, - 'nmap_report_all_closed_local.html') + 'services_report_all_closed_local.html') CONF_FILE = 'modules/test/' + MODULE + '/conf/module_config.json' -class NMAPTest(unittest.TestCase): +class ServicesTest(unittest.TestCase): """Contains and runs all the unit tests concerning DNS behaviors""" @classmethod @@ -41,7 +41,7 @@ def setUpClass(cls): os.makedirs(OUTPUT_DIR, exist_ok=True) # Test the module report generation - def nmap_module_ports_open_report_test(self): + def services_module_ports_open_report_test(self): # Move test scan into expected folder src_scan_results_path = os.path.join(RESULTS_DIR, 'ports_open_scan_result.json') @@ -49,14 +49,14 @@ def nmap_module_ports_open_report_test(self): OUTPUT_DIR, 'services_scan_results.json') shutil.copy(src_scan_results_path, dst_scan_results_path) - nmap_module = ServicesModule(module=MODULE, + services_module = ServicesModule(module=MODULE, log_dir=OUTPUT_DIR, conf_file=CONF_FILE, results_dir=OUTPUT_DIR, run=False, nmap_scan_results_path=OUTPUT_DIR) - report_out_path = nmap_module.generate_module_report() + report_out_path = services_module.generate_module_report() # Read the generated report with open(report_out_path, 'r', encoding='utf-8') as file: @@ -64,7 +64,8 @@ def nmap_module_ports_open_report_test(self): formatted_report = self.add_formatting(report_out) # Write back the new formatted_report value - out_report_path = os.path.join(OUTPUT_DIR, 'nmap_report_ports_open.html') + out_report_path = os.path.join( + OUTPUT_DIR, 'services_report_ports_open.html') with open(out_report_path, 'w', encoding='utf-8') as file: file.write(formatted_report) @@ -75,21 +76,21 @@ def nmap_module_ports_open_report_test(self): self.assertEqual(report_out, report_local) # Test the module report generation with all ports closed - def nmap_module_report_all_closed_test(self): + def services_module_report_all_closed_test(self): src_scan_results_path = os.path.join(RESULTS_DIR, 'all_closed_scan_result.json') dst_scan_results_path = os.path.join( OUTPUT_DIR, 'services_scan_results.json') shutil.copy(src_scan_results_path, dst_scan_results_path) - nmap_module = ServicesModule(module=MODULE, + services_module = ServicesModule(module=MODULE, log_dir=OUTPUT_DIR, conf_file=CONF_FILE, results_dir=OUTPUT_DIR, run=False, nmap_scan_results_path=OUTPUT_DIR) - report_out_path = nmap_module.generate_module_report() + report_out_path = services_module.generate_module_report() # Read the generated report with open(report_out_path, 'r', encoding='utf-8') as file: @@ -97,7 +98,8 @@ def nmap_module_report_all_closed_test(self): formatted_report = self.add_formatting(report_out) # Write back the new formatted_report value - out_report_path = os.path.join(OUTPUT_DIR, 'nmap_report_all_closed.html') + out_report_path = os.path.join( + OUTPUT_DIR, 'services_report_all_closed.html') with open(out_report_path, 'w', encoding='utf-8') as file: file.write(formatted_report) @@ -121,8 +123,8 @@ def add_formatting(self, body): if __name__ == '__main__': suite = unittest.TestSuite() # Module report test - suite.addTest(NMAPTest('nmap_module_ports_open_report_test')) - suite.addTest(NMAPTest('nmap_module_report_all_closed_test')) + suite.addTest(ServicesTest('services_module_ports_open_report_test')) + suite.addTest(ServicesTest('services_module_report_all_closed_test')) runner = unittest.TextTestRunner() runner.run(suite) diff --git a/testing/unit/unit_test.Dockerfile b/testing/unit/unit_test.Dockerfile index 3de2ec974..c8e0c468a 100644 --- a/testing/unit/unit_test.Dockerfile +++ b/testing/unit/unit_test.Dockerfile @@ -9,9 +9,10 @@ ENV DEBIAN_FRONTEND=noninteractive ARG MODULE_DIR=modules/test ARG UNIT_TEST_DIR=testing/unit ARG FRAMEWORK_DIR=framework +ARG RESOURCES_DIR=resources # Install common software -RUN apt-get install -yq net-tools iputils-ping tzdata tcpdump iproute2 jq python3 python3-pip dos2unix nmap wget --fix-missing +RUN apt-get install -yq net-tools iputils-ping tzdata tcpdump iproute2 jq python3 python3-pip dos2unix nmap wget libpangocairo-1.0-0 --fix-missing # Install framework python modules COPY $FRAMEWORK_DIR/ /testrun/$FRAMEWORK_DIR @@ -20,6 +21,9 @@ COPY $FRAMEWORK_DIR/ /testrun/$FRAMEWORK_DIR COPY $MODULE_DIR/ /testrun/$MODULE_DIR COPY $UNIT_TEST_DIR /testrun/$UNIT_TEST_DIR +# Copy resources folder +COPY $RESOURCES_DIR /testrun/resources + # Install required software for TLS module RUN apt-get update && apt-get install -y tshark @@ -29,6 +33,9 @@ RUN pip3 install -r /testrun/framework/requirements.txt # Install all python requirements for the TLS module RUN pip3 install -r /testrun/modules/test/tls/python/requirements.txt +# Install all python requirements for the services module +RUN pip3 install -r /testrun/modules/test/services/python/requirements.txt + # Remove incorrect line endings RUN dos2unix /testrun/modules/test/tls/bin/* RUN dos2unix /testrun/testing/unit/* @@ -38,5 +45,3 @@ RUN chmod u+x /testrun/modules/test/tls/bin/* RUN chmod u+x /testrun/testing/unit/run_tests.sh WORKDIR /testrun/testing/unit/ - -#ENTRYPOINT [ "./run_tests.sh" ] \ No newline at end of file