From 429b49a021fee89a7ccea68915dd4573a8f77089 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Mon, 20 May 2024 13:19:01 +0100 Subject: [PATCH 1/7] Add get profiles format endpoint --- framework/python/src/api/api.py | 22 +++- framework/python/src/common/session.py | 34 +++++- framework/python/src/core/testrun.py | 3 +- resources/devices/template/device_config.json | 4 +- resources/risk_assessment.json | 115 ++++++++++++++++++ 5 files changed, 171 insertions(+), 7 deletions(-) create mode 100644 resources/risk_assessment.json diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index 9f0da15c2..a5a72f31f 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -95,10 +95,14 @@ def __init__(self, test_run): self._router.add_api_route("/device/edit", self.edit_device, methods=["POST"]) - + self._router.add_api_route("/system/modules", self.get_test_modules) + # Profiles + self._router.add_api_route("/profiles/format", + self._get_profiles_format) + # Allow all origins to access the API origins = ["*"] @@ -130,6 +134,9 @@ def _start(self): def stop(self): LOGGER.info("Stopping API") + def get_session(self): + return self._session + async def get_sys_interfaces(self): addrs = psutil.net_if_addrs() ifaces = {} @@ -527,7 +534,6 @@ async def edit_device(self, request: Request, response: Response): response.status_code = status.HTTP_400_BAD_REQUEST return self._generate_msg(False, "Invalid JSON received") - async def get_report(self, response: Response, device_name, timestamp): @@ -596,3 +602,15 @@ def _validate_device_json(self, json_obj): def _get_test_run(self): return self._test_run + + # Profiles + def _get_profiles_format(self, response: Response): + + # Check if Testrun was able to load the format originally + if self.get_session().get_profiles_format() is None: + response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + return self._generate_msg( + False, + "Testrun could not load the risk assessment format") + + return self.get_session().get_profiles_format() diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index df7a1c3b2..bd9dc658a 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -29,12 +29,17 @@ API_PORT_KEY = 'api_port' MAX_DEVICE_REPORTS_KEY = 'max_device_reports' +PROFILE_FORMAT_PATH = 'resources/risk_assessment.json' +PROFILES_DIR = 'local/profiles' + LOGGER = logger.get_logger('session') class TestrunSession(): """Represents the current session of Test Run.""" - def __init__(self, config_file, version): + def __init__(self, root_dir, config_file, version): + self._root_dir = root_dir + self._status = 'Idle' self._device = None self._started = None @@ -46,11 +51,18 @@ def __init__(self, config_file, version): self._total_tests = 0 self._report_url = None - self._load_version(default_version=version) + # Profiles + self._profiles = [] + self._profile_format_json = None + # System configuration self._config_file = config_file self._config = self._get_default_config() + + # Loading methods + self._load_version(default_version=version) self._load_config() + self._load_profiles() tz = util.run_command('cat /etc/timezone') # TODO: Check if timezone is fetched successfully @@ -292,6 +304,24 @@ def get_report_url(self): def set_report_url(self, url): self._report_url = url + def _load_profiles(self): + + # Load format of questionnaire + LOGGER.debug('Loading risk assessment format') + + try: + 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) + except (IOError, ValueError) as e: + LOGGER.error( + 'An error occurred whilst loading the risk assessment format') + LOGGER.debug(e) + + def get_profiles_format(self): + return self._profile_format_json + def reset(self): self.set_status('Idle') self.set_target_device(None) diff --git a/framework/python/src/core/testrun.py b/framework/python/src/core/testrun.py index 22607a520..e7c77c517 100644 --- a/framework/python/src/core/testrun.py +++ b/framework/python/src/core/testrun.py @@ -89,7 +89,8 @@ def __init__(self, self._register_exits() # Create session - self._session = TestrunSession(config_file=self._config_file, + self._session = TestrunSession(root_dir=root_dir, + config_file=self._config_file, version=self.get_version()) # Register runtime parameters diff --git a/resources/devices/template/device_config.json b/resources/devices/template/device_config.json index dc6d74924..85ff6aafe 100644 --- a/resources/devices/template/device_config.json +++ b/resources/devices/template/device_config.json @@ -2,7 +2,7 @@ "manufacturer": "Manufacturer X", "model": "Device X", "mac_addr": "aa:bb:cc:dd:ee:ff", - "max_device_tests":5, + "max_device_tests": 0, "test_modules": { "dns": { "enabled": true @@ -16,7 +16,7 @@ "baseline": { "enabled": false }, - "nmap": { + "services": { "enabled": true }, "tls": { diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json new file mode 100644 index 000000000..25f0f6888 --- /dev/null +++ b/resources/risk_assessment.json @@ -0,0 +1,115 @@ +[ + { + "question": "What type of device is this?", + "type": "select", + "options": [ + "IoT Sensor", + "IoT Controller", + "Smart Device", + "Something else" + ], + "validation": { + "required": true + } + }, + { + "question": "How will this device be used at Google?", + "type": "text", + "validation": { + "max": "128", + "required": true + } + }, + { + "question": "What is the email of the device owner(s)?", + "type": "text", + "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" + ], + "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", + "validation": { + "required": true + } + }, + { + "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", + "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)?" + ] + }, + { + "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", + "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" + ] + }, + { + "question": "Does the network protocol assure server-to-client identity verification?", + "type": "select", + "options": [ + "Yes", + "No", + "I don't know" + ], + "validation": { + "required": true + } + }, + { + "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", + "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" + ] + }, + { + "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", + "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." + ] + } +] \ No newline at end of file From 08e035383e6da0587a782a138f43581fed6394ed Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Thu, 23 May 2024 20:01:50 +0100 Subject: [PATCH 2/7] Update risk assessment format --- resources/risk_assessment.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/risk_assessment.json b/resources/risk_assessment.json index 25f0f6888..e341dd904 100644 --- a/resources/risk_assessment.json +++ b/resources/risk_assessment.json @@ -14,7 +14,7 @@ }, { "question": "How will this device be used at Google?", - "type": "text", + "type": "text-long", "validation": { "max": "128", "required": true @@ -22,7 +22,7 @@ }, { "question": "What is the email of the device owner(s)?", - "type": "text", + "type": "email-multiple", "validation": { "required": true, "max": "128" From ef5f87bd63193dde0c07450b735fb9475d6c2918 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Fri, 24 May 2024 12:02:27 +0100 Subject: [PATCH 3/7] Re-add modules endpoint --- framework/python/src/api/api.py | 8 ++++++++ framework/python/src/common/session.py | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index 38a2627cb..304d9f7bb 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -617,6 +617,7 @@ def _get_profiles_format(self, response: Response): return self.get_session().get_profiles_format() + # Certificates def get_certs(self): LOGGER.debug("Received certs list request") @@ -707,3 +708,10 @@ async def delete_cert(self, request: Request, response: Response): except Exception as e: LOGGER.error("An error occurred whilst deleting a certificate") LOGGER.debug(e) + + def get_test_modules(self): + modules = [] + for module in self._test_run.get_test_orc().get_test_modules(): + if module.enabled and module.enable_container: + modules.append(module.display_name) + return modules diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index f22dd593c..d8ca44def 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -86,7 +86,7 @@ def __init__(self, root_dir): self._config = self._get_default_config() # Loading methods - self._load_version(default_version=version) + self._load_version() self._load_config() self._load_profiles() @@ -182,7 +182,7 @@ def _load_config(self): LOGGER.debug(self._config) - def _load_version(self, default_version): + def _load_version(self): version_cmd = util.run_command( 'dpkg-query --showformat=\'${Version}\' --show testrun') # index 1 of response is the stderr byte stream so if @@ -192,7 +192,7 @@ def _load_version(self, default_version): version = version_cmd[0] self._version = version else: - self._version = default_version + self._version = '?' LOGGER.info(f'Running Testrun version {self._version}') def get_version(self): From 7a92b962799d6fab16ac2e842e831b37be47d966 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Thu, 30 May 2024 21:20:00 +0100 Subject: [PATCH 4/7] Work on profiles --- cmd/install | 1 + cmd/package | 3 +- docs/dev/mockoon.json | 1742 +++++++++++++++++++ docs/dev/postman.json | 1063 +++++++++++ framework/python/src/api/api.py | 9 +- framework/python/src/common/risk_profile.py | 35 + framework/python/src/common/session.py | 17 +- 7 files changed, 2863 insertions(+), 7 deletions(-) create mode 100644 docs/dev/mockoon.json create mode 100644 docs/dev/postman.json create mode 100644 framework/python/src/common/risk_profile.py diff --git a/cmd/install b/cmd/install index 9d4f4de52..32c129afd 100755 --- a/cmd/install +++ b/cmd/install @@ -63,5 +63,6 @@ sudo cmd/build # Create local folders mkdir -p local/devices mkdir -p local/root_certs +mkdir -p local/risk_profiles echo Finished installing Testrun diff --git a/cmd/package b/cmd/package index cdc7cc8cf..48f2f3985 100755 --- a/cmd/package +++ b/cmd/package @@ -46,8 +46,9 @@ cp -r resources $MAKE_SRC_DIR/usr/local/testrun/ mkdir -p $MAKE_SRC_DIR/usr/local/testrun/local cp local/system.json.example $MAKE_SRC_DIR/usr/local/testrun/local/system.json.example -# Create device repository +# Create device repository and risk profiles database mkdir -p $MAKE_SRC_DIR/usr/local/testrun/local/devices +mkdir -p $MAKE_SRC_DIR/usr/local/testrun/local/risk_profiles # Copy root_certs folder mkdir -p local/root_certs diff --git a/docs/dev/mockoon.json b/docs/dev/mockoon.json new file mode 100644 index 000000000..a73eb5beb --- /dev/null +++ b/docs/dev/mockoon.json @@ -0,0 +1,1742 @@ +{ + "uuid": "1b2ab06a-28fe-4bb6-9714-4347bff629bd", + "lastMigration": 32, + "name": "Testrun", + "endpointPrefix": "", + "latency": 0, + "port": 8000, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "609cbb2f-b064-4d94-9d47-705f902af57d", + "type": "http", + "documentation": "Fetch a list of available interfaces", + "method": "get", + "endpoint": "system/interfaces", + "responses": [ + { + "uuid": "7a736bd6-7087-4227-91d7-b9efb5d46bc1", + "body": "{\n \"enx00e04c020fa8\": \"00:e0:4c:02:0f:a8\",\n \"enx207bd26205e9\": \"20:7b:d2:62:05:e9\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Returns a list of available interfaces", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "32ddc969-bf37-4bd6-a926-329ffa351583", + "type": "http", + "documentation": "Set the system configuration", + "method": "post", + "endpoint": "system/config", + "responses": [ + { + "uuid": "2513112c-7b46-4093-9954-585f9d2b5807", + "body": "{\n \"success\": \"Successfully updated the configuration\"\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "internet_intf", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "device_intf", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "46a870dd-a506-4bc3-a8ea-fb823d9ad700", + "body": "{\n \"error\": \"Invalid configuration received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid config", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "internet_intf", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "device_intf", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "24a155d7-8d30-458e-bc03-2240ecc95a30", + "type": "http", + "documentation": "Fetch a list of available devices", + "method": "get", + "endpoint": "devices", + "responses": [ + { + "uuid": "eef8575a-b420-4b56-b879-303b3c58d7d9", + "body": "[\n {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB 140\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": false\n }\n }\n },\n {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff\",\n \"manufacturer\": \"Manufacturer X\",\n \"model\": \"Device X\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"services\": {\n \"enabled\": true\n }\n }\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "98e836d3-8af1-4321-9cdc-7feba8b40d8e", + "type": "http", + "documentation": "Create a device", + "method": "post", + "endpoint": "device", + "responses": [ + { + "uuid": "bee3a25e-ffd7-4a38-a894-26217efb106f", + "body": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-SRC\",\n \"mac_addr\": \"00:1e:42:35:73:c2\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"services\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "latency": 0, + "statusCode": 201, + "label": "Created a new device", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "mac_addr", + "value": "00:1e:42:35:73:c2", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "77c157b9-0175-4c84-b660-cf96e712e450", + "body": "{\n \"error\": \"A device with that MAC address already exists\"\n}", + "latency": 0, + "statusCode": 409, + "label": "Device already exists", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "8f021e4c-4d30-4bdd-8e03-04bb136a9970", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid request", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "7d454ec4-61a2-4ede-a5bc-d7634d0d007b", + "body": "{\n \"error\": \"Invalid JSON received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid JSON", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "dcead7f8-71da-43ef-974b-ceddf8f262cc", + "type": "http", + "documentation": "Start a new Testrun", + "method": "post", + "endpoint": "system/start", + "responses": [ + { + "uuid": "1fed2343-e85b-4026-b96c-e024aa4535f5", + "body": "{\n \"status\": \"Waiting for Device\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-SRC\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-07-18T11:14:42.917670\",\n \"finished\": null, \n \"report\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun started successfully", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "", + "value": "", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "5ec818f2-562f-4bfa-bf1e-e8055b927bac", + "body": "{\n \"status\": \"Monitoring\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-SRC\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-07-18T11:14:42.917670\",\n \"finished\": null, \n \"report\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun monitoring device", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "", + "value": "", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "ee6e5e58-4fbb-46b3-8c78-2309ec01dfc0", + "body": "{\n \"error\": \"A testrun is already in progress\"\n}", + "latency": 0, + "statusCode": 409, + "label": "Testrun is already in progress", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "b34bc849-66c6-4409-84f0-5418edba7de4", + "body": "{\n \"error\": \"No device specified\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Missing device information", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "mac_addr", + "value": "", + "invert": false, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "f5b7e6ed-1c33-4c63-bbc3-63d251b29fbf", + "body": "{\n \"error\": \"Device not found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Device not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "874df7e9-1f62-4fdf-9ec8-100df7d0291f", + "body": "{\n \"error\": \"Configured interfaces are not ready for use. Ensure both interfaces are connected.\"\n}", + "latency": 0, + "statusCode": 500, + "label": "System not configured correctly", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "9a9482ee-09d6-4ab7-9fb0-bbfed791bd9d", + "type": "http", + "documentation": "Get the current status of Testrun", + "method": "get", + "endpoint": "system/status", + "responses": [ + { + "uuid": "a6d2fa8a-2c0e-4915-b119-703776d5982b", + "body": "{\n \"status\": \"In Progress\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": null,\n \"report\": null,\n \"tests\": {\n \"total\": 26,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",,\n \"required_result\": \"Required\"\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Feature Not Present\",\n \"required_result\": \"Roadmap\"\n \"recommendations\": [\n \"An example of a step to resolve\",\n \"Disable any running NTP server\"\n ]\n } \n ]\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun In Progress", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "9a9adf23-be2b-4bb3-bb89-bd3d5ae731a8", + "body": "{\n \"status\": \"Monitoring\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": null,\n \"report\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Monitoring", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "6b2055fc-3e9a-43cd-8157-92168b1a0df3", + "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"report\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Idle", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "6e8b9ee4-9905-480d-974a-ebe0a3778a31", + "body": "{\n \"status\": \"Cancelled\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": \"2023-06-22T09:24:00.123Z\",\n \"report\": null,\n \"tests\": {\n \"total\": 22,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Compliant\",\n \"required_result\": \"Required\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Feature Not Present\",\n \"required_result\": \"Roadmap\"\n }\n ]\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Cancelled", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "551c1bca-85e5-4d19-8800-902ddb76c88f", + "body": "{\n \"status\": \"Waiting for Device\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": null,\n \"report\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Waiting for Device", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "1a1346d2-d309-43c3-8993-2416c704ef06", + "body": "{\n \"status\": \"Non-Compliant\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": \"2023-06-22T09:26:00.123Z\",\n \"report\": \"https://api.testrun.io/report.pdf\",\n \"tests\": {\n \"total\": 26,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Non-Compliant\",\n \"required_result\": \"Compliant\"\n \"recommendations\": [\n \"An example of a step to resolve\",\n \"Disable any running NTP server\"\n ]\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Compliant\",\n \"required_result\": \"Roadmap\"\n },\n {\n \"name\": \"security.services.ftp\",\n \"description\": \"FTP server should not be available\",\n \"result\": \"Compliant\",\n \"required_result\": \"Required\"\n }\n ]\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Test Run Non-Compliant", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "afe4b3e4-8401-46ea-8850-2dcb919fb7d3", + "body": "{\n \"status\": \"Compliant\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": \"2023-06-22T09:26:00.123Z\",\n \"report\": \"https://api.testrun.io/report.pdf\",\n \"tests\": {\n \"total\": 3,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Compliant\",\n \"required_result\": \"Required\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Feature Not Present\",\n \"required_result\": \"Roadmap\"\n },\n {\n \"name\": \"security.services.ftp\",\n \"description\": \"FTP server should not be available\",\n \"result\": \"Compliant\",\n \"required_result\": \"Required\"\n }\n ]\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Compliant", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "16da9b0b-68ac-41bb-b698-8e196ebabd4c", + "body": "{\n \"status\": \"Error\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": \"2023-06-22T09:26:00.123Z\",\n \"report\": \"https://api.testrun.io/report.pdf\",\n \"tests\": {\n \"total\": 3,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Error\",\n \"required_result\": \"Required\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Feature Not Present\",\n \"required_result\": \"Roadmap\"\n },\n {\n \"name\": \"security.services.ftp\",\n \"description\": \"FTP server should not be available\",\n \"result\": \"Compliant\",\n \"required_result\": \"Required\"\n }\n ]\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun Error", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "217a5e1b-fda6-4930-9f05-1372bc11590c", + "type": "http", + "documentation": "Stop the current Testrun", + "method": "post", + "endpoint": "system/stop", + "responses": [ + { + "uuid": "84004877-fe78-4817-a542-be1aa23088ec", + "body": "{\n \"success\": \"Testrun cancelled successfully\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Testrun cancelled successfully", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "04561650-fffe-4880-8c69-b94e8b96ba38", + "body": "{\n \"error\": \"Testrun is not currently running\"\n}", + "latency": 0, + "statusCode": 404, + "label": "No Testrun currently in-progress", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "a8ba2fcb-8e05-42f4-9d5f-cea63aa8ead7", + "type": "http", + "documentation": "Fetch a list of previous Testruns", + "method": "get", + "endpoint": "reports", + "responses": [ + { + "uuid": "9536ff4c-f97f-4880-b9fc-f477686ad6b8", + "body": "[\n {\n \"mac_addr\": \"00:1e:42:35:73:c6\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB140\",\n \"firmware\": \"1.2.3\",\n \"test_modules\": {\n \"connection\": {\n \"enabled\": false\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": true\n },\n \"tls\": {\n \"enabled\": true\n },\n \"protocol\": {\n \"enabled\": true\n }\n }\n },\n \"status\": \"Non-Compliant\",\n \"started\": \"2024-05-03 12:09:59\",\n \"finished\": \"2024-05-03 12:15:51\",\n \"tests\": {\n \"total\": 20,\n \"results\": [\n {\n \"name\": \"protocol.valid_bacnet\",\n \"description\": \"BACnet discovery could not resolve any devices\",\n \"expected_behavior\": \"BACnet traffic can be seen on the network and packets are valid and not malformed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Skipped\"\n },\n {\n \"name\": \"protocol.bacnet.version\",\n \"description\": \"No BACnet devices discovered.\",\n \"expected_behavior\": \"The BACnet client implements an up to date version of BACnet\",\n \"required_result\": \"Recommended\",\n \"result\": \"Skipped\"\n },\n {\n \"name\": \"protocol.valid_modbus\",\n \"description\": \"Failed to establish Modbus connection to device\",\n \"expected_behavior\": \"Any Modbus functionality works as expected and valid Modbus traffic can be observed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Non-Compliant\"\n },\n {\n \"name\": \"ntp.network.ntp_support\",\n \"description\": \"Device sent NTPv3 packets. NTPv3 is not allowed.\",\n \"expected_behavior\": \"The device sends an NTPv4 request to the configured NTP server.\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\"\n },\n {\n \"name\": \"ntp.network.ntp_dhcp\",\n \"description\": \"Device sent NTP request to non-DHCP provided server\",\n \"expected_behavior\": \"Device can accept NTP server address, provided by the DHCP server (DHCP OFFER PACKET)\",\n \"required_result\": \"Roadmap\",\n \"result\": \"Non-Compliant\"\n },\n {\n \"name\": \"security.services.ftp\",\n \"description\": \"No FTP server found\",\n \"expected_behavior\": \"There is no FTP service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.ssh.version\",\n \"description\": \"SSH server found running protocol 2.0\",\n \"expected_behavior\": \"SSH server is not running or server is SSHv2\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.telnet\",\n \"description\": \"No telnet server found\",\n \"expected_behavior\": \"There is no Telnet service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.smtp\",\n \"description\": \"No SMTP server found\",\n \"expected_behavior\": \"There is no SMTP service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.http\",\n \"description\": \"Found HTTP server running on port 80/tcp\",\n \"expected_behavior\": \"Device is unreachable on port 80 (or any other port) and only responds to HTTPS requests on port 443 (or any other port if HTTP is used at all)\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\"\n },\n {\n \"name\": \"security.services.pop\",\n \"description\": \"No POP server found\",\n \"expected_behavior\": \"There is no POP service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.imap\",\n \"description\": \"No IMAP server found\",\n \"expected_behavior\": \"There is no IMAP service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.snmpv3\",\n \"description\": \"No SNMP server found\",\n \"expected_behavior\": \"Device is unreachable on port 161 (or any other port) and device is unreachable on port 162 (or any other port) unless SNMP is essential in which case it is SNMPv3 is used.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.vnc\",\n \"description\": \"No VNC server found\",\n \"expected_behavior\": \"Device cannot be accessed / connected to via VNC on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.services.tftp\",\n \"description\": \"No TFTP server found\",\n \"expected_behavior\": \"There is no TFTP service running on any port\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"ntp.network.ntp_server\",\n \"description\": \"No NTP server found\",\n \"expected_behavior\": \"The device does not respond to NTP requests when it's IP is set as the NTP server on another device\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"DNS traffic detected from device\",\n \"expected_behavior\": \"The device sends DNS requests.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"DNS traffic detected only to DHCP provided server\",\n \"expected_behavior\": \"The device sends DNS requests to the DNS server provided by the DHCP server\",\n \"required_result\": \"Roadmap\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"security.tls.v1_2_server\",\n \"description\": \"TLS 1.2 not validated: Certificate has expired\\nEC key length passed: 256 >= 224\\nDevice certificate has not been signed\\nTLS 1.3 not validated: Certificate has expired\\nEC key length passed: 256 >= 224\\nDevice certificate has not been signed\",\n \"expected_behavior\": \"TLS 1.2 certificate is issued to the web browser client when accessed\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\"\n },\n {\n \"name\": \"security.tls.v1_2_client\",\n \"description\": \"No outbound connections were found.\",\n \"expected_behavior\": \"The packet indicates a TLS connection with at least TLS 1.2 and support for ECDH and ECDSA ciphers\",\n \"required_result\": \"Required\",\n \"result\": \"Skipped\"\n }\n ]\n },\n \"report\": \"http://localhost:8000/report/123 123/2024-05-03T12:09:59\"\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "a218761a-6ac6-466b-932e-9d58b6ec3a2e", + "type": "http", + "documentation": "Delete a device", + "method": "delete", + "endpoint": "device", + "responses": [ + { + "uuid": "5afbef24-a245-4ca2-b41d-6d867120f179", + "body": "{\n \"success\": \"Successfully deleted the device\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Deleted successfully", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "mac_addr", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "7618b710-3528-467f-8271-b6ee12f69a88", + "body": "{\n \"error\": \"Device not found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Device not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "491a490a-f9e3-457e-887f-f508d088c6c8", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Device MAC address not defined", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "mac_addr", + "value": "", + "invert": false, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "85843fbf-2b63-41ab-9836-51bdc0a31b8e", + "body": "{\n \"error\": \"Cannot delete this device whilst it is being tested\"\n}", + "latency": 0, + "statusCode": 403, + "label": "Testrun in progress", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "fd6fc843-4a77-4532-ba38-f81cdf345dc2", + "type": "http", + "documentation": "Get the current system configuration", + "method": "get", + "endpoint": "system/config", + "responses": [ + { + "uuid": "b78733cb-15e5-48ba-8ac1-34eb04dbaa28", + "body": "{\n \"network\": {\n \"device_intf\": \"enx207bd2620617\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"INFO\",\n \"startup_timeout\": 60,\n \"monitor_period\": 60\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "internet_intf", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "device_intf", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "12928dee-d2c8-4730-a1be-26ad796811dd", + "type": "http", + "documentation": "Obtain the current Testrun version", + "method": "get", + "endpoint": "system/version", + "responses": [ + { + "uuid": "f3afbdaf-e78c-4248-822a-853ea70ff621", + "body": "{\n \"installed_version\": \"1.0\",\n \"update_available\": true,\n \"latest_version\": \"1.1\",\n \"latest_version_url\": \"https://github.com/google/testrun/releases/tag/v1.1\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Update available", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "e37c0fbc-13c6-4c91-bbee-8eea148dd8d5", + "body": "{\n \"installed_version\": \"1.1\",\n \"installed_release_date\": \"20 Oct 2023\",\n \"update_available\": false,\n \"latest_version\": \"1.1\",\n \"latest_version_url\": \"https://github.com/google/testrun/releases/tag/v1.1\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Latest version installed", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "ac0ea995-ea90-4c6a-8767-3104e149de68", + "type": "http", + "documentation": "Delete a Testrun report", + "method": "delete", + "endpoint": "report", + "responses": [ + { + "uuid": "35b1585b-13d0-4702-9733-9e2f7fc08ab9", + "body": "{\n \"success\": \"Successfully deleted that report\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Deleted report successfully", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "ac611b0e-8171-4981-a7e8-93af0a065f8a", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid request received", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "7589fcf9-43c8-4ab8-8abf-a64ea115b038", + "body": "{\n \"error\": \"That Testrun report could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Report not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "220e4ba9-6463-4dc3-b714-f77643706b7d", + "body": "{\n \"error\": \"An error occured whilst deleting the report\"\n}", + "latency": 0, + "statusCode": 500, + "label": "Error occured", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "f1d72c42-6f48-488d-a394-4d31b9a1da5e", + "type": "http", + "documentation": "Get a Testrun PDF report", + "method": "get", + "endpoint": "report/{device_name}/{timestamp}", + "responses": [ + { + "uuid": "09092f2d-907b-452c-b47b-b2f3b9b47189", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "Report found", + "headers": [], + "bodyType": "FILE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "ee16a180-9d32-4784-9574-701cf56ff370", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid request received", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "e0464663-9ef1-49d4-941c-926aa06b0907", + "body": "{\n \"error\": \"That Testrun report could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Report not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "a7fbb2c8-81dc-4a40-80d6-482119314086", + "body": "{\n \"error\": \"An error occured whilst getting the report\"\n}", + "latency": 0, + "statusCode": 500, + "label": "Error occured", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "239b1309-52da-4dc8-b914-35c1be6d2ff9", + "type": "http", + "documentation": "Save a device configuration", + "method": "post", + "endpoint": "device/edit", + "responses": [ + { + "uuid": "71e21fad-c93c-4efa-9929-c1496c44ecba", + "body": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "latency": 0, + "statusCode": 200, + "label": "Updated an existing device", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "mac_addr", + "value": "00:1e:42:35:73:c4", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "4ec87c14-fff3-4273-ae07-70ea45670174", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Incorrect JSON format used", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "329afa70-8f92-4b15-9280-be1336e5e32a", + "body": "{\n \"error\": \"A device with that MAC address could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Device not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "1456f02a-ec93-47d5-8dfe-7467e325ba6e", + "body": "{\n \"error\": \"A device with that MAC address already exists\"\n}", + "latency": 0, + "statusCode": 409, + "label": "Device with that MAC address already exists", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "34d39075-6c44-41c9-9221-a9ed7ae27d3e", + "type": "http", + "documentation": "Get a Testrun results archive", + "method": "get", + "endpoint": "export/{device_name}/{timestamp}", + "responses": [ + { + "uuid": "73d2266f-2bdd-4128-ba82-bb62a454c335", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "Results found", + "headers": [], + "bodyType": "FILE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "63865c53-a848-47cf-804d-a6ca606249f5", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid request received", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "52b96932-a1c4-4e7d-b29e-af08922bbcea", + "body": "{\n \"error\": \"Test results could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Test attempt not found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "3bcb2d6d-3290-43bb-8392-7bfffda4feae", + "body": "{\n \"error\": \"An error occured whilst getting the test attempt\"\n}", + "latency": 0, + "statusCode": 500, + "label": "Error occured", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "0aed9026-18f5-4055-9670-83007ea18aaa", + "type": "http", + "documentation": "Shutdown Testrun", + "method": "post", + "endpoint": "system/shutdown", + "responses": [ + { + "uuid": "3fc98bfb-f662-4ef4-8fa1-da522ea18993", + "body": "null", + "latency": 0, + "statusCode": 200, + "label": "Shutting down", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "6ac393e4-9fbd-466a-804c-cdce26b0bc01", + "body": "{\n \"error\": \"Unable to shutdown. A test is currently in progress.\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Test in progress", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "6d6b9fa9-c961-4007-89f3-9ea9cccc05e7", + "type": "http", + "documentation": "List root CA certificates", + "method": "get", + "endpoint": "system/config/certs", + "responses": [ + { + "uuid": "581b126d-fadd-4461-bece-97b5b1fd97fd", + "body": "[\n {\n \"name\": \"iot.bms.google.com\",\n \"organisation\": \"Google, Inc.\",\n \"expires\": \"2024-09-01T09:00:12Z\",\n \"status\": \"Valid\"\n },\n {\n \"name\": \"sensor.bms.google.com\",\n \"organisation\": \"Google, Inc.\",\n \"expires\": \"2022-09-01T09:00:12Z\",\n \"status\": \"Expired\"\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "Listing 2x certificates", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "b9fa58e6-95c8-45ea-b6fb-a16287a6e65b", + "body": "[]", + "latency": 0, + "statusCode": 200, + "label": "No certificates", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "fb33213d-4448-431c-8733-1ce292644af6", + "type": "http", + "documentation": "", + "method": "delete", + "endpoint": "system/config/certs", + "responses": [ + { + "uuid": "63d0656a-2dc4-4a8e-bf53-8de209485c7f", + "body": "{\n \"success\": \"Successfully deleted that certificate\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Successfully deleted", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "906f342a-0981-43a7-a0a2-7fd862cc0ac9", + "body": "{\n \"error\": \"A certificate with that name could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "A certificate with that name could not be found", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "0e927c03-7a2f-41cb-8404-bd94e9213fd3", + "type": "http", + "documentation": "Upload root CA certificate", + "method": "post", + "endpoint": "system/config/certs", + "responses": [ + { + "uuid": "37e4a9bf-7b8b-42df-a62e-adb27faf7c1b", + "body": "{\n \"success\": \"Successfully uploaded that certificate to the store\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Successfully uploaded", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "5d5640b8-2b53-4101-8e31-e056cd3771dc", + "body": "{\n \"error\": \"Failed to upload certificate. Is it in the correct format?\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Incorrect format", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "2a46c346-843c-4c85-8d4c-a55bc6387b34", + "body": "{\n \"error\": \"That certificate has already been uploaded\"\n}", + "latency": 0, + "statusCode": 409, + "label": "Duplicate certificate", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "64b9226c-ae14-4559-883f-470212f63c57", + "type": "http", + "documentation": "List the test modules available", + "method": "get", + "endpoint": "system/modules", + "responses": [ + { + "uuid": "88e55282-3b57-476c-9716-b28c5ad795f0", + "body": "[\n \"Connection\",\n \"Services\",\n \"NTP\",\n \"DNS\"\n \"Protocol\",\n \"TLS\"\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "90a6beb3-a4c0-491f-9beb-b36c29e0dee5", + "type": "http", + "documentation": "Fetch a list of the profiles saved by the user", + "method": "get", + "endpoint": "profiles", + "responses": [ + { + "uuid": "26526b9e-8288-473d-826c-275511c789ec", + "body": "[\n {\n \"name\": \"Primary profile\",\n \"status\": \"Valid\",\n \"created\": \"2024-05-23 12:38:26\",\n \"version\": \"v1.3\",\n \"questions\": [\n [\n {\n \"question\": \"What type of device is this?\",\n \"type\": \"select\",\n \"options\": [\n \"IoT Sensor\",\n \"IoT Controller\",\n \"Smart Device\",\n \"Something else\"\n ],\n \"answer\": \"IoT Sensor\",\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"How will this device be used at Google?\",\n \"type\": \"text-long\",\n \"answer\": \"Installed in a building\",\n \"validation\": {\n \"max\": \"128\",\n \"required\": true\n }\n },\n {\n \"question\": \"What is the email of the device owner(s)?\",\n \"type\": \"email-multiple\",\n \"answer\": \"boddey@google.com, cmeredith@google.com\",\n \"validation\": {\n \"required\": true,\n \"max\": \"128\"\n }\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"type\": \"select\",\n \"options\": [\n \"Google\",\n \"Third Party\"\n ],\n \"answer\": \"Google\",\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"type\": \"select\",\n \"options\": [\n \"Yes\",\n \"No\",\n \"N/A\"\n ],\n \"default\": \"N/A\",\n \"answer\": \"Yes\",\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Are any of the following statements true about your device?\",\n \"description\": \"This tells us about the data your device will collect\",\n \"type\": \"select-multiple\",\n \"answer\": [\n 0,\n 2\n ],\n \"options\": [\n \"The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)\",\n \"The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets\",\n \"The device stream confidential business data in real-time (seconds)?\"\n ]\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"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.\",\n \"type\": \"select-multiple\",\n \"answer\": [\n 0,\n 1,\n 5\n ],\n \"options\": [\n \"PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership\",\n \"Data transmission occurs across less-trusted networks (e.g. the internet).\",\n \"A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)\",\n \"A confidentiality breach during transmission would have a substantial negative impact\",\n \"The device encrypts data during transmission\",\n \"The device network protocol is well-established and currently used by Google\"\n ]\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"type\": \"select\",\n \"answer\": \"Yes\",\n \"options\": [\n \"Yes\",\n \"No\",\n \"I don't know\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"description\": \"This tells us about how this device is managed remotely.\",\n \"type\": \"select-multiple\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"options\": [\n \"PII/PHI, or confidential business data is accessible from the device without authentication\",\n \"Unrecoverable actions (e.g. disk wipe) can be performed remotely\",\n \"Authentication is required for remote access\",\n \"The management interface is accessible from the public internet\",\n \"Static credentials are used for administration\"\n ]\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"description\": \"This informs us about what other systems and processes this device is a part of.\",\n \"type\": \"select-multiple\",\n \"answer\": [\n 2,\n 3\n ],\n \"options\": [\n \"The device monitors an environment for active risks to human life.\",\n \"The device is used to convey people, or critical property.\",\n \"The device controls robotics in human-accessible spaces.\",\n \"The device controls physical access systems.\",\n \"The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)\",\n \"The device's failure would cause faults in other high-criticality processes.\"\n ]\n }\n ]\n ]\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "One profile", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "6234def9-6b47-4ed8-a416-d160b25a99c8", + "body": "[]", + "latency": 0, + "statusCode": 200, + "label": "No profiles", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "86267532-5d37-4c12-9dfd-81d16c6164f1", + "type": "http", + "documentation": "Delete a profile", + "method": "delete", + "endpoint": "profiles", + "responses": [ + { + "uuid": "0c66bb41-e28b-40fe-866d-1a0c140489ec", + "body": "{\n \"success\": \"Successfully deleted the risk profile\"\n}", + "latency": 0, + "statusCode": 200, + "label": "Deleted", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "b9c4540e-d5cf-4ee3-a450-68c16552355a", + "body": "{\n \"error\": \"A profile with that name could not be found\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Profile does not exist", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "45635915-a144-46a9-b59a-f4b9d2fc8ebe", + "type": "http", + "documentation": "Create or update a risk profile", + "method": "post", + "endpoint": "profiles", + "responses": [ + { + "uuid": "5c6e3b25-a4cc-48e8-b04f-bc7f9bcf0719", + "body": "{\n \"success\": \"Successfully created a risk profile\"\n}", + "latency": 0, + "statusCode": 201, + "label": "Created", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "f65866fa-3f7c-4cd0-b731-a2cbbfbdea10", + "body": "{\n \"error\": \"Invalid request received\"\n}", + "latency": 0, + "statusCode": 400, + "label": "Invalid request", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "db611e13-2076-4af0-855d-fc2d7447e395", + "body": "{\n \"error\": \"A profile with that name already exists.\"\n}", + "latency": 0, + "statusCode": 404, + "label": "Already exists", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "26f0f76f-e787-4ebe-a3f8-ea3a6004bc15", + "type": "http", + "documentation": "Fetch the current format of the profiles questionnaire", + "method": "get", + "endpoint": "profiles/format", + "responses": [ + { + "uuid": "a044a39d-e40d-48b1-83f4-762adcc1c444", + "body": "[\n {\n \"question\": \"What type of device is this?\",\n \"type\": \"select\",\n \"options\": [\n \"IoT Sensor\",\n \"IoT Controller\",\n \"Smart Device\",\n \"Something else\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"How will this device be used at Google?\",\n \"type\": \"text-long\",\n \"validation\": {\n \"max\": \"128\",\n \"required\": true\n }\n },\n {\n \"question\": \"What is the email of the device owner(s)?\",\n \"type\": \"email-multiple\",\n \"validation\": {\n \"required\": true,\n \"max\": \"128\"\n }\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"type\": \"select\",\n \"options\": [\n \"Google\",\n \"Third Party\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"type\": \"select\",\n \"options\": [\n \"Yes\",\n \"No\",\n \"N/A\"\n ],\n \"default\": \"N/A\",\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Are any of the following statements true about your device?\",\n \"description\": \"This tells us about the data your device will collect\",\n \"type\": \"select-multiple\",\n \"options\": [\n \"The device collects any Personal Identifiable Information (PII) or Personal Health Information (PHI)\",\n \"The device collects intellectual property and trade secrets, sensitive business data, critical infrastructure data, identity assets\",\n \"The device stream confidential business data in real-time (seconds)?\"\n ]\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"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.\",\n \"type\": \"select-multiple\",\n \"options\": [\n \"PII/PHI, confidential business data, or crown jewel data is transmitted to a destination outside Alphabet's ownership\",\n \"Data transmission occurs across less-trusted networks (e.g. the internet).\",\n \"A failure in data transmission would likely have a substantial negative impact (https://www.rra.rocks/docs/standard_levels#levels-definitions)\",\n \"A confidentiality breach during transmission would have a substantial negative impact\",\n \"The device encrypts data during transmission\",\n \"The device network protocol is well-established and currently used by Google\"\n ]\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"type\": \"select\",\n \"options\": [\n \"Yes\",\n \"No\",\n \"I don't know\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"description\": \"This tells us about how this device is managed remotely.\",\n \"type\": \"select-multiple\",\n \"options\": [\n \"PII/PHI, or confidential business data is accessible from the device without authentication\",\n \"Unrecoverable actions (e.g. disk wipe) can be performed remotely\",\n \"Authentication is required for remote access\",\n \"The management interface is accessible from the public internet\",\n \"Static credentials are used for administration\"\n ]\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"description\": \"This informs us about what other systems and processes this device is a part of.\",\n \"type\": \"select-multiple\",\n \"options\": [\n \"The device monitors an environment for active risks to human life.\",\n \"The device is used to convey people, or critical property.\",\n \"The device controls robotics in human-accessible spaces.\",\n \"The device controls physical access systems.\",\n \"The device is involved in processes required by regulations, or compliance. (ex. privacy, security, safety regulations)\",\n \"The device's failure would cause faults in other high-criticality processes.\"\n ]\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "609cbb2f-b064-4d94-9d47-705f902af57d" + }, + { + "type": "route", + "uuid": "fd6fc843-4a77-4532-ba38-f81cdf345dc2" + }, + { + "type": "route", + "uuid": "32ddc969-bf37-4bd6-a926-329ffa351583" + }, + { + "type": "route", + "uuid": "24a155d7-8d30-458e-bc03-2240ecc95a30" + }, + { + "type": "route", + "uuid": "98e836d3-8af1-4321-9cdc-7feba8b40d8e" + }, + { + "type": "route", + "uuid": "239b1309-52da-4dc8-b914-35c1be6d2ff9" + }, + { + "type": "route", + "uuid": "a218761a-6ac6-466b-932e-9d58b6ec3a2e" + }, + { + "type": "route", + "uuid": "dcead7f8-71da-43ef-974b-ceddf8f262cc" + }, + { + "type": "route", + "uuid": "9a9482ee-09d6-4ab7-9fb0-bbfed791bd9d" + }, + { + "type": "route", + "uuid": "217a5e1b-fda6-4930-9f05-1372bc11590c" + }, + { + "type": "route", + "uuid": "0aed9026-18f5-4055-9670-83007ea18aaa" + }, + { + "type": "route", + "uuid": "a8ba2fcb-8e05-42f4-9d5f-cea63aa8ead7" + }, + { + "type": "route", + "uuid": "ac0ea995-ea90-4c6a-8767-3104e149de68" + }, + { + "type": "route", + "uuid": "12928dee-d2c8-4730-a1be-26ad796811dd" + }, + { + "type": "route", + "uuid": "f1d72c42-6f48-488d-a394-4d31b9a1da5e" + }, + { + "type": "route", + "uuid": "34d39075-6c44-41c9-9221-a9ed7ae27d3e" + }, + { + "type": "route", + "uuid": "0e927c03-7a2f-41cb-8404-bd94e9213fd3" + }, + { + "type": "route", + "uuid": "6d6b9fa9-c961-4007-89f3-9ea9cccc05e7" + }, + { + "type": "route", + "uuid": "fb33213d-4448-431c-8733-1ce292644af6" + }, + { + "type": "route", + "uuid": "64b9226c-ae14-4559-883f-470212f63c57" + }, + { + "type": "route", + "uuid": "90a6beb3-a4c0-491f-9beb-b36c29e0dee5" + }, + { + "type": "route", + "uuid": "86267532-5d37-4c12-9dfd-81d16c6164f1" + }, + { + "type": "route", + "uuid": "45635915-a144-46a9-b59a-f4b9d2fc8ebe" + }, + { + "type": "route", + "uuid": "26f0f76f-e787-4ebe-a3f8-ea3a6004bc15" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [], + "callbacks": [] +} \ No newline at end of file diff --git a/docs/dev/postman.json b/docs/dev/postman.json new file mode 100644 index 000000000..39e4529b3 --- /dev/null +++ b/docs/dev/postman.json @@ -0,0 +1,1063 @@ +{ + "info": { + "_postman_id": "3d270980-478e-4243-9130-ee5cab278ed5", + "name": "Testrun", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "28312403" + }, + "item": [ + { + "name": "Get System Interfaces", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/system/interfaces", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "interfaces" + ] + } + }, + "response": [ + { + "name": "Interfaces", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/system/interfaces", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "interfaces" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"enx00e04c020fa8\": \"00:e0:4c:02:0f:a8\",\n \"enx207bd26205e9\": \"20:7b:d2:62:05:e9\"\n}" + } + ] + }, + { + "name": "Set Configuration", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"network\": {\n \"device_intf\": \"enx207bd2620617\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"DEBUG\",\n \"startup_timeout\": 60,\n \"monitor_period\": 20,\n \"runtime\": 120\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/config", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config" + ] + } + }, + "response": [ + { + "name": "Configuration Set", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"network\": {\n \"device_intf\": \"enx00e04c020fa8\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"DEBUG\",\n \"runtime\": 120,\n \"startup_timeout\": 60,\n \"monitor_period\": 60 \n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/config", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"network\": {\n \"device_intf\": \"enx00e04c020fa8\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"DEBUG\",\n \"runtime\": 120,\n \"startup_timeout\": 60,\n \"monitor_period\": 60 \n}" + }, + { + "name": "Invalid JSON", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"network\": {\n \"device_intf\": \"enx00e04c020fa8\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"DEBUG\",\n \"runtime\": 120,\n \"startup_timeout\": 60\n \"monitor_period\": 60 \n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/config", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Invalid JSON received\"\n}" + } + ] + }, + { + "name": "Start Testrun", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"firmware\": \"1.2.2\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/start", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "start" + ] + } + }, + "response": [ + { + "name": "Invalid request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"device\": {\n \"firmware\": \"1.2.2\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/start", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "start" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Invalid request received\"\n}" + }, + { + "name": "Starting", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff\",\n \"firmware\": \"1.2.2\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/start", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "start" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"status\": \"Starting\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB 140\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": false\n }\n }\n }\n \"started\": \"2023-07-18T11:14:42.917670\",\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}" + }, + { + "name": "Device Not Found", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:99\",\n \"firmware\": \"1.2.2\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/start", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "start" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Device not found\"\n}" + }, + { + "name": "Invalid JSON", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff,\n \"firmware\": \"1.2.2\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/start", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "start" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Invalid JSON received\"\n}" + } + ] + }, + { + "name": "Stop Testrun", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/stop", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "stop" + ] + } + }, + "response": [ + { + "name": "Not Running", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/stop", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "stop" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Test Run is not running\"\n}" + }, + { + "name": "Stopped", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/stop", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "stop" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"success\": \"Test Run has stopped\"\n}" + } + ] + }, + { + "name": "Get Devices", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/devices", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "devices" + ] + } + }, + "response": [ + { + "name": "Devices", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/devices", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "devices" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "[\n {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB 140\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": false\n }\n }\n },\n {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff\",\n \"manufacturer\": \"Manufacturer X\",\n \"model\": \"Device X\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n }\n }\n }\n]" + }, + { + "name": "No Devices", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/devices", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "devices" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "[\n\n]" + } + ] + }, + { + "name": "Get Configuration", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/system/config", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config" + ] + } + }, + "response": [ + { + "name": "Configuration", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "localhost:8000/system/config", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"network\": {\n \"device_intf\": \"enx207bd2620617\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"INFO\",\n \"startup_timeout\": 60,\n \"monitor_period\": 60,\n \"runtime\": 120\n}" + } + ] + }, + { + "name": "Get System Status", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/status", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "status" + ] + } + }, + "response": [ + { + "name": "Test In Progress", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/status", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "status" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"status\": \"In Progress\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": null,\n \"tests\": {\n \"total\": 26,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Non-Compliant\"\n } \n ]\n }\n}" + }, + { + "name": "Not Running", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/status", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "status" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": [\n ]\n }\n}" + } + ] + }, + { + "name": "Get Test Reports", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/reports", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "reports" + ] + } + }, + "response": [ + { + "name": "Test In Progress", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/status", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "status" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"status\": \"In Progress\",\n \"device\": {\n \"manufacturer\": \"Delta\",\n \"model\": \"03-DIN-CPU\",\n \"mac_addr\": \"01:02:03:04:05:06\",\n \"firmware\": \"1.2.2\"\n },\n \"started\": \"2023-06-22T09:20:00.123Z\",\n \"finished\": null,\n \"tests\": {\n \"total\": 26,\n \"results\": [\n {\n \"name\": \"dns.network.hostname_resolution\",\n \"description\": \"The device should resolve hostnames\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"dns.network.from_dhcp\",\n \"description\": \"The device should use the DNS server provided by the DHCP server\",\n \"result\": \"Non-Compliant\"\n } \n ]\n }\n}" + }, + { + "name": "Not Running", + "originalRequest": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/system/status", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "status" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": [\n ]\n }\n}" + } + ] + }, + { + "name": "Create Device", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"manufacturer\": \"Delta\",\n \"mac_addr\": \"00:1e:42:35:73:c1\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"connection\": {\n \"enabled\": true\n },\n \"nmap\": {\n \"enabled\": false\n },\n \"dns\": {\n \"enabled\": false\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"tls\": {\n \"enabled\": false\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device" + ] + } + }, + "response": [ + { + "name": "Create Device", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"test_modules\": [\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n ]\n}" + }, + { + "name": "Invalid JSON", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\"\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Invalid JSON received\"\n}" + } + ] + }, + { + "name": "Edit Device", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c1\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB140\",\n \"test_modules\": {\n \"connection\": {\n \"enabled\": true\n },\n \"nmap\": {\n \"enabled\": false\n },\n \"dns\": {\n \"enabled\": false\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": false\n },\n \"tls\": {\n \"enabled\": false\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device/edit", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device", + "edit" + ] + } + }, + "response": [ + { + "name": "Create Device", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"test_modules\": [\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n ]\n}" + }, + { + "name": "Invalid JSON", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\"\n \"test_modules\": {\n \"dns\": {\n \"enabled\": false\n },\n \"nmap\": {\n \"enabled\": true\n },\n \"dhcp\": {\n \"enabled\": false\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "localhost:8000/device", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "device" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": null, + "header": null, + "cookie": [], + "body": "{\n \"error\": \"Invalid JSON received\"\n}" + } + ] + }, + { + "name": "Delete Report", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"timestamp\": \"2023-10-10 16:53:47\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8000/report", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "report" + ] + } + }, + "response": [] + }, + { + "name": "Get Version", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8000/system/version", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "version" + ] + } + }, + "response": [] + }, + { + "name": "Get Report", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8000/report/{device_name}/{timestamp}", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "report", + "{device_name}", + "{timestamp}" + ] + } + }, + "response": [] + }, + { + "name": "Get Export", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8000/export/{device_name}/{timestamp}", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "export", + "{device_name}", + "{timestamp}" + ] + } + }, + "response": [] + }, + { + "name": "List Certificates", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8000/system/config/certs/list", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config", + "certs", + "list" + ] + } + }, + "response": [] + }, + { + "name": "Upload Certificate", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "file", + "file": {} + }, + "url": { + "raw": "http://localhost:8000/system/config/certs/upload", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config", + "certs", + "upload" + ] + } + }, + "response": [] + }, + { + "name": "Delete Certificate", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"iot.bms.google.com\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8000/system/config/certs/delete", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8000", + "path": [ + "system", + "config", + "certs", + "delete" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index f3a778e13..3f89154ea 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -118,7 +118,9 @@ def __init__(self, test_run): # Profiles self._router.add_api_route("/profiles/format", - self._get_profiles_format) + self.get_profiles_format) + self._router.add_api_route("/profiles", + self.get_profiles) # Allow all origins to access the API origins = ["*"] @@ -612,7 +614,7 @@ def _get_test_run(self): return self._test_run # Profiles - def _get_profiles_format(self, response: Response): + def get_profiles_format(self, response: Response): # Check if Testrun was able to load the format originally if self.get_session().get_profiles_format() is None: @@ -623,6 +625,9 @@ def _get_profiles_format(self, response: Response): return self.get_session().get_profiles_format() + def get_profiles(self): + return self._generate_msg(True, "profiles") + # Certificates def get_certs(self): LOGGER.debug("Received certs list request") diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py new file mode 100644 index 000000000..959711415 --- /dev/null +++ b/framework/python/src/common/risk_profile.py @@ -0,0 +1,35 @@ +# 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. +"""Stores additional information about a device's risk""" + +class RiskProfile(): + + def __init__(self): + self._name = 'Unknown profile' + self._status = 'Draft' + self._timestamp = None + self._version = None + self._questions = [] + + def get_name(self): + return self._name + + def get_status(self): + return self._status + + def get_timestamp(self): + return self._timestamp + + def get_version(self): + return self._version \ No newline at end of file diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index d8ca44def..2031a5b30 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -37,10 +37,7 @@ CONFIG_FILE_PATH = 'local/system.json' PROFILE_FORMAT_PATH = 'resources/risk_assessment.json' -PROFILES_DIR = 'local/profiles' - -PROFILE_FORMAT_PATH = 'resources/risk_assessment.json' -PROFILES_DIR = 'local/profiles' +PROFILES_DIR = 'local/risk_profiles' LOGGER = logger.get_logger('session') @@ -348,6 +345,18 @@ def _load_profiles(self): LOGGER.error( 'An error occurred whilst loading the risk assessment format') LOGGER.debug(e) + + # 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('Discovered profile {risk_profile_file}') + 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 From 8486fabffa1735f8b62e8a17e57dd3bce8e58717 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Fri, 31 May 2024 13:58:58 +0100 Subject: [PATCH 5/7] Add load profiles format --- cmd/install | 14 ++++----- framework/python/src/api/api.py | 2 +- framework/python/src/common/risk_profile.py | 32 +++++++++++---------- framework/python/src/common/session.py | 17 ++++++++--- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/cmd/install b/cmd/install index 32c129afd..53d12b324 100755 --- a/cmd/install +++ b/cmd/install @@ -47,13 +47,6 @@ pip3 install -r framework/requirements.txt # Copy the default configuration cp -n local/system.json.example local/system.json -# Set file permissions on system config -# This does not work on GitHub actions -if logname ; then - USER_NAME=$(logname) - sudo chown "$USER_NAME" local/system.json -fi - # Exit out of python virtual environment deactivate @@ -65,4 +58,11 @@ mkdir -p local/devices mkdir -p local/root_certs mkdir -p local/risk_profiles +# Set file permissions on local +# This does not work on GitHub actions +if logname ; then + USER_NAME=$(logname) + sudo chown -R "$USER_NAME" local +fi + echo Finished installing Testrun diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index 247c1a86d..ab2dcf8ac 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -626,7 +626,7 @@ def get_profiles_format(self, response: Response): return self.get_session().get_profiles_format() def get_profiles(self): - return self._generate_msg(True, "profiles") + return self.get_session().get_profiles() # Certificates def get_certs(self): diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 959711415..303d3d576 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -16,20 +16,22 @@ class RiskProfile(): def __init__(self): - self._name = 'Unknown profile' - self._status = 'Draft' - self._timestamp = None - self._version = None - self._questions = [] + self.name = 'Unknown profile' + self.status = 'Draft' + self.created = None + self.version = None + self.questions = [] - def get_name(self): - return self._name + def __init__(self, json_data): + self.name = json_data['name'] + self.status = json_data['status'] + self.created = json_data['created'] + self.version = json_data['version'] + self.questions = json_data['questions'] + + # Check the profile has not expired + self.check_status() - def get_status(self): - return self._status - - def get_timestamp(self): - return self._timestamp - - def get_version(self): - return self._version \ No newline at end of file + def check_status(self): + self.status = 'Valid' + return self.status \ No newline at end of file diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index 2833df8b5..65e46454a 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -19,6 +19,7 @@ import json import os from common import util, logger +from common.risk_profile import RiskProfile # Certificate dependencies from cryptography import x509 @@ -40,9 +41,6 @@ PROFILE_FORMAT_PATH = 'resources/risk_assessment.json' PROFILES_DIR = 'local/risk_profiles' -PROFILE_FORMAT_PATH = 'resources/risk_assessment.json' -PROFILES_DIR = 'local/profiles' - LOGGER = logger.get_logger('session') class TestrunSession(): @@ -366,13 +364,24 @@ def _load_profiles(self): for risk_profile_file in os.listdir(os.path.join( self._root_dir, PROFILES_DIR )): - LOGGER.debug('Discovered profile {risk_profile_file}') + 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(json_data) + 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 + + def get_profiles(self): + return self._profiles def reset(self): self.set_status('Idle') From 66db61bd7443b27f595f224b51b5279ae1809f18 Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Fri, 31 May 2024 14:41:33 +0100 Subject: [PATCH 6/7] Pylint --- framework/python/src/api/api.py | 2 -- framework/python/src/common/risk_profile.py | 32 +++++++++------------ framework/python/src/common/session.py | 7 +++-- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py index ab2dcf8ac..884c803e6 100644 --- a/framework/python/src/api/api.py +++ b/framework/python/src/api/api.py @@ -13,8 +13,6 @@ # limitations under the License. """Provides Testrun data via REST API.""" from fastapi import (FastAPI, - File, - Form, APIRouter, Response, Request, diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 303d3d576..32e1c1a61 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -13,25 +13,19 @@ # limitations under the License. """Stores additional information about a device's risk""" + class RiskProfile(): - def __init__(self): - self.name = 'Unknown profile' - self.status = 'Draft' - self.created = None - self.version = None - self.questions = [] + def __init__(self, json_data): + self.name = json_data['name'] + self.status = json_data['status'] + self.created = json_data['created'] + self.version = json_data['version'] + self.questions = json_data['questions'] + + # Check the profile has not expired + self.check_status() - def __init__(self, json_data): - self.name = json_data['name'] - self.status = json_data['status'] - self.created = json_data['created'] - self.version = json_data['version'] - self.questions = json_data['questions'] - - # Check the profile has not expired - self.check_status() - - def check_status(self): - self.status = 'Valid' - return self.status \ No newline at end of file + def check_status(self): + self.status = 'Valid' + return self.status diff --git a/framework/python/src/common/session.py b/framework/python/src/common/session.py index 65e46454a..ca5015c52 100644 --- a/framework/python/src/common/session.py +++ b/framework/python/src/common/session.py @@ -197,7 +197,8 @@ def _load_version(self): LOGGER.debug('Failed getting the version from dpkg-query') # Try getting the version from the make control file try: - version = util.run_command('$(grep -R "Version: " $MAKE_CONTROL_DIR | awk "{print $2}"') + version = util.run_command( + '$(grep -R "Version: " $MAKE_CONTROL_DIR | awk "{print $2}"') except Exception as e: LOGGER.debug('Failed getting the version from make control file') LOGGER.error(e) @@ -356,7 +357,7 @@ def _load_profiles(self): LOGGER.error( 'An error occurred whilst loading the risk assessment format') LOGGER.debug(e) - + # Load existing profiles LOGGER.debug('Loading risk profiles') @@ -379,7 +380,7 @@ def _load_profiles(self): def get_profiles_format(self): return self._profile_format_json - + def get_profiles(self): return self._profiles From b771f5c74f01295646400a7a2ca5dbcdefde22ed Mon Sep 17 00:00:00 2001 From: Jacob Boddey Date: Fri, 31 May 2024 15:28:32 +0100 Subject: [PATCH 7/7] Add check status --- framework/python/src/common/risk_profile.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py index 32e1c1a61..bd82c076c 100644 --- a/framework/python/src/common/risk_profile.py +++ b/framework/python/src/common/risk_profile.py @@ -13,6 +13,9 @@ # limitations under the License. """Stores additional information about a device's risk""" +from datetime import datetime + +SECONDS_IN_YEAR = 31536000 class RiskProfile(): @@ -27,5 +30,15 @@ def __init__(self, json_data): self.check_status() def check_status(self): - self.status = 'Valid' + if self.status == 'Valid': + + # Check expiry + created_date = datetime.strptime( + self.created, "%Y-%m-%d %H:%M:%S").timestamp() + + today = datetime.now().timestamp() + + if created_date < (today - SECONDS_IN_YEAR): + self.status = 'Expired' + return self.status