From 55931d58b85e36c07f6266af42514ea47df721f7 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Fri, 21 Jun 2024 12:50:14 +0100 Subject: [PATCH 1/8] Update risk profile logic --- framework/python/src/api/api.py | 5 +- framework/python/src/common/risk_profile.py | 130 +++++++++--- framework/python/src/common/session.py | 72 +++++-- local/risk_profiles/Primary profile.json | 2 +- resources/risk_assessment.json | 218 ++++++++++++++++---- 5 files changed, 333 insertions(+), 94 deletions(-) diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index 40b6fed34..d81432321 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -679,7 +679,10 @@ def get_profiles_format(self, response: Response): return self.get_session().get_profiles_format() def get_profiles(self): - return self.get_session().get_profiles() + profiles = [] + for profile in self.get_session().get_profiles(): + profiles.append(json.loads(profile.to_json())) + return profiles async def update_profile(self, request: Request, response: Response): diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 0b89feaa9..e9d25dbf8 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Stores additional information about a device's risk""" -#import datetime from datetime import datetime from common import logger import json @@ -30,35 +29,39 @@ class RiskProfile(): """Python representation of a risk profile""" def __init__(self, profile_json=None, profile_format=None): + if profile_json is None or profile_format is None: return + self.name = profile_json['name'] - self.created = datetime.now().strftime('%Y-%m-%d') + self.created = datetime.now() self.version = profile_json['version'] self.questions = profile_json['questions'] self.status = None self.categories = None self.risk = None + self._validate(profile_json, profile_format) - self._update_categories() - self._update_risk() + self._update_risk(profile_format) # Load a profile without modifying the created date # but still validate the profile def load(self, profile_json, profile_format): self.name = profile_json['name'] - self.created = profile_json['created'] + self.created = datetime.strptime( + profile_json['created'], '%Y-%m-%d') self.version = profile_json['version'] self.questions = profile_json['questions'] self.status = None self.categories = None self._validate(profile_json, profile_format) - self._update_categories() - self._update_risk() + self._update_risk(profile_format) + return self def update(self, profile_json, profile_format): + # Construct a new profile from json data new_profile = RiskProfile(profile_json, profile_format) @@ -71,7 +74,6 @@ def update(self, profile_json, profile_format): self.created = new_profile.created self.questions = new_profile.questions self.status = new_profile.status - self.categories = new_profile.categories def _validate(self, profile_json, profile_format): if self._valid(profile_json, profile_format): @@ -79,29 +81,84 @@ def _validate(self, profile_json, profile_format): else: self.status = 'Draft' - def _update_categories(self): - if self.status == 'Valid': - self.categories = [] - self.categories.append( - self._get_category_status(DATA_COLLECTION_CATEGORY)) - self.categories.append( - self._get_category_status(DATA_TRANSMISSION_CATEGORY)) - self.categories.append( - self._get_category_status(REMOTE_OPERATION_CATEGORY)) - self.categories.append( - self._get_category_status(OPERATING_ENVIRONMENT_CATEGORY)) - - def _update_risk(self): + def _update_risk(self, profile_format): + if self.status == 'Valid': + + # Default risk = Limited risk = 'Limited' - for category in self.categories: - if 'status' in category and category['status'] == 'High': + + # Check each question in profile + for question in self.questions: + question_text = question['question'] + + # Fetch the risk levels from the profile format + format_q = self._get_format_question( + question_text, 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'] + + question_risk = 'Limited' + + # The answer is a single string (select) + if isinstance(answer, str): + + format_option = self._get_format_question_option( + format_q, answer) + + # Format options may just be a list of strings with + # no risk attached + if format_option is None: + continue + + if 'risk' in format_option and format_option['risk'] == 'High': + question_risk = format_option['risk'] + + # A list of indexes is the answer (select-multiple) + elif isinstance(answer, list): + + format_options = format_q['options'] + + for index in answer: + option = self._get_option_from_index(format_options, index) + + if option is None: + LOGGER.error('Answer had an invalid index') + continue + + if 'risk' in option and option['risk'] == 'High': + question_risk = 'High' + + question['risk'] = question_risk + + for question in self.questions: + 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): + for q in profile_format: + if q['question'] == question: + return q + return None + + def _get_option_from_index(self, options, index): + i = 0 + for option in options: + if i == index: + return option + i+=1 + return None + def _check_answer(self, question): status = 'Limited' if question['validation']['required']: @@ -114,14 +171,6 @@ def _check_answer(self, question): status = 'High' return status - def _get_category_status(self, category): - status = 'Limited' - for question in self.questions: - if 'category' in question and question['category'] == category: - if question['validation']['required']: - status = 'High' if self._check_answer(question) == 'High' else status - return {'name': category, 'status': status} - def _get_profile_question(self, profile_json, question): for q in profile_json['questions']: @@ -130,6 +179,19 @@ def _get_profile_question(self, profile_json, question): return None + def _get_format_question_option(self, question_obj, answer): + + for option in question_obj['options']: + + # Ignore just string lists + if isinstance(option, str): + continue + + if option['text'] == answer: + return option + + return None + def _valid(self, profile_json, profile_format): # Check name field is present @@ -165,8 +227,7 @@ def _valid(self, profile_json, profile_format): def _expired(self): # Check expiry - created_date = datetime.strptime( - self.created, '%Y-%m-%d').timestamp() + created_date = self.created.timestamp() today = datetime.now().timestamp() return created_date < (today - SECONDS_IN_YEAR) @@ -174,8 +235,9 @@ def to_json(self, pretty=False): json_dict = { 'name': self.name, 'version': self.version, - 'created': self.created, + 'created': self.created.strftime('%Y-%m-%d'), 'status': self.status, + 'risk': self.risk, 'questions': self.questions } if self.categories is not None: diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index 341a08791..038a43879 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -82,7 +82,11 @@ def __init__(self, root_dir): # Profiles self._profiles = [] + # Profile format that is passed to the frontend + # (excluding internal properties) self._profile_format_json = None + # Profile format used for internal validation + self._profile_format = None # System configuration self._config_file = os.path.join(root_dir, CONFIG_FILE_PATH) @@ -359,34 +363,60 @@ def _load_profiles(self): with open(os.path.join( self._root_dir, PROFILE_FORMAT_PATH ), encoding='utf-8') as profile_format_file: - self._profile_format_json = json.load(profile_format_file) + + format_json = json.load(profile_format_file) + + # Save original profile format for internal validation + self._profile_format = format_json + except (IOError, ValueError) as e: LOGGER.error( 'An error occurred whilst loading the risk assessment format') LOGGER.debug(e) + profile_format_array = [] + + # Remove internal properties + for question_obj in format_json: + new_obj = {} + for key in question_obj: + if key == 'options': + options = [] + for option in question_obj[key]: + if isinstance(option, str): + options.append(option) + else: + options.append(option['text']) + new_obj['options'] = options + else: + new_obj[key] = question_obj[key] + + profile_format_array.append(new_obj) + + self._profile_format_json = profile_format_array + # Load existing profiles LOGGER.debug('Loading risk profiles') - try: - for risk_profile_file in os.listdir(os.path.join( - self._root_dir, PROFILES_DIR - )): - LOGGER.debug(f'Discovered profile {risk_profile_file}') - - with open(os.path.join( - self._root_dir, PROFILES_DIR, risk_profile_file - ), encoding='utf-8') as f: - json_data = json.load(f) - risk_profile = RiskProfile() - risk_profile = risk_profile.load( - profile_json=json_data, - profile_format=self.get_profiles_format()) - self._profiles.append(risk_profile) + #try: + for risk_profile_file in os.listdir(os.path.join( + self._root_dir, PROFILES_DIR + )): + LOGGER.debug(f'Discovered profile {risk_profile_file}') - except Exception as e: - LOGGER.error('An error occurred whilst loading risk profiles') - LOGGER.debug(e) + with open(os.path.join( + self._root_dir, PROFILES_DIR, risk_profile_file + ), encoding='utf-8') as f: + json_data = json.load(f) + risk_profile = RiskProfile() + risk_profile = risk_profile.load( + profile_json=json_data, + profile_format=self._profile_format) + self._profiles.append(risk_profile) + + # except Exception as e: + # LOGGER.error('An error occurred whilst loading risk profiles') + # LOGGER.debug(e) def get_profiles_format(self): return self._profile_format_json @@ -413,12 +443,12 @@ def update_profile(self, profile_json): # Create a new risk profile risk_profile = RiskProfile(profile_json=profile_json, - profile_format=self.get_profiles_format()) + profile_format=self._profile_format) self._profiles.append(risk_profile) else: - risk_profile.update(profile_json, self.get_profiles_format()) + risk_profile.update(profile_json, self._profile_format) # Check if name has changed if 'rename' in profile_json: # Delete the original file diff --git a/local/risk_profiles/Primary profile.json b/local/risk_profiles/Primary profile.json index f5dcf53a1..5ddf7b85f 100644 --- a/local/risk_profiles/Primary profile.json +++ b/local/risk_profiles/Primary profile.json @@ -1 +1 @@ -{"name": "Primary profile", "version": "1.3-alpha", "created": "2024-06-18", "status": "Valid", "questions": [{"question": "What type of device is this?", "answer": "IoT Sensor"}, {"question": "How will this device be used at Google?", "answer": "Hey"}, {"question": "Is this device going to be managed by Google or a third party?", "answer": "Managed by Google"}, {"question": "What is the email of the device owner(s)?", "answer": "boddey@google.com, cmeredith@google.com"}, {"question": "Is this device going to be managed by Google or a third party?", "answer": "Google"}, {"question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", "answer": "Yes"}, {"question": "Are any of the following statements true about your device?", "answer": [0, 1]}, {"question": "Which of the following statements are true about this device?", "answer": [1, 2, 3]}, {"question": "Does the network protocol assure server-to-client identity verification?", "answer": "Yes"}, {"question": "Click the statements that best describe the characteristics of this device.", "answer": [0, 1]}, {"question": "Are any of the following statements true about this device?", "answer": [2, 3, 5]}], "categories": [{"name": "Data Collection", "status": "Limited"}, {"name": "Data Transmission", "status": "Limited"}, {"name": "Remote Operation", "status": "Limited"}, {"name": "Operating Environment", "status": "Limited"}]} \ No newline at end of file +{"name": "Primary profile", "version": "1.3-alpha", "created": "2024-06-21", "status": "Valid", "risk": "High", "questions": [{"question": "What type of device is this?", "answer": "IoT Sensor"}, {"question": "How will this device be used at Google?", "answer": "Hey"}, {"question": "What is the email of the device owner(s)?", "answer": "boddey@google.com, cmeredith@google.com"}, {"question": "Is this device going to be managed by Google or a third party?", "answer": "Google", "risk": "Limited"}, {"question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", "answer": "Yes", "risk": "Limited"}, {"question": "Are any of the following statements true about your device?", "answer": [3], "risk": "Limited"}, {"question": "Which of the following statements are true about this device?", "answer": [6], "risk": "High"}, {"question": "Does the network protocol assure server-to-client identity verification?", "answer": "Yes", "risk": "Limited"}, {"question": "Click the statements that best describe the characteristics of this device.", "answer": [4], "risk": "High"}, {"question": "Are any of the following statements true about this device?", "answer": [0], "risk": "High"}]} \ No newline at end of file diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json index f774c1ece..03037206c 100644 --- a/resources/risk_assessment.json +++ b/resources/risk_assessment.json @@ -3,10 +3,56 @@ "question": "What type of device is this?", "type": "select", "options": [ - "IoT Sensor", - "IoT Controller", - "Smart Device", - "Something else" + "Analytics - Energy Modeling", + "Analytics - HVAC Optimization", + "Analytics - Load Disaggregation", + "Analytics - Occupancy", + "Analytics - Personal Profile", + "Analytics - Time Management", + "Analytics - Other", + "Building Automation Gateway", + "IoT Gateway", + "Controller - AHU", + "Controller - Boiler", + "Controller - Chiller", + "Controller - FCU", + "Controller - Pump", + "Controller - CRAC", + "Controller - VAV", + "Controller - VRF", + "Controller - Multiple", + "Controller - Other", + "Controller - Lighting", + "Controller - Blinds/Facades", + "Controller - Lifts/Elevators", + "Controller - UPS", + "Sensor - Air Quality", + "Sensor - Vibration", + "Sensor - Humidity", + "Sensor - Water", + "Sensor - Occupancy", + "Sensor - Volume", + "Sensor - Weight", + "Sensor - Weather", + "Sensor - Steam", + "Sensor - Air Flow", + "Sensor - Lighting", + "Sensor - Other", + "Sensor - Air Quality", + "Monitoring - Fire System", + "Monitoring - Emergency Lighting", + "Monitoring - Other", + "Monitoring - UPS", + "Meter - Water", + "Meter - Gas", + "Meter - Electricity", + "Meter - Other", + "Camera", + "Badge Reader", + "Other", + "Data - Storage", + "Data - Processing", + "Touch Panel" ], "validation": { "required": true @@ -14,14 +60,16 @@ }, { "question": "How will this device be used at Google?", + "description": "Desribe your use case. Add links to user journey diagrams and TDD if available.", "type": "text-long", "validation": { - "max": "128", + "max": "512", "required": true } }, { "question": "What is the email of the device owner(s)?", + "description": "The device owner is the person or group responsible for the device", "type": "email-multiple", "validation": { "required": true, @@ -30,10 +78,15 @@ }, { "question": "Is this device going to be managed by Google or a third party?", + "description": "A manufacturer or supplier is considered third party in this case", "type": "select", "options": [ - "Google", - "Third Party" + { + "text": "Google" + }, + { + "text": "Third Party" + } ], "validation": { "required": true @@ -43,9 +96,15 @@ "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" + { + "text": "Yes" + }, + { + "text": "No" + }, + { + "text": "N/A" + } ], "default": "N/A", "validation": { @@ -58,10 +117,22 @@ "description": "This tells us about the data your device will collect", "type": "select-multiple", "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)?", - "None of the above" + { + "text": "The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)", + "risk": "High" + }, + { + "text": "The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets", + "risk": "High" + }, + { + "text": "The device streams confidential business data in real-time (seconds)?", + "risk": "High" + }, + { + "text": "None of the above", + "risk": "Limited" + } ], "validation": { "required": true @@ -73,13 +144,38 @@ "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", "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", - "None of the above" + { + "text": "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", + "risk": "High" + }, + { + "text": "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", + "risk": "High" + }, + { + "text": "Data transmission occurs across less-trusted networks (e.g. the internet).", + "risk": "High" + }, + { + "text": "A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)", + "risk": "High" + }, + { + "text": "A confidentiality breach during transmission would have a substantial negative impact", + "risk": "High" + }, + { + "text": "The device encrypts data during transmission", + "risk": "High" + }, + { + "text": "The device network protocol is well-established and currently used by Google", + "risk": "High" + }, + { + "text": "None of the above", + "risk": "Limited" + } ], "validation": { "required": true @@ -90,9 +186,18 @@ "question": "Does the network protocol assure server-to-client identity verification?", "type": "select", "options": [ - "Yes", - "No", - "I don't know" + { + "text": "Yes", + "risk": "Limited" + }, + { + "text": "No", + "risk": "High" + }, + { + "text": "I don't know" + } + ], "validation": { "required": true @@ -104,12 +209,30 @@ "description": "This tells us about how this device is managed remotely.", "type": "select-multiple", "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", - "None of the above" + { + "text": "PII/PHI, or confidential business data is accessible from the device without authentication", + "risk": "High" + }, + { + "text": "Unrecoverable actions (e.g. disk wipe) can be performed remotely", + "risk": "High" + }, + { + "text": "Authentication is required for remote access", + "risk": "High" + }, + { + "text": "The management interface is accessible from the public internet", + "risk": "High" + }, + { + "text": "Static credentials are used for administration", + "risk": "High" + }, + { + "text": "None of the above", + "risk": "Limited" + } ], "validation": { "required": true @@ -121,13 +244,34 @@ "description": "This informs us about what other systems and processes this device is a part of.", "type": "select-multiple", "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.", - "None of the above" + { + "text": "The device monitors an environment for active risks to human life.", + "risk": "High" + }, + { + "text": "The device is used to convey people, or critical property.", + "risk": "High" + }, + { + "text": "The device controls robotics in human-accessible spaces.", + "risk": "High" + }, + { + "text": "The device controls physical access systems.", + "risk": "High" + }, + { + "text": "The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)", + "risk": "High" + }, + { + "text": "The device's failure would cause faults in other high-criticality processes.", + "risk": "High" + }, + { + "text": "None of the above", + "risk": "Limited" + } ], "validation": { "required": true From 34ac20de87aa7bf9797ff7af712b45e35ff967b4 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Mon, 24 Jun 2024 11:34:32 -0600 Subject: [PATCH 2/8] Update test profiles Remove duplicate test file Cleanup temp test files --- .gitignore | 1 + .../output/risk_profile_draft.json | 144 -------------- .../output/risk_profile_expired.json | 162 ---------------- .../output/risk_profile_high.json | 180 ------------------ .../output/risk_profile_limited.json | 166 ---------------- .../output/risk_profile_renamed.json | 180 ------------------ .../profiles/risk_profile_draft.json | 2 +- .../profiles/risk_profile_expired.json | 2 +- .../profiles/risk_profile_test.py | 124 ------------ .../profiles/risk_profile_valid_high.json | 2 +- .../profiles/risk_profile_valid_limited.json | 2 +- .../unit/risk_profile/risk_profile_test.py | 6 +- testing/unit/run_tests.sh | 126 ++++++------ 13 files changed, 71 insertions(+), 1026 deletions(-) delete mode 100644 testing/unit/risk_profile/output/risk_profile_draft.json delete mode 100644 testing/unit/risk_profile/output/risk_profile_expired.json delete mode 100644 testing/unit/risk_profile/output/risk_profile_high.json delete mode 100644 testing/unit/risk_profile/output/risk_profile_limited.json delete mode 100644 testing/unit/risk_profile/output/risk_profile_renamed.json delete mode 100644 testing/unit/risk_profile/profiles/risk_profile_test.py diff --git a/.gitignore b/.gitignore index 26fdf0fcd..82b6bbf64 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ testing/unit/ntp/output/ testing/unit/tls/output/ testing/unit/tls/tmp/ testing/unit/report/output/ +testing/unit/risk_profile/output/ *.deb make/DEBIAN/postinst diff --git a/testing/unit/risk_profile/output/risk_profile_draft.json b/testing/unit/risk_profile/output/risk_profile_draft.json deleted file mode 100644 index ce13703a8..000000000 --- a/testing/unit/risk_profile/output/risk_profile_draft.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "name": "Primary profile", - "version": "v1.3", - "created": "2024-06-17 09:33:09.527835", - "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 - } - }, - { - "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" - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - } - ] -} \ No newline at end of file diff --git a/testing/unit/risk_profile/output/risk_profile_expired.json b/testing/unit/risk_profile/output/risk_profile_expired.json deleted file mode 100644 index 9baee34a7..000000000 --- a/testing/unit/risk_profile/output/risk_profile_expired.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "name": "Primary profile", - "version": "v1.3", - "created": "2022-05-23 12:38:26", - "status": "Expired", - "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 - } - }, - { - "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" - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - } - ] -} \ No newline at end of file diff --git a/testing/unit/risk_profile/output/risk_profile_high.json b/testing/unit/risk_profile/output/risk_profile_high.json deleted file mode 100644 index 6f20bc0b4..000000000 --- a/testing/unit/risk_profile/output/risk_profile_high.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "name": "Primary profile", - "version": "v1.3", - "created": "2024-06-17 09:33:09.475069", - "status": "Valid", - "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 - } - }, - { - "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" - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - } - ], - "categories": [ - { - "name": "Data Collection", - "status": "High" - }, - { - "name": "Data Transmission", - "status": "High" - }, - { - "name": "Remote Operation", - "status": "High" - }, - { - "name": "Operating Environment", - "status": "High" - } - ] -} \ No newline at end of file diff --git a/testing/unit/risk_profile/output/risk_profile_limited.json b/testing/unit/risk_profile/output/risk_profile_limited.json deleted file mode 100644 index ac739bc3a..000000000 --- a/testing/unit/risk_profile/output/risk_profile_limited.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "name": "Primary profile", - "version": "v1.3", - "created": "2024-06-17 09:33:09.495570", - "status": "Valid", - "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 - } - }, - { - "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" - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - } - ], - "categories": [ - { - "name": "Data Collection", - "status": "Limited" - }, - { - "name": "Data Transmission", - "status": "High" - }, - { - "name": "Remote Operation", - "status": "Limited" - }, - { - "name": "Operating Environment", - "status": "Limited" - } - ] -} \ No newline at end of file diff --git a/testing/unit/risk_profile/output/risk_profile_renamed.json b/testing/unit/risk_profile/output/risk_profile_renamed.json deleted file mode 100644 index 57fa86111..000000000 --- a/testing/unit/risk_profile/output/risk_profile_renamed.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "name": "Primary profile renamed", - "version": "v1.3", - "created": "2024-06-17 09:33:09.505411", - "status": "Valid", - "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 - } - }, - { - "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" - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - }, - { - "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 - } - } - ], - "categories": [ - { - "name": "Data Collection", - "status": "High" - }, - { - "name": "Data Transmission", - "status": "High" - }, - { - "name": "Remote Operation", - "status": "High" - }, - { - "name": "Operating Environment", - "status": "High" - } - ] -} \ No newline at end of file diff --git a/testing/unit/risk_profile/profiles/risk_profile_draft.json b/testing/unit/risk_profile/profiles/risk_profile_draft.json index 56aa6f255..315ec3417 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_draft.json +++ b/testing/unit/risk_profile/profiles/risk_profile_draft.json @@ -1,7 +1,7 @@ { "name": "Primary profile", "status": "Valid", - "created": "2024-05-23 12:38:26", + "created": "2024-05-23", "version": "v1.3", "questions": [ { diff --git a/testing/unit/risk_profile/profiles/risk_profile_expired.json b/testing/unit/risk_profile/profiles/risk_profile_expired.json index ca9cf5817..5bcedab07 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_expired.json +++ b/testing/unit/risk_profile/profiles/risk_profile_expired.json @@ -1,7 +1,7 @@ { "name": "Primary profile", "status": "Valid", - "created": "2022-05-23 12:38:26", + "created": "2022-05-23", "version": "v1.3", "questions": [ { diff --git a/testing/unit/risk_profile/profiles/risk_profile_test.py b/testing/unit/risk_profile/profiles/risk_profile_test.py deleted file mode 100644 index 743e41f8a..000000000 --- a/testing/unit/risk_profile/profiles/risk_profile_test.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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. -"""Module run all the Risk Profile related unit tests""" -import unittest -import os -import json -from risk_profile import RiskProfile -SECONDS_IN_YEAR = 31536000 - -MODULE = 'risk_profile' - -# Define the file paths -UNIT_TEST_DIR = 'testing/unit/' -TEST_FILES_DIR = os.path.join('testing/unit', MODULE) -OUTPUT_DIR = os.path.join(TEST_FILES_DIR, 'output/') - - -class RiskProfileTest(unittest.TestCase): - """Contains and runs all the unit tests concerning DNS behaviors""" - - @classmethod - 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: - cls.profile_format = json.loads(file.read()) - - - def risk_profile_high_test(self): - # Read the risk profile json file - risk_profile_path = os.path.join(TEST_FILES_DIR, 'risk_profile_valid_high.json') - with open(risk_profile_path, 'r', encoding='utf-8') as file: - risk_profile_json = json.loads(file.read()) - - # Create the RiskProfile object from the json file - risk_profile = RiskProfile(risk_profile_json,self.profile_format) - - # Write the profile to file - output_file = os.path.join(OUTPUT_DIR,'risk_profile_high.json') - with open(output_file, 'w', encoding='utf-8') as file: - file.write(risk_profile.to_json(pretty=True)) - - def risk_profile_limited_test(self): - # Read the risk profile json file - risk_profile_path = os.path.join(TEST_FILES_DIR, 'risk_profile_valid_limited.json') - with open(risk_profile_path, 'r', encoding='utf-8') as file: - risk_profile_json = json.loads(file.read()) - - # Create the RiskProfile object from the json file - risk_profile = RiskProfile(risk_profile_json,self.profile_format) - - # Write the profile to file - output_file = os.path.join(OUTPUT_DIR,'risk_profile_limited.json') - with open(output_file, 'w', encoding='utf-8') as file: - file.write(risk_profile.to_json(pretty=True)) - - def risk_profile_rename_test(self): - # Read the risk profile json file - risk_profile_path = os.path.join(TEST_FILES_DIR, 'risk_profile_valid_high.json') - with open(risk_profile_path, 'r', encoding='utf-8') as file: - risk_profile_json = json.loads(file.read()) - - # Create the RiskProfile object from the json file - risk_profile = RiskProfile(risk_profile_json,self.profile_format) - - # Rename the profile - risk_profile_json['rename'] = 'Primary profile renamed' - risk_profile.update(risk_profile_json,self.profile_format) - - # Write the renamed profile to file - output_file = os.path.join(OUTPUT_DIR,'risk_profile_renamed.json') - with open(output_file, 'w', encoding='utf-8') as file: - file.write(risk_profile.to_json(pretty=True)) - - def risk_profile_draft_test(self): - # Read the risk profile json file - risk_profile_path = os.path.join(TEST_FILES_DIR, 'risk_profile_draft.json') - with open(risk_profile_path, 'r', encoding='utf-8') as file: - risk_profile_json = json.loads(file.read()) - - # Create the RiskProfile object from the json file - risk_profile = RiskProfile(risk_profile_json,self.profile_format) - - # Write the profile to file - output_file = os.path.join(OUTPUT_DIR,'risk_profile_draft.json') - with open(output_file, 'w', encoding='utf-8') as file: - file.write(risk_profile.to_json(pretty=True)) - - def risk_profile_expired_test(self): - # Read the risk profile json file - risk_profile_path = os.path.join(TEST_FILES_DIR, 'risk_profile_draft.json') - with open(risk_profile_path, 'r', encoding='utf-8') as file: - risk_profile_json = json.loads(file.read()) - - # Create the RiskProfile object from the json file - risk_profile = RiskProfile(risk_profile_json,self.profile_format) - - # Write the profile to file - output_file = os.path.join(OUTPUT_DIR,'risk_profile_draft.json') - with open(output_file, 'w', encoding='utf-8') as file: - file.write(risk_profile.to_json(pretty=True)) - -if __name__ == '__main__': - suite = unittest.TestSuite() - - # suite.addTest(RiskProfileTest('risk_profile_high_test')) - # suite.addTest(RiskProfileTest('risk_profile_limited_test')) - # suite.addTest(RiskProfileTest('risk_profile_rename_test')) - suite.addTest(RiskProfileTest('risk_profile_draft_test')) - suite.addTest(RiskProfileTest('risk_profile_expired_test')) - - runner = unittest.TextTestRunner() - runner.run(suite) \ 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 af96fe558..78ed9a5fa 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_valid_high.json +++ b/testing/unit/risk_profile/profiles/risk_profile_valid_high.json @@ -1,7 +1,7 @@ { "name": "Primary profile", "status": "Valid", - "created": "2024-05-23 12:38:26", + "created": "2024-05-23", "version": "v1.3", "questions": [ { 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 ae9423087..aa6c73297 100644 --- a/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json +++ b/testing/unit/risk_profile/profiles/risk_profile_valid_limited.json @@ -1,7 +1,7 @@ { "name": "Primary profile", "status": "Valid", - "created": "2024-05-23 12:38:26", + "created": "2024-05-23", "version": "v1.3", "questions": [ { diff --git a/testing/unit/risk_profile/risk_profile_test.py b/testing/unit/risk_profile/risk_profile_test.py index fedd946d9..c4c80c1e4 100644 --- a/testing/unit/risk_profile/risk_profile_test.py +++ b/testing/unit/risk_profile/risk_profile_test.py @@ -53,7 +53,7 @@ def risk_profile_high_test(self): with open(output_file, 'w', encoding='utf-8') as file: file.write(risk_profile.to_json(pretty=True)) - self.assertEqual(risk_profile.status, 'Valid') + self.assertEqual(risk_profile.risk, 'High') def risk_profile_limited_test(self): # Read the risk profile json file @@ -70,7 +70,7 @@ def risk_profile_limited_test(self): with open(output_file, 'w', encoding='utf-8') as file: file.write(risk_profile.to_json(pretty=True)) - self.assertEqual(risk_profile.status, 'Valid') + self.assertEqual(risk_profile.risk, 'Limited') def risk_profile_rename_test(self): # Read the risk profile json file @@ -80,7 +80,7 @@ def risk_profile_rename_test(self): risk_profile_json = json.loads(file.read()) -# Create the RiskProfile object from the json file + # Create the RiskProfile object from the json file risk_profile = RiskProfile(risk_profile_json, self.profile_format) # Rename the profile diff --git a/testing/unit/run_tests.sh b/testing/unit/run_tests.sh index b74eebc02..975627fc3 100644 --- a/testing/unit/run_tests.sh +++ b/testing/unit/run_tests.sh @@ -1,64 +1,64 @@ -#!/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" - -# 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 - +#!/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" + +# 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 + popd >/dev/null 2>&1 \ No newline at end of file From 716da44fa2ffc52591b458322f70cdc030ac8e1b Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Mon, 24 Jun 2024 12:22:30 -0600 Subject: [PATCH 3/8] Remove categories from profile --- framework/python/src/common/risk_profile.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index e9d25dbf8..c2d2ce0f8 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -38,7 +38,6 @@ def __init__(self, profile_json=None, profile_format=None): self.version = profile_json['version'] self.questions = profile_json['questions'] self.status = None - self.categories = None self.risk = None self._validate(profile_json, profile_format) @@ -53,7 +52,6 @@ def load(self, profile_json, profile_format): self.version = profile_json['version'] self.questions = profile_json['questions'] self.status = None - self.categories = None self._validate(profile_json, profile_format) self._update_risk(profile_format) @@ -240,7 +238,5 @@ def to_json(self, pretty=False): 'risk': self.risk, 'questions': self.questions } - if self.categories is not None: - json_dict['categories'] = self.categories indent = 2 if pretty else None return json.dumps(json_dict, indent=indent) From 0d06f5465dc677ef2fe9c0b909200ca6766d0c7b Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Mon, 24 Jun 2024 20:17:40 +0000 Subject: [PATCH 4/8] Update expiration check to account for leap years --- framework/python/src/common/risk_profile.py | 20 +++++++++----------- framework/requirements.txt | 5 ++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index c2d2ce0f8..53937b08a 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -13,17 +13,11 @@ # limitations under the License. """Stores additional information about a device's risk""" from datetime import datetime +from dateutil.relativedelta import relativedelta from common import logger import json LOGGER = logger.get_logger('risk_profile') -SECONDS_IN_YEAR = 31536000 - -DATA_COLLECTION_CATEGORY = 'Data Collection' -DATA_TRANSMISSION_CATEGORY = 'Data Transmission' -REMOTE_OPERATION_CATEGORY = 'Remote Operation' -OPERATING_ENVIRONMENT_CATEGORY = 'Operating Environment' - class RiskProfile(): """Python representation of a risk profile""" @@ -224,10 +218,14 @@ def _valid(self, profile_json, profile_format): return all_questions_answered and all_questions_present def _expired(self): - # Check expiry - created_date = self.created.timestamp() - today = datetime.now().timestamp() - return created_date < (today - SECONDS_IN_YEAR) + # Calculate the date one year after the creation date + expiry_date = self.created + relativedelta(years=1) + + # Normalize the current date and time to midnight + today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + + # Check if the current date and time is past the expiry date + return today > expiry_date def to_json(self, pretty=False): json_dict = { diff --git a/framework/requirements.txt b/framework/requirements.txt index 3989c3b74..e47609925 100644 --- a/framework/requirements.txt +++ b/framework/requirements.txt @@ -26,4 +26,7 @@ markdown==3.5.2 # Requirements for the session cryptography==42.0.7 -pytz==2024.1 \ No newline at end of file +pytz==2024.1 + +# Requirements for the risk profile +python-dateutil=2.9.0 \ No newline at end of file From ac8b7fcd0fe52b9d3113480f8faaeb47e2433a89 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Tue, 25 Jun 2024 12:08:07 +0100 Subject: [PATCH 5/8] Update risk assessment questions --- local/.gitignore | 1 + resources/risk_assessment.json | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/local/.gitignore b/local/.gitignore index 06f79c1ca..84d72ff6e 100644 --- a/local/.gitignore +++ b/local/.gitignore @@ -1,3 +1,4 @@ system.json devices root_certs +risk_profiles diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json index 03037206c..75162401a 100644 --- a/resources/risk_assessment.json +++ b/resources/risk_assessment.json @@ -144,10 +144,6 @@ "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", "options": [ - { - "text": "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", - "risk": "High" - }, { "text": "PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership", "risk": "High" @@ -165,11 +161,7 @@ "risk": "High" }, { - "text": "The device encrypts data during transmission", - "risk": "High" - }, - { - "text": "The device network protocol is well-established and currently used by Google", + "text": "The device does not encrypt data during transmission", "risk": "High" }, { @@ -218,7 +210,7 @@ "risk": "High" }, { - "text": "Authentication is required for remote access", + "text": "Authentication is not required for remote access", "risk": "High" }, { From 1615d8cab34ef60cf14b76e54a917af0ae297f94 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Tue, 25 Jun 2024 12:09:37 +0100 Subject: [PATCH 6/8] Update dependency --- framework/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/requirements.txt b/framework/requirements.txt index e47609925..ee96c7e61 100644 --- a/framework/requirements.txt +++ b/framework/requirements.txt @@ -29,4 +29,4 @@ cryptography==42.0.7 pytz==2024.1 # Requirements for the risk profile -python-dateutil=2.9.0 \ No newline at end of file +python-dateutil==2.9.0 \ No newline at end of file From 921b2dfa65b9757196c65291f496e4ecde468a49 Mon Sep 17 00:00:00 2001 From: jhughesbiot Date: Tue, 25 Jun 2024 09:40:32 -0600 Subject: [PATCH 7/8] Add missing question --- resources/risk_assessment.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json index 75162401a..e14501e3f 100644 --- a/resources/risk_assessment.json +++ b/resources/risk_assessment.json @@ -164,6 +164,10 @@ "text": "The device does not encrypt data during transmission", "risk": "High" }, + { + "text": "The device network protocol is well-established and currently used by Google", + "risk": "High" + }, { "text": "None of the above", "risk": "Limited" From 028f86be3d62a5e35ad155dcc296fe4f75c49fa1 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Thu, 27 Jun 2024 10:16:45 +0100 Subject: [PATCH 8/8] Update format and add error handling --- framework/python/src/common/risk_profile.py | 8 +- local/risk_profiles/Primary profile.json | 2 +- resources/risk_assessment.json | 236 ++++++++++++++------ 3 files changed, 178 insertions(+), 68 deletions(-) diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 53937b08a..4eca18b88 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -88,6 +88,11 @@ def _update_risk(self, profile_format): format_q = self._get_format_question( question_text, profile_format) + if format_q is None: + # This occurs when a question found in a current profile + # has been removed from the format (format change) + continue + # We only want to check the select or select-multiple # questions for now if format_q['type'] in ['select', 'select-multiple']: @@ -118,7 +123,8 @@ def _update_risk(self, profile_format): option = self._get_option_from_index(format_options, index) if option is None: - LOGGER.error('Answer had an invalid index') + LOGGER.error('Answer had an invalid index for question: ' + + format_q['question']) continue if 'risk' in option and option['risk'] == 'High': diff --git a/local/risk_profiles/Primary profile.json b/local/risk_profiles/Primary profile.json index 5ddf7b85f..f95947111 100644 --- a/local/risk_profiles/Primary profile.json +++ b/local/risk_profiles/Primary profile.json @@ -1 +1 @@ -{"name": "Primary profile", "version": "1.3-alpha", "created": "2024-06-21", "status": "Valid", "risk": "High", "questions": [{"question": "What type of device is this?", "answer": "IoT Sensor"}, {"question": "How will this device be used at Google?", "answer": "Hey"}, {"question": "What is the email of the device owner(s)?", "answer": "boddey@google.com, cmeredith@google.com"}, {"question": "Is this device going to be managed by Google or a third party?", "answer": "Google", "risk": "Limited"}, {"question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", "answer": "Yes", "risk": "Limited"}, {"question": "Are any of the following statements true about your device?", "answer": [3], "risk": "Limited"}, {"question": "Which of the following statements are true about this device?", "answer": [6], "risk": "High"}, {"question": "Does the network protocol assure server-to-client identity verification?", "answer": "Yes", "risk": "Limited"}, {"question": "Click the statements that best describe the characteristics of this device.", "answer": [4], "risk": "High"}, {"question": "Are any of the following statements true about this device?", "answer": [0], "risk": "High"}]} \ No newline at end of file +{"name": "Primary profile", "version": "1.3-alpha", "created": "2024-06-27", "status": "Valid", "risk": "High", "questions": [{"question": "What type of device is this?", "answer": "IoT Gateway", "risk": "High"}, {"question": "How will this device be used at Google?", "answer": "Hey"}, {"question": "Is this device going to be managed by Google or a third party?", "answer": "Google", "risk": "Limited"}, {"question": "Will the third-party device administrator be able to grant access to authorized Google personnel upon request?", "answer": "Yes", "risk": "Limited"}, {"question": "Are any of the following statements true about your device?", "answer": [3], "risk": "Limited"}, {"question": "Which of the following statements are true about this device?", "answer": [5], "risk": "Limited"}, {"question": "Does the network protocol assure server-to-client identity verification?", "answer": "Yes", "risk": "Limited"}, {"question": "Click the statements that best describe the characteristics of this device.", "answer": [4], "risk": "High"}, {"question": "Are any of the following statements true about this device?", "answer": [0], "risk": "High"}]} \ No newline at end of file diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json index e14501e3f..784322421 100644 --- a/resources/risk_assessment.json +++ b/resources/risk_assessment.json @@ -3,56 +3,170 @@ "question": "What type of device is this?", "type": "select", "options": [ - "Analytics - Energy Modeling", - "Analytics - HVAC Optimization", - "Analytics - Load Disaggregation", - "Analytics - Occupancy", - "Analytics - Personal Profile", - "Analytics - Time Management", - "Analytics - Other", - "Building Automation Gateway", - "IoT Gateway", - "Controller - AHU", - "Controller - Boiler", - "Controller - Chiller", - "Controller - FCU", - "Controller - Pump", - "Controller - CRAC", - "Controller - VAV", - "Controller - VRF", - "Controller - Multiple", - "Controller - Other", - "Controller - Lighting", - "Controller - Blinds/Facades", - "Controller - Lifts/Elevators", - "Controller - UPS", - "Sensor - Air Quality", - "Sensor - Vibration", - "Sensor - Humidity", - "Sensor - Water", - "Sensor - Occupancy", - "Sensor - Volume", - "Sensor - Weight", - "Sensor - Weather", - "Sensor - Steam", - "Sensor - Air Flow", - "Sensor - Lighting", - "Sensor - Other", - "Sensor - Air Quality", - "Monitoring - Fire System", - "Monitoring - Emergency Lighting", - "Monitoring - Other", - "Monitoring - UPS", - "Meter - Water", - "Meter - Gas", - "Meter - Electricity", - "Meter - Other", - "Camera", - "Badge Reader", - "Other", - "Data - Storage", - "Data - Processing", - "Touch Panel" + { + "text": "Building Automation Gateway", + "risk": "High" + }, + { + "text": "IoT Gateway", + "risk": "High" + }, + { + "text": "Controller - AHU", + "risk": "Limited" + }, + { + "text": "Controller - Boiler", + "risk": "High" + }, + { + "text": "Controller - Chiller", + "risk": "High" + }, + { + "text": "Controller - FCU", + "risk": "Limited" + }, + { + "text": "Controller - Pump", + "risk": "Limited" + }, + { + "text": "Controller - CRAC", + "risk": "Limited" + }, + { + "text": "Controller - VAV", + "risk": "Limited" + }, + { + "text": "Controller - VRF", + "risk": "Limited" + }, + { + "text": "Controller - Multiple", + "risk": "High" + }, + { + "text": "Controller - Other", + "risk": "High" + }, + { + "text": "Controller - Lighting", + "risk": "Limited" + }, + { + "text": "Controller - Blinds/Facades", + "risk": "Limited" + }, + { + "text": "Controller - Lifts/Elevators", + "risk": "High" + }, + { + "text": "Controller - UPS", + "risk": "Limited" + }, + { + "text": "Sensor - Air Quality", + "risk": "Limited" + }, + { + "text": "Sensor - Vibration", + "risk": "Limited" + }, + { + "text": "Sensor - Humidity", + "risk": "Limited" + }, + { + "text": "Sensor - Water", + "risk": "Limited" + }, + { + "text": "Sensor - Occupancy", + "risk": "High" + }, + { + "text": "Sensor - Volume", + "risk": "Limited" + }, + { + "text": "Sensor - Weight", + "risk": "Limited" + }, + { + "text": "Sensor - Weather", + "risk": "Limited" + }, + { + "text": "Sensor - Steam", + "risk": "High" + }, + { + "text": "Sensor - Air Flow", + "risk": "Limited" + }, + { + "text": "Sensor - Lighting", + "risk": "Limited" + }, + { + "text": "Sensor - Other", + "risk": "High" + }, + { + "text": "Sensor - Air Quality", + "risk": "Limited" + }, + { + "text": "Monitoring - Fire System", + "risk": "Limited" + }, + { + "text": "Monitoring - Emergency Lighting", + "risk": "Limited" + }, + { + "text": "Monitoring - Other", + "risk": "High" + }, + { + "text": "Monitoring - UPS", + "risk": "Limited" + }, + { + "text": "Meter - Water", + "risk": "Limited" + }, + { + "text": "Meter - Gas", + "risk": "Limited" + }, + { + "text": "Meter - Electricity", + "risk": "Limited" + }, + { + "text": "Meter - Other", + "risk": "High" + }, + { + "text": "Other", + "risk": "High" + }, + { + "text": "Data - Storage", + "risk": "High" + }, + { + "text": "Data - Processing", + "risk": "High" + }, + { + "text": "Tablet", + "risk": "Limited" + } ], "validation": { "required": true @@ -67,25 +181,18 @@ "required": true } }, - { - "question": "What is the email of the device owner(s)?", - "description": "The device owner is the person or group responsible for the device", - "type": "email-multiple", - "validation": { - "required": true, - "max": "128" - } - }, { "question": "Is this device going to be managed by Google or a third party?", "description": "A manufacturer or supplier is considered third party in this case", "type": "select", "options": [ { - "text": "Google" + "text": "Google", + "risk": "Limited" }, { - "text": "Third Party" + "text": "Third Party", + "risk": "High" } ], "validation": { @@ -164,10 +271,6 @@ "text": "The device does not encrypt data during transmission", "risk": "High" }, - { - "text": "The device network protocol is well-established and currently used by Google", - "risk": "High" - }, { "text": "None of the above", "risk": "Limited" @@ -191,7 +294,8 @@ "risk": "High" }, { - "text": "I don't know" + "text": "I don't know", + "risk": "High" } ],