diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index 0fdb8c379..9ede3a352 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -7,13 +7,17 @@ on:
push:
branches:
- 'dev'
+ - 'release/*'
+
+permissions:
+ contents: read
jobs:
- testrun_package:
+ create_package:
permissions: {}
name: Package
runs-on: ubuntu-22.04
- timeout-minutes: 5
+ timeout-minutes: 10
steps:
- name: Checkout source
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -24,4 +28,72 @@ jobs:
uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
with:
name: testrun_package
- path: testrun*.deb
\ No newline at end of file
+ path: testrun*.deb
+
+ install_package_22:
+ permissions: {}
+ needs: create_package
+ name: Install on Ubuntu 22.04
+ runs-on: ubuntu-22.04
+ timeout-minutes: 15
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - name: Download package
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: testrun_package
+ - name: Install dependencies
+ shell: bash {0}
+ run: sudo cmd/prepare
+ - name: Install package
+ shell: bash {0}
+ run: sudo apt install ./testrun*.deb
+ - name: Start testrun
+ shell: bash {0}
+ run: sudo testrun > >(tee testrun_output.log) 2>&1 &
+ - name: Verify testrun started
+ shell: bash {0}
+ run: |
+ sleep 5
+ if grep -q "API waiting for requests" testrun_output.log; then
+ echo "Testrun started successfully."
+ else
+ echo "Testrun did not start correctly."
+ cat testrun_output.log
+ exit 1
+ fi
+
+ install_package_24:
+ permissions: {}
+ needs: create_package
+ name: Install on Ubuntu 24.04
+ runs-on: ubuntu-24.04
+ timeout-minutes: 15
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - name: Download package
+ uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: testrun_package
+ - name: Install dependencies
+ shell: bash {0}
+ run: sudo cmd/prepare
+ - name: Install package
+ shell: bash {0}
+ run: sudo apt install ./testrun*.deb
+ - name: Start testrun
+ shell: bash {0}
+ run: sudo testrun > >(tee testrun_output.log) 2>&1 &
+ - name: Verify testrun started
+ shell: bash {0}
+ run: |
+ sleep 5
+ if grep -q "API waiting for requests" testrun_output.log; then
+ echo "Testrun started successfully."
+ else
+ echo "Testrun did not start correctly."
+ cat testrun_output.log
+ exit 1
+ fi
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 12884c718..f0f89a631 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -7,10 +7,6 @@ on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
- # To guarantee Maintained check is occasionally updated. See
- # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
- schedule:
- - cron: '20 6 * * 4'
push:
branches: [ "main" ]
@@ -70,4 +66,4 @@ jobs:
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
with:
- sarif_file: results.sarif
+ sarif_file: results.sarif
\ No newline at end of file
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 0556a2189..d8da3ada0 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -7,10 +7,29 @@ on:
- cron: '0 13 * * *'
jobs:
+ testrun_tests:
+ permissions: {}
+ name: Tests
+ runs-on: ubuntu-22.04
+ timeout-minutes: 30
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - name: Install dependencies
+ shell: bash {0}
+ run: cmd/prepare
+ - name: Install Testrun
+ shell: bash {0}
+ run: TESTRUN_DIR=. cmd/install
+ timeout-minutes: 30
+ - name: Run tests
+ shell: bash {0}
+ run: testing/tests/test_tests
+
testrun_baseline:
permissions: {}
name: Baseline
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- name: Checkout source
@@ -29,7 +48,7 @@ jobs:
testrun_api:
permissions: {}
name: API
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- name: Checkout source
@@ -39,7 +58,7 @@ jobs:
run: cmd/prepare
- name: Install Testrun
shell: bash {0}
- run: TESTRUN_DIR=. cmd/install
+ run: cmd/install -l
timeout-minutes: 30
- name: Run tests
shell: bash {0}
@@ -55,6 +74,52 @@ jobs:
name: runtime_api_${{ github.run_id }}
path: runtime.tgz
+ testrun_unit:
+ permissions: {}
+ name: Unit
+ runs-on: ubuntu-22.04
+ timeout-minutes: 15
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - name: Install dependencies
+ shell: bash {0}
+ run: cmd/prepare
+ - name: Install Testrun
+ shell: bash {0}
+ run: cmd/install -l
+ - name: Run tests for conn module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh conn captures ethtool ifconfig output
+ - name: Run tests for dns module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh dns captures reports output
+ - name: Run tests for ntp module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh ntp captures reports output
+ - name: Run tests for protocol module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh protocol captures output
+ - name: Run tests for services module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh services reports results output
+ - name: Run tests for tls module
+ shell: bash {0}
+ run: bash testing/unit/run_test_module.sh tls captures certAuth certs reports root_certs output
+ - name: Run tests for risk profiles
+ shell: bash {0}
+ run: bash testing/unit/run_report_test.sh testing/unit/risk_profile/risk_profile_test.py
+ - name: Run tests for reports
+ shell: bash {0}
+ run: bash testing/unit/run_report_test.sh testing/unit/report/report_test.py
+ - name: Upload reports
+ uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ if: ${{ always() }}
+ with:
+ if-no-files-found: error
+ name: unit_reports_${{ github.run_id }}
+ path: testing/unit/report/output
+
pylint:
permissions: {}
name: Pylint
@@ -76,7 +141,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
- node-version: 18.18.0
+ node-version: 18.19.0
- name: Install Chromium Browser
run: sudo apt install chromium-browser
- name: Install dependencies
@@ -85,7 +150,7 @@ jobs:
- name: Run tests
run: |
export CHROME_BIN=/usr/bin/chromium-browser
- CI=true npm run test-headless
+ CI=true npm run test-ci
env:
CI: true
working-directory: ./modules/ui
@@ -99,7 +164,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
- node-version: 18.18.0
+ node-version: 18.19.0
- name: Install dependencies
run: npm install && npm ci
working-directory: ./modules/ui
diff --git a/.gitignore b/.gitignore
index 82b6bbf64..f1c4ea203 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ build/
# Ignore generated files from unit tests
testing/unit_test/temp/
+testing/unit/conn/output/
testing/unit/dns/output/
testing/unit/nmap/output/
testing/unit/ntp/output/
@@ -15,6 +16,12 @@ testing/unit/tls/output/
testing/unit/tls/tmp/
testing/unit/report/output/
testing/unit/risk_profile/output/
+testing/unit/services/output/
+
+# Ignore generated files from requirements generation
+*requirements_freeze.txt
+*unique_freeze.txt
+*requirements_gen.txt
*.deb
make/DEBIAN/postinst
diff --git a/README.md b/README.md
index 4a04e8885..e6cfc8943 100644
--- a/README.md
+++ b/README.md
@@ -4,84 +4,93 @@
[](https://github.com/google/testrun/actions/workflows/github-code-scanning/codeql)
[](https://github.com/google/testrun/actions/workflows/testing.yml)
-## Introduction :wave:
-Testrun automates specific test cases to verify network and security functionality in IoT devices. It is an open source tool which allows manufacturers of IP capable devices to test their devices for the purposes of Device Qualification within the BOS program.
+# Introduction :wave:
-## Motivation :bulb:
-Without tools like Testrun, test labs and engineers may need to maintain a large and complex network coupled with dynamic configuration files and constant software updates. The major issues which can and should be solved are:
- 1) The complexity of managing a testing network
- 2) The time required to perform testing of network functionality
- 3) The accuracy and consistency of testing network functionality
+Testrun automates specific test cases to verify network and security functionality in IoT devices. It's an open-source tool that manufacturers use to test their IP-capable devices for the purpose of device qualification within Google's Building Operating System (BOS) program.
-## How it works :triangular_ruler:
-Testrun creates an isolated and controlled network environment on a linux machine. This removes the necessity for complex hardware, advanced knowledge and networking experience whilst enabling test engineers to validate device behaviour against Google’s Building Operating System requirements.
+# Motivation :bulb:
-Two modes are supported by Testrun:
+Test labs and engineers often need to maintain a large and complex network coupled with dynamic configuration files and constant software updates. Testrun helps address major issues like:
-
- Automated testing
-
+- The complexity of managing a testing network
+- The time required to perform testing of network functionality
+- The accuracy and consistency of testing network functionality
-Once the device has become operational (steady state), automated testing of the DUT (device under test) will begin. Containerized test modules will then execute against the device, one module at a time. Once all test modules have been executed, a report will be produced - presenting the results.
-
- Lab network
-
+Testrun supports two modes: automated testing and lab network.
-When manual testing or configuration changes are required, Testrun will provide the network and some tools to assist an engineer performing the additional testing. This reduces the need to maintain a separate but identical lab network. Testrun will take care of packet captures and logs for each network service for further debugging.
+## Automated testing
-
+# Contents
-## Contents
+- [Get started](/docs/get_started.md)
+ - [Run on a virtual machine](/docs/virtual_machine.md)
+- [Network](/docs/network/README.md)
+ - [Network addresses](/docs/network/addresses.md)
+ - [Add a new network service](/docs/network/add_new_service.md)
+- [Testing](/docs/test/README.md)
+ - [Test modules](/docs/test/modules.md)
+ - [Test results](/docs/test/statuses.md)
+- [Developer guidelines](/docs/dev/README.md)
+- [Accessibility](/docs/ui/accessibility.md)
+- [Roadmap](/docs/roadmap.pdf)
- - [Get Started](get_started.md)
- - [Network](network/README.md)
- - [Network Overview](network/README.md)
- - [How to identify network interfaces](network/identify_interfaces.md)
- - [Addresses](network/addresses.md)
- - [Add a new network service](network/add_new_service.md)
- - [Testing](test/README.md)
- - [Test modules](test/modules.md)
- - [Test statuses](test/statuses.md)
- - [Development](dev/README.md)
- - [Running on a virtual machine](virtual_machine.md)
- - [Accessibility](ui/accessibility.mp4)
- - [Roadmap](roadmap.pdf)
+# Something missing?
+
+To request additional documentation or report an issue with existing resources, visit [the Issues tab](https://github.com/google/testrun/issues/new/choose).
diff --git a/docs/additional_config.md b/docs/additional_config.md
new file mode 100644
index 000000000..ea9aaaf51
--- /dev/null
+++ b/docs/additional_config.md
@@ -0,0 +1,121 @@
+# Additional Configuration Options
+
+Some configuration options are available but not exposed through the user interface and requires direct access.
+Modification of various configuration files is necessary to access these options.
+
+## Override test module timeout at the system level
+
+Testrun attempts to set reasonable timeouts for test modules to prevent overly long test times but sometimes
+a device or series of device may require longer than these default values. These can be overridden at
+the test module configuration level but is not preferred since these changes will be undone during every
+version upgrade. To modify these values:
+
+1. Navigate to the testrun installation directory. By default, this will be at:
+ `/usr/local/testrun`
+
+2. Open the system.json file and add the following section:
+ `"test_modules":{}`
+
+3. Add the module name(s) and timeout property into this test_modules section you wish to
+set the timeout property for:
+ ```
+ "test_modules":{
+ "connection":{
+ "timeout": 500
+ }
+ }
+ ```
+
+Before timeout options:
+```
+{
+ "network": {
+ "device_intf": "ens0",
+ "internet_intf": "ens1"
+ },
+ "log_level": "DEBUG",
+ "startup_timeout": 60,
+ "monitor_period": 60,
+ "max_device_reports": 5,
+ "org_name": "",
+ "single_intf": false
+ }
+```
+
+After timeout options:
+```
+{
+ "network": {
+ "device_intf": "ens0",
+ "internet_intf": "ens1"
+ },
+ "log_level": "DEBUG",
+ "startup_timeout": 60,
+ "monitor_period": 60,
+ "max_device_reports": 5,
+ "org_name": "",
+ "single_intf": false,
+ "test_modules":{
+ "connection":{
+ "timeout": 500
+ }
+ }
+}
+```
+
+## Override test module log level at the system level
+
+Test modules default to the log level info to prevent unecessary logging. These can be overridden at the test module configuration level but is not preferred since these changes will be undone during every version upgrade. To modify these values:
+
+1. Navigate to the testrun installation directory. By default, this will be at:
+ `/usr/local/testrun`
+
+2. Open the system.json file and add the following section:
+ `"test_modules":{}`
+
+3. Add the module name(s) and log_level property into this test_modules section you wish to
+set the log_level property for:
+ ```
+ "test_modules":{
+ "connection":{
+ "log_level": "DEGUG"
+ }
+ }
+ ```
+Valid options for modifying the log level are: INFO, DEBUG, WARNING, ERROR.
+
+Before log_level options:
+```
+{
+ "network": {
+ "device_intf": "ens0",
+ "internet_intf": "ens1"
+ },
+ "log_level": "DEBUG",
+ "startup_timeout": 60,
+ "monitor_period": 60,
+ "max_device_reports": 5,
+ "org_name": "",
+ "single_intf": false
+ }
+```
+
+After log_level options:
+```
+{
+ "network": {
+ "device_intf": "ens0",
+ "internet_intf": "ens1"
+ },
+ "log_level": "DEBUG",
+ "startup_timeout": 60,
+ "monitor_period": 60,
+ "max_device_reports": 5,
+ "org_name": "",
+ "single_intf": false,
+ "test_modules":{
+ "connection":{
+ "log_level": "DEBUG"
+ }
+ }
+```
\ No newline at end of file
diff --git a/docs/configure_device.md b/docs/configure_device.md
deleted file mode 100644
index 1db7155be..000000000
--- a/docs/configure_device.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-## Device Configuration (Deprecated)
-
-The device configuration file allows you to customize the testing behavior for a specific device. This file is located at `local/devices/{Device Name}/device_config.json`. Below is an overview of how to configure the device tests.
-
-## Device Information
-
-The device information section includes the manufacturer, model, and MAC address of the device. These details help identify the specific device being tested.
-
-## Test Modules
-
-Test modules are groups of tests that can be enabled or disabled as needed. You can choose which test modules to run on your device.
-
-### Enabling and Disabling Test Modules
-
-To enable or disable a test module, modify the `enabled` field within the respective module. Setting it to `true` enables the module, while setting it to `false` disables the module.
-
-## Customizing the Device Configuration
-
-To customize the device configuration for your specific device, follow these steps:
-
-1. Copy the default configuration file provided in the `resources/devices/template` folder.
- - Create a new folder for your device under `local/devices` directory.
- - Copy the `device_config.json` file from `resources/devices/template` to the newly created device folder.
-
-This ensures that you have a copy of the default configuration file, which you can then modify for your specific device.
-
-> Note: Ensure that the device configuration file is properly formatted, and the changes made align with the intended test behavior. Incorrect settings or syntax may lead to unexpected results during testing.
-
-If you encounter any issues or need assistance with the device configuration, refer to the Testrun documentation or ask a question on the Issues page.
diff --git a/docs/dev/README.md b/docs/dev/README.md
new file mode 100644
index 000000000..076cb827c
--- /dev/null
+++ b/docs/dev/README.md
@@ -0,0 +1,35 @@
+
+
+# Developer guidelines
+
+## How to contribute
+
+As an open source project, we encourage contributions from the community to help Testrun remain an expanding but stable product. To contribute, follow the steps below:
+
+1. Sign the [Google Contributor License Agreement (CLA)](https://cla.developers.google.com/).
+ - Whether you're an individual or contributing on behalf of your organization, you must be covered by a Google CLA.
+
+1. Determine the scope of your contribution.
+ - Keep it simple. Your contribution is more likely to be accepted if you change fewer files.
+ - Ensure your pull request addresses one thing, such as a bug fix, dependency issue, or new framework capability.
+
+1. Reach out to the core maintainers at [testrun-team@googlegroups.com](mailto:testrun-team@googlegroups.com).
+ - They can provide confirmation that your proposed changes meet our objectives and align with Testrun principles, making them more likely to be accepted.
+
+1. Fork Testrun and get developing.
+ - We aim to provide thorough and clear developer documentation to help you contribute successfully.
+
+## Code quality
+
+To ensure code quality, use the appropriate style guide when developing code for Testrun:
+
+- [Python](https://google.github.io/styleguide/pyguide.html)
+- [Angular](https://google.github.io/styleguide/angularjs-google-style.html)
+- [Shell](https://google.github.io/styleguide/shellguide.html)
+- [HTML/CSS](https://google.github.io/styleguide/htmlcssguide.html)
+- [JSON](https://google.github.io/styleguide/jsoncstyleguide.xml)
+- [Markdown](https://google.github.io/styleguide/docguide/style.html)
+
+## Automated actions
+
+The current code base has zero code lint issues. To maintain this, all lint checks are enforced on pull requests to dev and main. You should ensure these lint checks pass before marking your pull requests as Ready for review.
diff --git a/docs/dev/mockoon.json b/docs/dev/mockoon.json
index a73eb5beb..8e9590e76 100644
--- a/docs/dev/mockoon.json
+++ b/docs/dev/mockoon.json
@@ -604,8 +604,8 @@
"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]",
+ "uuid": "6adc954a-55c9-40ed-8f49-cf38f659d883",
+ "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 TLS 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 \"export\": \"http://localhost:8000/export/123 123/2024-05-03T12:09:59\"\n \n }\n]",
"latency": 0,
"statusCode": 200,
"label": "",
@@ -885,10 +885,10 @@
},
{
"uuid": "220e4ba9-6463-4dc3-b714-f77643706b7d",
- "body": "{\n \"error\": \"An error occured whilst deleting the report\"\n}",
+ "body": "{\n \"error\": \"An error occurred whilst deleting the report\"\n}",
"latency": 0,
"statusCode": 500,
- "label": "Error occured",
+ "label": "Error occurred",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
@@ -971,10 +971,10 @@
},
{
"uuid": "a7fbb2c8-81dc-4a40-80d6-482119314086",
- "body": "{\n \"error\": \"An error occured whilst getting the report\"\n}",
+ "body": "{\n \"error\": \"An error occurred whilst getting the report\"\n}",
"latency": 0,
"statusCode": 500,
- "label": "Error occured",
+ "label": "Error occurred",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
@@ -1151,10 +1151,10 @@
},
{
"uuid": "3bcb2d6d-3290-43bb-8392-7bfffda4feae",
- "body": "{\n \"error\": \"An error occured whilst getting the test attempt\"\n}",
+ "body": "{\n \"error\": \"An error occurred whilst getting the test attempt\"\n}",
"latency": 0,
"statusCode": 500,
- "label": "Error occured",
+ "label": "Error occurred",
"headers": [],
"bodyType": "INLINE",
"filePath": "",
@@ -1602,6 +1602,111 @@
}
],
"responseMode": null
+ },
+ {
+ "uuid": "af7fdcb0-721d-4198-a8ef-c6d8c4eba8c8",
+ "type": "http",
+ "documentation": "Get a Testrun PDF profile",
+ "method": "post",
+ "endpoint": "report/{profile_name}",
+ "responses": [
+ {
+ "uuid": "9a759f46-4bc4-433a-be86-e456f069c217",
+ "body": "",
+ "latency": 0,
+ "statusCode": 200,
+ "label": "Profile found - no device selected",
+ "headers": [],
+ "bodyType": "FILE",
+ "filePath": "",
+ "databucketID": "",
+ "sendFileAsBody": false,
+ "rules": [],
+ "rulesOperator": "OR",
+ "disableTemplating": false,
+ "fallbackTo404": false,
+ "default": true,
+ "crudKey": "id",
+ "callbacks": []
+ },
+ {
+ "uuid": "c9a09ae7-3158-4956-93ac-4c8a90dfced8",
+ "body": "",
+ "latency": 0,
+ "statusCode": 200,
+ "label": "Profile found - device selected ",
+ "headers": [],
+ "bodyType": "FILE",
+ "filePath": "",
+ "databucketID": "",
+ "sendFileAsBody": false,
+ "rules": [],
+ "rulesOperator": "OR",
+ "disableTemplating": false,
+ "fallbackTo404": false,
+ "default": false,
+ "crudKey": "id",
+ "callbacks": []
+ },
+ {
+ "uuid": "5f98471e-15b6-47a4-a68d-e98c3a538b40",
+ "body": "{\n \"error\": \"Profile could not be found\"\n}",
+ "latency": 0,
+ "statusCode": 404,
+ "label": "Profile not found",
+ "headers": [],
+ "bodyType": "INLINE",
+ "filePath": "",
+ "databucketID": "",
+ "sendFileAsBody": false,
+ "rules": [],
+ "rulesOperator": "OR",
+ "disableTemplating": false,
+ "fallbackTo404": false,
+ "default": false,
+ "crudKey": "id",
+ "callbacks": []
+ },
+ {
+ "uuid": "767d9e78-386e-4bf7-bec8-71a005efdce9",
+ "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": "5d76bea0-39c1-45f2-80f1-de6f770cb999",
+ "body": "{\n \"error\": \"Error retrieving the profile PDF\"\n}",
+ "latency": 0,
+ "statusCode": 500,
+ "label": "Error occurred",
+ "headers": [],
+ "bodyType": "INLINE",
+ "filePath": "",
+ "databucketID": "",
+ "sendFileAsBody": false,
+ "rules": [],
+ "rulesOperator": "OR",
+ "disableTemplating": false,
+ "fallbackTo404": false,
+ "default": false,
+ "crudKey": "id",
+ "callbacks": []
+ }
+ ],
+ "responseMode": null
}
],
"rootChildren": [
@@ -1700,6 +1805,10 @@
{
"type": "route",
"uuid": "26f0f76f-e787-4ebe-a3f8-ea3a6004bc15"
+ },
+ {
+ "type": "route",
+ "uuid": "af7fdcb0-721d-4198-a8ef-c6d8c4eba8c8"
}
],
"proxyMode": false,
diff --git a/docs/dev/postman.json b/docs/dev/postman.json
index 39e4529b3..08369ac55 100644
--- a/docs/dev/postman.json
+++ b/docs/dev/postman.json
@@ -1,9 +1,10 @@
{
"info": {
- "_postman_id": "3d270980-478e-4243-9130-ee5cab278ed5",
+ "_postman_id": "f42dd4c6-e1a3-4ae3-a991-24cceb4d2627",
"name": "Testrun",
+ "description": "API endpoints for the Testrun API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
- "_exporter_id": "28312403"
+ "_exporter_id": "37950585"
},
"item": [
{
@@ -21,11 +22,12 @@
"system",
"interfaces"
]
- }
+ },
+ "description": "Obtain a list of applicable and available network adapters for use in testing"
},
"response": [
{
- "name": "Interfaces",
+ "name": "Interfaces Available (200)",
"originalRequest": {
"method": "GET",
"header": [],
@@ -43,10 +45,100 @@
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
"body": "{\n \"enx00e04c020fa8\": \"00:e0:4c:02:0f:a8\",\n \"enx207bd26205e9\": \"20:7b:d2:62:05:e9\"\n}"
+ },
+ {
+ "name": "No interfaces (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/interfaces",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "interfaces"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{}"
+ }
+ ]
+ },
+ {
+ "name": "Get Configuration",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/config",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config"
+ ]
+ },
+ "description": "Get the current system configuration"
+ },
+ "response": [
+ {
+ "name": "Get Configuration (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/config",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "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}"
}
]
},
@@ -57,7 +149,7 @@
"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}",
+ "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 \"org_name\": \"Google\"\n}",
"options": {
"raw": {
"language": "json"
@@ -74,17 +166,18 @@
"system",
"config"
]
- }
+ },
+ "description": "Update the current system configuration"
},
"response": [
{
- "name": "Configuration Set",
+ "name": "Configuration Set (200)",
"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}",
+ "raw": "{\n \"network\": {\n \"device_intf\": \"enx00e04c020fa8\",\n \"internet_intf\": \"enx207bd26205e9\"\n },\n \"log_level\": \"DEBUG\",\n \"runtime\": 120, // Optional\n \"startup_timeout\": 60, // Optional\n \"monitor_period\": 60 // Optional\n}",
"options": {
"raw": {
"language": "json"
@@ -105,13 +198,20 @@
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "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",
+ "name": "Invalid JSON (400)",
"originalRequest": {
"method": "POST",
"header": [],
@@ -138,8 +238,15 @@
},
"status": "Bad Request",
"code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
"body": "{\n \"error\": \"Invalid JSON received\"\n}"
}
@@ -152,7 +259,7 @@
"header": [],
"body": {
"mode": "raw",
- "raw": "{\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"firmware\": \"1.2.2\"\n }\n}",
+ "raw": "{\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff\",\n \"firmware\": \"1.2.2\",\n \"test_modules\": {\n \"protocol\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": false\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"tls\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n }\n }\n }\n}",
"options": {
"raw": {
"language": "json"
@@ -169,11 +276,52 @@
"system",
"start"
]
- }
+ },
+ "description": "Start Testrun against a target device"
},
"response": [
{
- "name": "Invalid request",
+ "name": "Starting (200)",
+ "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 \"test_modules\": {\n \"protocol\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": false\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"tls\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n }\n }\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": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"status\": \"Waiting for Device\",\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB140\",\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}"
+ },
+ {
+ "name": "Invalid Request (400)",
"originalRequest": {
"method": "POST",
"header": [],
@@ -200,19 +348,26 @@
},
"status": "Bad Request",
"code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
"body": "{\n \"error\": \"Invalid request received\"\n}"
},
{
- "name": "Starting",
+ "name": "Invalid JSON (400)",
"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}",
+ "raw": "{\n \"device\": {\n \"mac_addr\": \"aa:bb:cc:dd:ee:ff,\n \"firmware\": \"1.2.2\"\n }\n}",
"options": {
"raw": {
"language": "json"
@@ -231,15 +386,22 @@
]
}
},
- "status": "OK",
- "code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "body": "{\n \"error\": \"Invalid JSON received\"\n}"
},
{
- "name": "Device Not Found",
+ "name": "Device Not Found (404)",
"originalRequest": {
"method": "POST",
"header": [],
@@ -266,19 +428,26 @@
},
"status": "Not Found",
"code": 404,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"error\": \"Device not found\"\n}"
+ "body": "{\n \"error\": \"A device with that MAC address could not be found\"\n}"
},
{
- "name": "Invalid JSON",
+ "name": "Testrun Already in Progress (409)",
"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}",
+ "raw": "{\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"firmware\": \"1.2.2\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n }\n }\n }\n}",
"options": {
"raw": {
"language": "json"
@@ -297,12 +466,19 @@
]
}
},
- "status": "Bad Request",
- "code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "status": "Conflict",
+ "code": 409,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"error\": \"Invalid JSON received\"\n}"
+ "body": "{\n \"error\": \"Testrun cannot be started whilst a test is running on another device\"\n}"
}
]
},
@@ -330,11 +506,12 @@
"system",
"stop"
]
- }
+ },
+ "description": "Stop Testrun from running against a device"
},
"response": [
{
- "name": "Not Running",
+ "name": "Stopped (200)",
"originalRequest": {
"method": "POST",
"header": [],
@@ -359,15 +536,22 @@
]
}
},
- "status": "Bad Request",
- "code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"error\": \"Test Run is not running\"\n}"
+ "body": "{\n \"success\": \"Testrun stopped\"\n}"
},
{
- "name": "Stopped",
+ "name": "Not Running (404)",
"originalRequest": {
"method": "POST",
"header": [],
@@ -392,168 +576,151 @@
]
}
},
- "status": "OK",
- "code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"success\": \"Test Run has stopped\"\n}"
+ "body": "{\n \"error\": \"Testrun is not currently running\"\n}"
}
]
},
{
- "name": "Get Devices",
+ "name": "Get System Status",
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
"request": {
"method": "GET",
"header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
"url": {
- "raw": "localhost:8000/devices",
+ "raw": "localhost:8000/system/status",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "devices"
+ "system",
+ "status"
]
- }
+ },
+ "description": "Get the current status of Testrun. Suggested that this is called every 5 seconds to capture most events"
},
"response": [
{
- "name": "Devices",
+ "name": "Monitoring (200)",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
- "raw": "localhost:8000/devices",
+ "raw": "localhost:8000/system/status",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "devices"
+ "system",
+ "status"
]
}
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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]"
+ "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}"
},
{
- "name": "No Devices",
+ "name": "Test in Progress (200)",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
- "raw": "localhost:8000/devices",
+ "raw": "localhost:8000/system/status",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "devices"
+ "system",
+ "status"
]
}
},
"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"
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
],
- "port": "8000",
- "path": [
- "system",
- "config"
- ]
- }
- },
- "response": [
+ "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": "Configuration",
+ "name": "Cancelled (200)",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
- "raw": "localhost:8000/system/config",
+ "raw": "localhost:8000/system/status",
"host": [
"localhost"
],
"port": "8000",
"path": [
"system",
- "config"
+ "status"
]
}
},
"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"
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
}
- }
- },
- "url": {
- "raw": "localhost:8000/system/status",
- "host": [
- "localhost"
],
- "port": "8000",
- "path": [
- "system",
- "status"
- ]
- }
- },
- "response": [
+ "cookie": [],
+ "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}"
+ },
{
- "name": "Test In Progress",
+ "name": "Waiting for Device (200)",
"originalRequest": {
"method": "GET",
"header": [],
- "body": {
- "mode": "raw",
- "raw": "",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
"url": {
"raw": "localhost:8000/system/status",
"host": [
@@ -568,25 +735,23 @@
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "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}"
},
{
- "name": "Not Running",
+ "name": "Non-Compliant (200)",
"originalRequest": {
"method": "GET",
"header": [],
- "body": {
- "mode": "raw",
- "raw": "",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
"url": {
"raw": "localhost:8000/system/status",
"host": [
@@ -601,83 +766,148 @@
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "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}"
+ },
+ {
+ "name": "Compliant (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/status",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "status"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "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}"
+ },
+ {
+ "name": "Not Running - Idle (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/status",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "status"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": [\n ]\n }\n}"
+ "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": []\n }\n}"
}
]
},
{
- "name": "Get Test Reports",
- "protocolProfileBehavior": {
- "disableBodyPruning": true
- },
+ "name": "Shutdown Testrun",
"request": {
- "method": "GET",
+ "method": "POST",
"header": [],
- "body": {
- "mode": "raw",
- "raw": "",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
"url": {
- "raw": "localhost:8000/reports",
+ "raw": "http://localhost:8000/system/shutdown",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "reports"
+ "system",
+ "shutdown"
]
- }
+ },
+ "description": "Stop the Testrun framework"
},
"response": [
{
- "name": "Test In Progress",
+ "name": "Shutdown Testrun (200)",
"originalRequest": {
- "method": "GET",
+ "method": "POST",
"header": [],
- "body": {
- "mode": "raw",
- "raw": "",
- "options": {
- "raw": {
- "language": "json"
- }
- }
- },
"url": {
- "raw": "localhost:8000/system/status",
+ "raw": "http://localhost:8000/system/shutdown",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
"system",
- "status"
+ "shutdown"
]
}
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "body": "null"
},
{
- "name": "Not Running",
+ "name": "Test in Progress (400)",
"originalRequest": {
- "method": "GET",
- "header": [],
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
"body": {
"mode": "raw",
- "raw": "",
+ "raw": "{\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"firmware\": \"1.2.2\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n }\n }\n }\n}",
"options": {
"raw": {
"language": "json"
@@ -685,34 +915,99 @@
}
},
"url": {
- "raw": "localhost:8000/system/status",
+ "raw": "http://localhost:8000/system/shutdown",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
"system",
- "status"
+ "shutdown"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Unable to shutdown. A test is currently in progress.\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Get Version",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/version",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "version"
+ ]
+ },
+ "description": "Get the current Testrun version and check if a software update is available"
+ },
+ "response": [
+ {
+ "name": "Get Version (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/version",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "version"
]
}
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"status\": \"Idle\",\n \"device\": null,\n \"started\": null,\n \"finished\": null,\n \"tests\": {\n \"total\": 0,\n \"results\": [\n ]\n }\n}"
+ "body": "{\n \"installed_version\": \"v2.0\",\n \"update_available\": false,\n \"latest_version\": \"v2.0\",\n \"latest_version_url\": \"https://github.com/google/testrun/releases/tag/v2.0\"\n}"
}
]
},
{
- "name": "Create Device",
+ "name": "Get Test Reports",
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
"request": {
- "method": "POST",
+ "method": "GET",
"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}",
+ "raw": "",
"options": {
"raw": {
"language": "json"
@@ -720,91 +1015,107 @@
}
},
"url": {
- "raw": "localhost:8000/device",
+ "raw": "localhost:8000/reports",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device"
+ "reports"
]
- }
+ },
+ "description": "Get all previous Testrun reports"
},
"response": [
{
- "name": "Create Device",
+ "name": "Get Test Reports (200)",
"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"
- }
+ "method": "GET",
+ "header": [
+ {
+ "key": "Origin",
+ "value": "http://localhost:8000",
+ "type": "text"
}
- },
+ ],
"url": {
- "raw": "localhost:8000/device",
+ "raw": "localhost:8000/reports",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device"
+ "reports"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
]
}
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "body": "[\n {\n \"testrun\": {\n \"version\": \"2.0\"\n },\n \"mac_addr\": \"00:1e:42:28:9e:4a\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:28:9e:4a\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB140\",\n \"firmware\": \"test\",\n \"test_modules\": {\n \"protocol\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"tls\": {\n \"enabled\": true\n },\n \"connection\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n }\n }\n },\n \"status\": \"Non-Compliant\",\n \"started\": \"2000-01-01 00:00:00\",\n \"finished\": \"2000-01-01 00:30:00\",\n \"tests\": {\n \"total\": 40,\n \"results\": [\n {\n \"name\": \"protocol.valid_bacnet\",\n \"description\": \"BACnet device could not be discovered\",\n \"expected_behavior\": \"BACnet traffic can be seen on the network and packets are valid and not malformed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\"\n },\n {\n \"name\": \"protocol.bacnet.version\",\n \"description\": \"Device did not respond to BACnet discovery\",\n \"expected_behavior\": \"The BACnet client implements an up to date version of BACnet\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\"\n },\n {\n \"name\": \"protocol.valid_modbus\",\n \"description\": \"Device did not respond to Modbus connection\",\n \"expected_behavior\": \"Any Modbus functionality works as expected and valid Modbus traffic can be observed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\"\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 \"recommendations\": [\n \"Disable all unsecure HTTP servers\",\n \"Setup TLS on the web server\"\n ]\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\": \"connection.port_link\",\n \"description\": \"No port errors detected\",\n \"expected_behavior\": \"When the etherent cable is connected to the port, the device triggers the port to its enabled \\\"Link UP\\\" (LEDs illuminate on device and switch ports if present) state, and the switch shows no errors with the LEDs and when interrogated with a \\\"show interface\\\" command on most network switches.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.port_speed\",\n \"description\": \"Succesfully auto-negotiated speeds above 10 Mbps\",\n \"expected_behavior\": \"When the ethernet cable is connected to the port, the device autonegotiates a speed that can be checked with the \\\"show interface\\\" command on most network switches. The output of this command must also show that the \\\"configured speed\\\" is set to \\\"auto\\\".\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.port_duplex\",\n \"description\": \"Succesfully auto-negotiated full duplex\",\n \"expected_behavior\": \"When the ethernet cable is connected to the port, the device autonegotiates a full-duplex connection.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.switch.arp_inspection\",\n \"description\": \"Device uses ARP\",\n \"expected_behavior\": \"Device continues to operate correctly when ARP inspection is enabled on the switch. No functionality is lost with ARP inspection enabled.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.switch.dhcp_snooping\",\n \"description\": \"Device does not act as a DHCP server\",\n \"expected_behavior\": \"Device continues to operate correctly when DHCP snooping is enabled on the switch.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.dhcp_address\",\n \"description\": \"Device responded to leased ip address\",\n \"expected_behavior\": \"The device is not setup with a static IP address. The device accepts an IP address from a DHCP server (RFC 2131) and responds succesfully to an ICMP echo (ping) request.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.mac_address\",\n \"description\": \"MAC address found: 00:1e:42:28:9e:4a\",\n \"expected_behavior\": \"N/A\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.mac_oui\",\n \"description\": \"OUI Manufacturer found: Teltonika\",\n \"expected_behavior\": \"The MAC address prefix is registered in the IEEE Organizationally Unique Identifier database.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.private_address\",\n \"description\": \"All subnets are supported\",\n \"expected_behavior\": \"The device under test accepts IP addresses within all ranges specified in RFC 1918 and communicates using these addresses. The Internet Assigned Numbers Authority (IANA) has reserved the following three blocks of the IP address space for private internets. 10.0.0.0 - 10.255.255.255.255 (10/8 prefix). 172.16.0.0 - 172.31.255.255 (172.16/12 prefix). 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.shared_address\",\n \"description\": \"All subnets are supported\",\n \"expected_behavior\": \"The device under test accepts IP addresses within the ranges specified in RFC 6598 and communicates using these addresses\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.dhcp_disconnect\",\n \"description\": \"An error occurred whilst running this test\",\n \"expected_behavior\": \"A client SHOULD use DHCP to reacquire or verify its IP address and network parameters whenever the local network parameters may have changed; e.g., at system boot time or after a disconnection from the local network, as the local network configuration may change without the client's or user's knowledge. If a client has knowledge ofa previous network address and is unable to contact a local DHCP server, the client may continue to use the previous network addres until the lease for that address expires. If the lease expires before the client can contact a DHCP server, the client must immediately discontinue use of the previous network address and may inform local users of the problem.\",\n \"required_result\": \"Required\",\n \"result\": \"Error\"\n },\n {\n \"name\": \"connection.single_ip\",\n \"description\": \"Device is using multiple IP addresses\",\n \"expected_behavior\": \"The device under test does not behave as a network switch and only requets one IP address. This test is to avoid that devices implement network switches that allow connecting strings of daisy chained devices to one single network port, as this would not make 802.1x port based authentication possible.\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Ensure that all ports on the device are isolated\",\n \"Ensure only one DHCP client is running\"\n ]\n },\n {\n \"name\": \"connection.target_ping\",\n \"description\": \"Device responds to ping\",\n \"expected_behavior\": \"The device under test responds to an ICMP echo (ping) request.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.ipaddr.ip_change\",\n \"description\": \"Device has accepted an IP address change\",\n \"expected_behavior\": \"If the lease expires before the client receiveds a DHCPACK, the client moves to INIT state, MUST immediately stop any other network processing and requires network initialization parameters as if the client were uninitialized. If the client then receives a DHCPACK allocating the client its previous network addres, the client SHOULD continue network processing. If the client is given a new network address, it MUST NOT continue using the previous network address and SHOULD notify the local users of the problem.\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.ipaddr.dhcp_failover\",\n \"description\": \"Secondary DHCP server lease confirmed active in device\",\n \"expected_behavior\": \"\",\n \"required_result\": \"Required\",\n \"result\": \"Compliant\"\n },\n {\n \"name\": \"connection.ipv6_slaac\",\n \"description\": \"Device does not support IPv6 SLAAC\",\n \"expected_behavior\": \"The device under test complies with RFC4862 and forms a valid IPv6 SLAAC address\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Install a network manager that supports IPv6\",\n \"Disable DHCPv6\"\n ]\n },\n {\n \"name\": \"connection.ipv6_ping\",\n \"description\": \"No IPv6 SLAAC address found. Cannot ping\",\n \"expected_behavior\": \"The device responds to the ping as per RFC4443\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Enable ping response to IPv6 ICMP requests in network manager settings\",\n \"Create a firewall exception to allow ICMPv6 via LAN\"\n ]\n },\n {\n \"name\": \"security.tls.v1_2_server\",\n \"description\": \"TLS 1.2 certificate is invalid\",\n \"expected_behavior\": \"TLS 1.2 certificate is issued to the web browser client when accessed\",\n \"required_result\": \"Required if Applicable\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Enable TLS 1.2 support in the web server configuration\",\n \"Disable TLS 1.0 and 1.1\",\n \"Sign the certificate used by the web server\"\n ]\n },\n {\n \"name\": \"security.tls.v1_2_client\",\n \"description\": \"No outbound TLS 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 if Applicable\",\n \"result\": \"Feature Not Detected\"\n },\n {\n \"name\": \"security.tls.v1_3_server\",\n \"description\": \"TLS 1.3 certificate is invalid\",\n \"expected_behavior\": \"TLS 1.3 certificate is issued to the web browser client when accessed\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\"\n },\n {\n \"name\": \"security.tls.v1_3_client\",\n \"description\": \"No outbound TLS connections were found\",\n \"expected_behavior\": \"The packet indicates a TLS connection with at least TLS 1.3\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\"\n },\n {\n \"name\": \"ntp.network.ntp_support\",\n \"description\": \"Device has not sent any NTP requests\",\n \"expected_behavior\": \"The device sends an NTPv4 request to the configured NTP server.\",\n \"required_result\": \"Required\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Set the NTP version to v4 in the NTP client\",\n \"Install an NTP client that supports NTPv4\"\n ]\n },\n {\n \"name\": \"ntp.network.ntp_dhcp\",\n \"description\": \"Device has not sent any NTP requests\",\n \"expected_behavior\": \"Device can accept NTP server address, provided by the DHCP server (DHCP OFFER PACKET)\",\n \"required_result\": \"Roadmap\",\n \"result\": \"Feature Not Detected\"\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\": \"Informational\",\n \"result\": \"Informational\"\n },\n {\n \"name\": \"dns.mdns\",\n \"description\": \"No MDNS traffic detected from the device\",\n \"expected_behavior\": \"Device may send MDNS requests\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\"\n }\n ]\n },\n \"report\": \"http://localhost:8000/report/Teltonika TRB140/2024-09-10T13:19:24\",\n \"export\": \"http://localhost:8000/export/Teltonika TRB140/2024-09-10T13:19:24\"\n }\n]"
},
{
- "name": "Invalid JSON",
+ "name": "No Test Reports (200)",
"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"
- }
+ "method": "GET",
+ "header": [
+ {
+ "key": "Origin",
+ "value": "http://localhost:8000",
+ "type": "text"
}
- },
+ ],
"url": {
- "raw": "localhost:8000/device",
+ "raw": "localhost:8000/reports",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device"
+ "reports"
]
}
},
- "status": "Bad Request",
- "code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"error\": \"Invalid JSON received\"\n}"
+ "body": "[]"
}
]
},
{
- "name": "Edit Device",
+ "name": "Delete Report",
"request": {
- "method": "POST",
+ "method": "DELETE",
"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}",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"timestamp\": \"2023-10-10 16:53:47\"\n}",
"options": {
"raw": {
"language": "json"
@@ -812,26 +1123,27 @@
}
},
"url": {
- "raw": "localhost:8000/device/edit",
+ "raw": "http://localhost:8000/report",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device",
- "edit"
+ "report"
]
- }
+ },
+ "description": "Delete a previous Testrun report"
},
"response": [
{
- "name": "Create Device",
+ "name": "Report Deleted (200)",
"originalRequest": {
- "method": "POST",
+ "method": "DELETE",
"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}",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"timestamp\": \"2024-08-05 13:37:53\"\n}",
"options": {
"raw": {
"language": "json"
@@ -839,31 +1151,39 @@
}
},
"url": {
- "raw": "localhost:8000/device",
+ "raw": "http://localhost:8000/report",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device"
+ "report"
]
}
},
"status": "OK",
"code": 200,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"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}"
+ "body": "{\n \"success\": \"Deleted report\"\n}"
},
{
- "name": "Invalid JSON",
+ "name": "Invalid JSON (400)",
"originalRequest": {
- "method": "POST",
+ "method": "DELETE",
"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}",
+ "raw": "{\n \n}",
"options": {
"raw": {
"language": "json"
@@ -871,171 +1191,2871 @@
}
},
"url": {
- "raw": "localhost:8000/device",
+ "raw": "http://localhost:8000/report",
+ "protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "device"
+ "report"
]
}
},
"status": "Bad Request",
"code": 400,
- "_postman_previewlanguage": null,
- "header": null,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
"cookie": [],
- "body": "{\n \"error\": \"Invalid JSON received\"\n}"
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ },
+ {
+ "name": "Report Not Found (404))",
+ "originalRequest": {
+ "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"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Report not found\"\n}"
}
]
},
{
- "name": "Delete Report",
+ "name": "Get Report",
"request": {
- "method": "DELETE",
+ "method": "GET",
"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",
+ "raw": "http://localhost:8000/report/{device_name}/{timestamp}",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "report"
+ "report",
+ "{device_name}",
+ "{timestamp}"
]
- }
+ },
+ "description": "Get the PDF report for a specific device and timestamp"
},
- "response": []
- },
- {
- "name": "Get Version",
+ "response": [
+ {
+ "name": "Get Report (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/report/Teltonika TRB140/2000-01-01T00:00:00",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "report",
+ "Teltonika TRB140",
+ "2000-01-01T00:00:00"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "text",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/pdf",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "report.pdf"
+ },
+ {
+ "name": "Report Not Found (404)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/report/Teltonika TRB140/2024-05-05 13:37:53",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "report",
+ "Teltonika TRB140",
+ "2024-05-05 13:37:53"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Report could not be found\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Export Report",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"profile\": \"Primary profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/export/{device_name}/{timestamp}",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "export",
+ "{device_name}",
+ "{timestamp}"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
+ ]
+ },
+ "description": "Export the ZIP file of a specific device testing report"
+ },
+ "response": [
+ {
+ "name": "Export without profile (200)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/export/Teltonika TRB140/2024-08-05T13:37:53",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "export",
+ "Teltonika TRB140",
+ "2024-08-05T13:37:53"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "text",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/zip",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "report.zip"
+ },
+ {
+ "name": "Export with profile (200)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/export/Teltonika TRB140/2024-08-05T13:37:53",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "export",
+ "Teltonika TRB140",
+ "2024-08-05T13:37:53"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "Text",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/zip",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "report.zip"
+ },
+ {
+ "name": "Profile Not Found (404)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"profile\": \"Non-existing profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/export/Teltonika TRB140/2024-06-13T11:31:28",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "export",
+ "Teltonika TRB140",
+ "2024-06-13T11:31:28"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A profile with that name could not be found\"\n}"
+ },
+ {
+ "name": "Report Not Found (404)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/export/Teltonika TRB140/2020-08-05T13:37:53",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "export",
+ "Teltonika TRB140",
+ "2020-08-05T13:37:53"
+ ],
+ "query": [
+ {
+ "key": "",
+ "value": null,
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Report not found\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Get Devices",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/devices",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "devices"
+ ]
+ },
+ "description": "Obtain the list of devices from the device repository"
+ },
+ "response": [
+ {
+ "name": "Get Devices (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/devices",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "devices"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"folder_url\": \"local/devices/Teltonika TRB140\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Teltonika\",\n \"model\": \"TRB140\",\n \"test_modules\": {\n \"protocol\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": false\n },\n \"tls\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n }\n },\n \"ip_addr\": null,\n \"firmware\": null,\n \"device_folder\": \"Teltonika TRB140\",\n \"reports\": [\n {\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 \"protocol\": {\n \"enabled\": true\n },\n \"services\": {\n \"enabled\": false\n },\n \"connection\": {\n \"enabled\": false\n },\n \"tls\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": true\n },\n \"dns\": {\n \"enabled\": true\n }\n }\n },\n \"_mac_addr\": \"00:1e:42:35:73:c4\",\n \"_status\": \"Non-Compliant\",\n \"_started\": \"2024-08-05T13:37:53\",\n \"_finished\": \"2024-08-05T13:39:35\",\n \"_total_tests\": 12,\n \"_results\": [\n {\n \"name\": \"protocol.valid_bacnet\",\n \"description\": \"BACnet device could not be discovered\",\n \"expected_behavior\": \"BACnet traffic can be seen on the network and packets are valid and not malformed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\",\n \"recommendations\": []\n },\n {\n \"name\": \"protocol.bacnet.version\",\n \"description\": \"Device did not respond to BACnet discovery\",\n \"expected_behavior\": \"The BACnet client implements an up to date version of BACnet\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\",\n \"recommendations\": []\n },\n {\n \"name\": \"protocol.valid_modbus\",\n \"description\": \"Device did not respond to Modbus connection\",\n \"expected_behavior\": \"Any Modbus functionality works as expected and valid Modbus traffic can be observed\",\n \"required_result\": \"Recommended\",\n \"result\": \"Feature Not Detected\",\n \"recommendations\": []\n },\n {\n \"name\": \"security.tls.v1_2_server\",\n \"description\": \"TLS 1.2 certificate is invalid\",\n \"expected_behavior\": \"TLS 1.2 certificate is issued to the web browser client when accessed\",\n \"required_result\": \"Required if Applicable\",\n \"result\": \"Non-Compliant\",\n \"recommendations\": [\n \"Enable TLS 1.2 support in the web server configuration\",\n \"Disable TLS 1.0 and 1.1\",\n \"Sign the certificate used by the web server\"\n ]\n },\n {\n \"name\": \"security.tls.v1_2_client\",\n \"description\": \"TLS 1.2 client connections valid\",\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 if Applicable\",\n \"result\": \"Compliant\",\n \"recommendations\": []\n },\n {\n \"name\": \"security.tls.v1_3_server\",\n \"description\": \"TLS 1.3 certificate is invalid\",\n \"expected_behavior\": \"TLS 1.3 certificate is issued to the web browser client when accessed\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\",\n \"recommendations\": []\n },\n {\n \"name\": \"security.tls.v1_3_client\",\n \"description\": \"TLS 1.3 client connections valid\",\n \"expected_behavior\": \"The packet indicates a TLS connection with at least TLS 1.3\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\",\n \"recommendations\": []\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 \"recommendations\": [\n \"Set the NTP version to v4 in the NTP client\",\n \"Install an NTP client that supports NTPv4\"\n ]\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\": \"Feature Not Detected\",\n \"recommendations\": []\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 \"recommendations\": []\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\": \"Informational\",\n \"result\": \"Informational\",\n \"recommendations\": []\n },\n {\n \"name\": \"dns.mdns\",\n \"description\": \"No MDNS traffic detected from the device\",\n \"expected_behavior\": \"Device may send MDNS requests\",\n \"required_result\": \"Informational\",\n \"result\": \"Informational\",\n \"recommendations\": []\n }\n ],\n \"_module_reports\": [],\n \"_report_url\": \"http://localhost:8000/report/Teltonika TRB140/2024-08-05T13:37:53\",\n \"_cur_page\": 0,\n \"_version\": \"1.3.1\"\n }\n ],\n \"max_device_reports\": null\n },\n {\n \"folder_url\": \"local/devices/Google First\",\n \"mac_addr\": \"00:0e:12:15:13:c8\",\n \"manufacturer\": \"Google\",\n \"model\": \"First\",\n \"test_modules\": null,\n \"ip_addr\": null,\n \"firmware\": null,\n \"device_folder\": \"Google First\",\n \"reports\": [],\n \"max_device_reports\": null\n },\n {\n \"folder_url\": \"local/devices/Delta O3-DIN-CPU\",\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": true\n },\n \"nmap\": {\n \"enabled\": false\n }\n },\n \"ip_addr\": null,\n \"firmware\": null,\n \"device_folder\": \"Delta O3-DIN-CPU\",\n \"reports\": [],\n \"max_device_reports\": null\n }\n]"
+ },
+ {
+ "name": "No Devices (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/devices",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "devices"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[]"
+ }
+ ]
+ },
+ {
+ "name": "Create Device",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:55\",\n \"manufacturer\": \"TEST\",\n \"model\": \"Stest2\",\n \"type\": \"IoT Gateway\",\n \"technology\": \"Hardware - Access Control\",\n \"test_pack\": \"Device Qualification\",\n \"additional_info\": [\n {\n \"question\": \"What type of device is this?\",\n \"answer\": \"IoT Gateway\"\n },\n {\n \"question\": \"Please select the technology this device falls into\",\n \"answer\": \"Hardware - Access Control\"\n },\n {\n \"question\": \"Does your device process any sensitive information? \",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Can all non-essential services be disabled on your device?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is there a second IP port on the device?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Can the second IP port on your device be disabled?\",\n \"answer\": \"Yes\"\n }\n ],\n \"test_modules\": {\n \"protocol\": { \"enabled\": true },\n \"services\": { \"enabled\": true },\n \"ntp\": { \"enabled\": true },\n \"tls\": { \"enabled\": true },\n \"connection\": { \"enabled\": true },\n \"dns\": { \"enabled\": true }\n }}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ },
+ "description": "Create a new device to test"
+ },
+ "response": [
+ {
+ "name": "Create Device (201)",
+ "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}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Created",
+ "code": 201,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": null\n}"
+ },
+ {
+ "name": "Already Exists (409)",
+ "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}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Conflict",
+ "code": 409,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A device with that MAC address already exists\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "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}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid JSON received\"\n}"
+ },
+ {
+ "name": "Invalid Request (400)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid JSON received\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Delete device",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ },
+ "description": "Create a new device to test"
+ },
+ "response": [
+ {
+ "name": "Delete device (200)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully deleted the device\"\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"non-existing\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Device not found\"\n}"
+ },
+ {
+ "name": "No Mac (400)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ },
+ {
+ "name": "Testrun in Progress (403)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device"
+ ]
+ }
+ },
+ "status": "Forbidden",
+ "code": 403,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Cannot delete this device whilst it is being tested\"\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"
+ ]
+ },
+ "description": "Update the configuration for a device"
+ },
+ "response": [
+ {
+ "name": "Edit Device (200)",
+ "originalRequest": {
+ "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:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\"enabled\": true},\n \"connection\": {\"enabled\": true},\n \"ntp\": {\"enabled\": false},\n \"baseline\": {\"enabled\": true},\n \"nmap\": {\"enabled\": false}\n }\n }\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device/edit",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device",
+ "edit"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\n \"enabled\": true\n },\n \"connection\": {\n \"enabled\": true\n },\n \"ntp\": {\n \"enabled\": false\n },\n \"baseline\": {\n \"enabled\": true\n },\n \"nmap\": {\n \"enabled\": false\n }\n }\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"non-existing\",\n \"device\": {\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\"enabled\": true},\n \"connection\": {\"enabled\": true},\n \"ntp\": {\"enabled\": false},\n \"baseline\": {\"enabled\": true},\n \"nmap\": {\"enabled\": false}\n }\n }\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device/edit",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device",
+ "edit"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A device with that MAC address could not be found\"\n}"
+ },
+ {
+ "name": "Mac Address in Use (409)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"device\": {\n \"mac_addr\": \"00:0e:12:15:13:c8\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\"enabled\": true},\n \"connection\": {\"enabled\": true},\n \"ntp\": {\"enabled\": false},\n \"baseline\": {\"enabled\": true},\n \"nmap\": {\"enabled\": false}\n }\n }\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device/edit",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device",
+ "edit"
+ ]
+ }
+ },
+ "status": "Conflict",
+ "code": 409,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A device with that MAC address already exists\"\n}"
+ },
+ {
+ "name": "Device is Being Tested (403)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"mac_addr\": \"00:1e:42:35:73:c4\",\n \"device\": {\n \"mac_addr\": \"00:0e:12:15:13:c8\",\n \"manufacturer\": \"Delta\",\n \"model\": \"O3-DIN-CPU\",\n \"test_modules\": {\n \"dns\": {\"enabled\": true},\n \"connection\": {\"enabled\": true},\n \"ntp\": {\"enabled\": false},\n \"baseline\": {\"enabled\": true},\n \"nmap\": {\"enabled\": false}\n }\n }\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device/edit",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device",
+ "edit"
+ ]
+ }
+ },
+ "status": "Forbidden",
+ "code": 403,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Cannot edit this device whilst it is being tested\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "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:c4\"\n \"model\": \"O3-DIN-CPU\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "localhost:8000/device/edit",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "device",
+ "edit"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Get Devices Format",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/devices/format",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "devices",
+ "format"
+ ]
+ },
+ "description": "Obtain the list of devices from the device repository"
+ },
+ "response": [
+ {
+ "name": "Get Devices Format (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/devices/format",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "devices",
+ "format"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"step\": 1,\n \"title\": \"Select device type & technology\",\n \"description\": \"Before your device can go through testing, tell us more about your device and its functionality. It is important that we fully understand your device before a thorough assessment can be made.\",\n \"questions\": [\n {\n \"id\": 1,\n \"question\": \"What type of device is this?\",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"text\": \"Building Automation Gateway\",\n \"risk\": \"High\",\n \"id\": 1\n },\n {\n \"text\": \"IoT Gateway\",\n \"risk\": \"High\",\n \"id\": 2\n },\n {\n \"text\": \"Controller - AHU\",\n \"risk\": \"High\",\n \"id\": 3\n },\n {\n \"text\": \"Controller - Boiler\",\n \"risk\": \"High\",\n \"id\": 4\n },\n {\n \"text\": \"Controller - Chiller\",\n \"risk\": \"High\",\n \"id\": 5\n },\n {\n \"text\": \"Controller - FCU\",\n \"risk\": \"Limited\",\n \"id\": 6\n },\n {\n \"text\": \"Controller - Pump\",\n \"risk\": \"Limited\",\n \"id\": 7\n },\n {\n \"text\": \"Controller - CRAC\",\n \"risk\": \"High\",\n \"id\": 8\n },\n {\n \"text\": \"Controller - VAV\",\n \"risk\": \"Limited\",\n \"id\": 9\n },\n {\n \"text\": \"Controller - VRF\",\n \"risk\": \"Limited\",\n \"id\": 10\n },\n {\n \"text\": \"Controller - Multiple\",\n \"risk\": \"High\",\n \"id\": 11\n },\n {\n \"text\": \"Controller - Other\",\n \"risk\": \"High\",\n \"id\": 12\n },\n {\n \"text\": \"Controller - Lighting\",\n \"risk\": \"Limited\",\n \"id\": 13\n },\n {\n \"text\": \"Controller - Blinds/Facades\",\n \"risk\": \"High\",\n \"id\": 14\n },\n {\n \"text\": \"Controller - Lifts/Elevators\",\n \"risk\": \"High\",\n \"id\": 15\n },\n {\n \"text\": \"Controller - UPS\",\n \"risk\": \"High\",\n \"id\": 16\n },\n {\n \"text\": \"Sensor - Air Quality\",\n \"risk\": \"Limited\",\n \"id\": 17\n },\n {\n \"text\": \"Sensor - Vibration\",\n \"risk\": \"Limited\",\n \"id\": 18\n },\n {\n \"text\": \"Sensor - Humidity\",\n \"risk\": \"Limited\",\n \"id\": 19\n },\n {\n \"text\": \"Sensor - Water\",\n \"risk\": \"Limited\",\n \"id\": 20\n },\n {\n \"text\": \"Sensor - Occupancy\",\n \"risk\": \"High\",\n \"id\": 21\n },\n {\n \"text\": \"Sensor - Volume\",\n \"risk\": \"Limited\",\n \"id\": 22\n },\n {\n \"text\": \"Sensor - Weight\",\n \"risk\": \"Limited\",\n \"id\": 23\n },\n {\n \"text\": \"Sensor - Weather\",\n \"risk\": \"Limited\",\n \"id\": 24\n },\n {\n \"text\": \"Sensor - Steam\",\n \"risk\": \"High\",\n \"id\": 25\n },\n {\n \"text\": \"Sensor - Air Flow\",\n \"risk\": \"Limited\",\n \"id\": 26\n },\n {\n \"text\": \"Sensor - Lighting\",\n \"risk\": \"Limited\",\n \"id\": 27\n },\n {\n \"text\": \"Sensor - Other\",\n \"risk\": \"High\",\n \"id\": 28\n },\n {\n \"text\": \"Sensor - Air Quality\",\n \"risk\": \"Limited\",\n \"id\": 29\n },\n {\n \"text\": \"Monitoring - Fire System\",\n \"risk\": \"Limited\",\n \"id\": 30\n },\n {\n \"text\": \"Monitoring - Emergency Lighting\",\n \"risk\": \"Limited\",\n \"id\": 31\n },\n {\n \"text\": \"Monitoring - Other\",\n \"risk\": \"High\",\n \"id\": 32\n },\n {\n \"text\": \"Monitoring - UPS\",\n \"risk\": \"Limited\",\n \"id\": 33\n },\n {\n \"text\": \"Meter - Water\",\n \"risk\": \"Limited\",\n \"id\": 34\n },\n {\n \"text\": \"Meter - Gas\",\n \"risk\": \"Limited\",\n \"id\": 35\n },\n {\n \"text\": \"Meter - Electricity\",\n \"risk\": \"Limited\",\n \"id\": 36\n },\n {\n \"text\": \"Meter - Other\",\n \"risk\": \"High\",\n \"id\": 37\n },\n {\n \"text\": \"Other\",\n \"risk\": \"High\",\n \"id\": 38\n },\n {\n \"text\": \"Data - Storage\",\n \"risk\": \"High\",\n \"id\": 39\n },\n {\n \"text\": \"Data - Processing\",\n \"risk\": \"High\",\n \"id\": 40\n },\n {\n \"text\": \"Tablet\",\n \"risk\": \"High\",\n \"id\": 41\n }\n ]\n },\n {\n \"id\": 2,\n \"question\": \"Please select the technology this device falls into\",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"id\": 1,\n \"text\": \"Hardware - Access Control\"\n },\n {\n \"id\": 2,\n \"text\": \"Hardware - Air quality\"\n },\n {\n \"id\": 3,\n \"text\": \"Hardware - Asset location tracking\"\n },\n {\n \"id\": 4,\n \"text\": \"Hardware - Audio Visual\"\n },\n {\n \"id\": 5,\n \"text\": \"Hardware - Blinds/Facade\"\n },\n {\n \"id\": 6,\n \"text\": \"Hardware - Cameras\"\n },\n {\n \"id\": 7,\n \"text\": \"Hardware - Catering\"\n },\n {\n \"id\": 8,\n \"text\": \"Hardware - Data Ingestion/Managment\"\n },\n {\n \"id\": 9,\n \"text\": \"Hardware - EV Charging\"\n },\n {\n \"id\": 10,\n \"text\": \"Hardware - Fitness\"\n },\n {\n \"id\": 11,\n \"text\": \"Hardware - HVAC\"\n },\n {\n \"id\": 12,\n \"text\": \"Hardware - Irrigation\"\n },\n {\n \"id\": 13,\n \"text\": \"Hardware - Leak Detection\"\n },\n {\n \"id\": 14,\n \"text\": \"Hardware - Lifts/Elevators\"\n },\n {\n \"id\": 15,\n \"text\": \"Hardware - Lighting\"\n },\n {\n \"id\": 16,\n \"text\": \"Hardware - Metering\"\n },\n {\n \"id\": 17,\n \"text\": \"Hardware - Monitoring\"\n },\n {\n \"id\": 18,\n \"text\": \"Hardware - Occupancy\"\n },\n {\n \"id\": 19,\n \"text\": \"Hardware - System Integration\"\n },\n {\n \"id\": 20,\n \"text\": \"Hardware - Time Management\"\n },\n {\n \"id\": 21,\n \"text\": \"Hardware - UPS\"\n },\n {\n \"id\": 22,\n \"text\": \"Hardware - Waste Management\"\n },\n {\n \"id\": 23,\n \"text\": \"Building Automation\"\n },\n {\n \"id\": 24,\n \"text\": \"Other\"\n }\n ]\n },\n {\n \"id\": 3,\n \"question\": \"Does your device process any sensitive information? \",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"id\": 1,\n \"text\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"id\": 2,\n \"text\": \"No\",\n \"risk\": \"High\"\n },\n {\n \"id\": 3,\n \"text\": \"I don't know\",\n \"risk\": \"High\"\n }\n ]\n }\n ]\n },\n {\n \"step\": 2,\n \"title\": \"Tell us more about your device\",\n \"description\": \"Before your device can go through testing, tell us more about your device and its functionality. It is important that we fully understand your device before a thorough assessment can be made.\",\n \"questions\": [\n {\n \"id\": 1,\n \"question\": \"Can all non-essential services be disabled on your device?\",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"text\": \"Yes\",\n \"id\": 1\n },\n {\n \"text\": \"No\",\n \"id\": 2\n }\n ]\n },\n {\n \"id\": 2,\n \"question\": \"Is there a second IP port on the device?\",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"text\": \"Yes\",\n \"id\": 1\n },\n {\n \"text\": \"No\",\n \"id\": 2\n }\n ]\n },\n {\n \"id\": 3,\n \"question\": \"Can the second IP port on your device be disabled?\",\n \"validation\": {\n \"required\": true\n },\n \"type\": \"select\",\n \"options\": [\n {\n \"text\": \"Yes\",\n \"id\": 1\n },\n {\n \"text\": \"No\",\n \"id\": 2\n },\n {\n \"text\": \"N/A\",\n \"id\": 3\n }\n ]\n }\n ]\n }\n]"
+ }
+ ]
+ },
+ {
+ "name": "Get System Modules",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/modules",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "modules"
+ ]
+ },
+ "description": "Get a list of all root CA certificates that have been loaded into Testrun"
+ },
+ "response": [
+ {
+ "name": "Get System Modules (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/modules",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "modules"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[\n \"Protocol\",\n \"Services\",\n \"Connection\",\n \"TLS\",\n \"NTP\",\n \"DNS\"\n]"
+ }
+ ]
+ },
+ {
+ "name": "Get System Testpacks",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/testpacks",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "testpacks"
+ ]
+ },
+ "description": "Obtain a list of applicable and available network adapters for use in testing"
+ },
+ "response": [
+ {
+ "name": "System Testpacks (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "localhost:8000/system/testpacks",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "testpacks"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[\n \"Pilot Assessment\",\n \"Device Qualification\"\n]"
+ }
+ ]
+ },
+ {
+ "name": "List Certificates",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ },
+ "description": "Get a list of all root CA certificates that have been loaded into Testrun"
+ },
+ "response": [
+ {
+ "name": "List Certificates (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"name\": \"GTS Root R1\",\n \"status\": \"Valid\",\n \"organisation\": \"Google Trust Services LLC\",\n \"expires\": \"2036-06-22T00:00:00+00:00\",\n \"filename\": \"crt.pem\"\n },\n {\n \"name\": \"WR2\",\n \"status\": \"Valid\",\n \"organisation\": \"Google Trust Services LLC\",\n \"expires\": \"2029-02-20T14:00:00+00:00\",\n \"filename\": \"WR2.pem\"\n }\n]"
+ },
+ {
+ "name": "No Certificates (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "[]"
+ }
+ ]
+ },
+ {
+ "name": "Upload Certificate",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "NH1mNzqEQ/1c3.pem"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ },
+ "description": "Upload a new root CA certificate into Testrun"
+ },
+ "response": [
+ {
+ "name": "Upload Certificate (201)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "NH1mNzqEQ/1c3.pem"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Created",
+ "code": 201,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"name\": \"GTS Root R1\",\n \"status\": \"Valid\",\n \"organisation\": \"Google Trust Services LLC\",\n \"expires\": \"2036-06-22T00:00:00+00:00\",\n \"filename\": \"crt.pem\"\n}"
+ },
+ {
+ "name": "Duplicate (409)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "NH1mNzqEQ/1c3.pem"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Conflict",
+ "code": 409,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A certificate with that file name already exists.\"\n}"
+ },
+ {
+ "name": "Invalid Cert (400)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "postman-cloud:///1ef5fb1a-6342-44e0-b35f-700d496e0cc5"
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Failed to upload certificate. Is it in the correct format?\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Delete Certificate",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"GTS CA 1C3\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ },
+ "description": "Delete a root CA certificate"
+ },
+ "response": [
+ {
+ "name": "Delete Certificate (200)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"GTS Root R1\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully deleted the certificate\"\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing name\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A certificate with that name could not be found\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Received a bad request\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Get Profile Format",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles/format",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles",
+ "format"
+ ]
+ },
+ "description": "Obtain the current format of the risk assessment questionnaire"
+ },
+ "response": [
+ {
+ "name": "Get Profile Format (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles/format",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles",
+ "format"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"question\": \"How will this device be used at Google?\",\n \"description\": \"Desribe your use case. Add links to user journey diagrams and TDD if available.\",\n \"type\": \"text-long\",\n \"validation\": {\n \"max\": \"512\",\n \"required\": true\n }\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"description\": \"A manufacturer or supplier is considered third party in this case\",\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 \"category\": \"Data Transmission\",\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/sensitive business data, Intellectual Property and Trade Secrets, Critical Infrastructure and Identity Assets to a domain 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 does not encrypt data during transmission\",\n \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"category\": \"Data Transmission\",\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 \"category\": \"Remote Operation\",\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 not required for remote access\",\n \"The management interface is accessible from the public internet\",\n \"Static credentials are used for administration\",\n \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"category\": \"Operating Environment\",\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 \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Comments\",\n \"description\": \"Anything else to share?\",\n \"type\": \"text-long\",\n \"validation\": {\n \"max\": \"512\"\n }\n }\n]"
+ }
+ ]
+ },
+ {
+ "name": "List Profiles",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ },
+ "description": "Get a list of risk assessment profiles saved by the user"
+ },
+ "response": [
+ {
+ "name": "List Profiles (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]"
+ },
+ {
+ "name": "No Profiles (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
+ ],
+ "cookie": [],
+ "body": "[\n \n]"
+ }
+ ]
+ },
+ {
+ "name": "Export Profile",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profile/{profile_name}",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "{profile_name}"
+ ]
+ },
+ "description": "Get the PDF report for a specific device and timestamp"
+ },
+ "response": [
+ {
+ "name": "Get Profile No Device (200)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profile/Test",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "Test"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "text",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/pdf",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "profile.pdf"
+ },
+ {
+ "name": "Get Profile with Device (200))",
+ "originalRequest": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\"mac_addr\": \"00:1e:42:35:73:c4\"}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profile/Test",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "Test"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "text",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/pdf",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "profile.pdf"
+ },
+ {
+ "name": "Profile Not Found (404)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profile/NonExistingProfile",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "NonExistingProfile"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Profile could not be found\"\n}"
+ },
+ {
+ "name": "Device Not Found (404)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\"mac_addr\": \"non_existing_mac_addr\"}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profile/Test",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "Test"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A device with that mac address could not be found\"\n}"
+ },
+ {
+ "name": "Internal Server Error (500) Copy",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profile/Test",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profile",
+ "Test"
+ ]
+ }
+ },
+ "status": "Internal Server Error",
+ "code": 500,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Error retrieving the profile PDF\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Update Profile",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"Testing\",\n \"status\": \"Valid\",\n \"questions\": [\n {\n \"question\": \"What type of device is this?\",\n \"answer\": \"IoT Gateway\"\n },\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"asdasd\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"N/A\"\n },\n {\n \"question\": \"Are any of the following statements true about your device?\",\n \"answer\": [\n 3\n ]\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 5\n ]\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 5\n ]\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 6\n ]\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ },
+ "description": "Create or update a risk assessment questionnaire response"
+ },
+ "response": [
+ {
+ "name": "Update Profile (200)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "\n{\n \"name\": \"New Profile\",\n \"rename\": \"Updated New Profile\",\n \"version\": \"2.0.1\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully updated that profile\"\n}"
+ },
+ {
+ "name": "Create Profile (201)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "\n{\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}\n",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Created",
+ "code": 201,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully created a new profile\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ },
+ {
+ "name": "Not Implemented (501)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Not Implemented",
+ "code": 501,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Risk profiles are not available right now\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "Delete Profile",
+ "request": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"New Profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ },
+ "description": "Delete an existing risk assessment questionaire response"
+ },
+ "response": [
+ {
+ "name": "Delete Profile (200)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"New Profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully deleted that profile\"\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A profile with that name could not be found\"\n}"
+ },
+ {
+ "name": "Internal Serves Error (500)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Internal Server Error",
+ "code": 500,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"An error occurred whilst deleting that profile\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ }
+ ]
+ },
+ {
+ "name": "http://localhost:8000/profile/Test",
"request": {
- "method": "GET",
- "header": [],
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\"mac_addr\": \"non_existing_mac_addr\"}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
"url": {
- "raw": "http://localhost:8000/system/version",
+ "raw": "http://localhost:8000/profile/Test",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "system",
- "version"
+ "profile",
+ "Test"
]
- }
+ },
+ "description": "Delete a root CA certificate"
},
- "response": []
+ "response": [
+ {
+ "name": "Delete Certificate (200)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"GTS Root R1\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully deleted the certificate\"\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing name\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A certificate with that name could not be found\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/system/config/certs",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "system",
+ "config",
+ "certs"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Received a bad request\"\n}"
+ }
+ ]
},
{
- "name": "Get Report",
+ "name": "Get Profile Format",
"request": {
"method": "GET",
"header": [],
"url": {
- "raw": "http://localhost:8000/report/{device_name}/{timestamp}",
+ "raw": "http://localhost:8000/profiles/format",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "report",
- "{device_name}",
- "{timestamp}"
+ "profiles",
+ "format"
]
- }
+ },
+ "description": "Obtain the current format of the risk assessment questionnaire"
},
- "response": []
+ "response": [
+ {
+ "name": "Get Profile Format (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles/format",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles",
+ "format"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
+ ],
+ "cookie": [],
+ "body": "[\n {\n \"question\": \"How will this device be used at Google?\",\n \"description\": \"Desribe your use case. Add links to user journey diagrams and TDD if available.\",\n \"type\": \"text-long\",\n \"validation\": {\n \"max\": \"512\",\n \"required\": true\n }\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"description\": \"A manufacturer or supplier is considered third party in this case\",\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 \"category\": \"Data Transmission\",\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/sensitive business data, Intellectual Property and Trade Secrets, Critical Infrastructure and Identity Assets to a domain 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 does not encrypt data during transmission\",\n \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"category\": \"Data Transmission\",\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 \"category\": \"Remote Operation\",\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 not required for remote access\",\n \"The management interface is accessible from the public internet\",\n \"Static credentials are used for administration\",\n \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"category\": \"Operating Environment\",\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 \"None of the above\"\n ],\n \"validation\": {\n \"required\": true\n }\n },\n {\n \"question\": \"Comments\",\n \"description\": \"Anything else to share?\",\n \"type\": \"text-long\",\n \"validation\": {\n \"max\": \"512\"\n }\n }\n]"
+ }
+ ]
},
{
- "name": "Get Export",
+ "name": "List Profiles",
"request": {
"method": "GET",
"header": [],
"url": {
- "raw": "http://localhost:8000/export/{device_name}/{timestamp}",
+ "raw": "http://localhost:8000/profiles",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "export",
- "{device_name}",
- "{timestamp}"
+ "profiles"
]
- }
+ },
+ "description": "Get a list of risk assessment profiles saved by the user"
},
- "response": []
- },
- {
- "name": "List Certificates",
- "request": {
- "method": "GET",
- "header": [],
- "url": {
- "raw": "http://localhost:8000/system/config/certs/list",
- "protocol": "http",
- "host": [
- "localhost"
+ "response": [
+ {
+ "name": "List Profiles (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
],
- "port": "8000",
- "path": [
- "system",
- "config",
- "certs",
- "list"
- ]
+ "cookie": [],
+ "body": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]"
+ },
+ {
+ "name": "No Profiles (200)",
+ "originalRequest": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": ""
+ }
+ ],
+ "cookie": [],
+ "body": "[\n \n]"
}
- },
- "response": []
+ ]
},
{
- "name": "Upload Certificate",
+ "name": "Update Profile",
"request": {
"method": "POST",
"header": [],
"body": {
- "mode": "file",
- "file": {}
+ "mode": "raw",
+ "raw": "{\n \"name\": \"Testing\",\n \"status\": \"Valid\",\n \"questions\": [\n {\n \"question\": \"What type of device is this?\",\n \"answer\": \"IoT Gateway\"\n },\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"asdasd\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"N/A\"\n },\n {\n \"question\": \"Are any of the following statements true about your device?\",\n \"answer\": [\n 3\n ]\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 5\n ]\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 5\n ]\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 6\n ]\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
},
"url": {
- "raw": "http://localhost:8000/system/config/certs/upload",
+ "raw": "http://localhost:8000/profiles",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "system",
- "config",
- "certs",
- "upload"
+ "profiles"
]
- }
+ },
+ "description": "Create or update a risk assessment questionnaire response"
},
- "response": []
+ "response": [
+ {
+ "name": "Update Profile (200)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully updated that profile\"\n}"
+ },
+ {
+ "name": "Create Profile (201)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Created",
+ "code": 201,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully created a new profile\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ },
+ {
+ "name": "Not Implemented (501)",
+ "originalRequest": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "[\n {\n \"name\": \"New Profile\",\n \"version\": \"2.0\",\n \"created\": \"2024-10-08\",\n \"status\": \"Valid\",\n \"risk\": \"High\",\n \"questions\": [\n {\n \"question\": \"How will this device be used at Google?\",\n \"answer\": \"Yes\"\n },\n {\n \"question\": \"Is this device going to be managed by Google or a third party?\",\n \"answer\": \"Google\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Will the third-party device administrator be able to grant access to authorized Google personnel upon request?\",\n \"answer\": \"No\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Which of the following statements are true about this device?\",\n \"answer\": [\n 0,\n 1,\n 2\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Does the network protocol assure server-to-client identity verification?\",\n \"answer\": \"Yes\",\n \"risk\": \"Limited\"\n },\n {\n \"question\": \"Click the statements that best describe the characteristics of this device.\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Are any of the following statements true about this device?\",\n \"answer\": [\n 0\n ],\n \"risk\": \"High\"\n },\n {\n \"question\": \"Comments\",\n \"answer\": \"\"\n }\n ]\n }\n]",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Not Implemented",
+ "code": 501,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Risk profiles are not available right now\"\n}"
+ }
+ ]
},
{
- "name": "Delete Certificate",
+ "name": "Delete Profile",
"request": {
"method": "DELETE",
"header": [],
"body": {
"mode": "raw",
- "raw": "{\n \"name\": \"iot.bms.google.com\"\n}",
+ "raw": "{\n \"name\": \"New Profile\"\n}",
"options": {
"raw": {
"language": "json"
@@ -1043,21 +4063,184 @@
}
},
"url": {
- "raw": "http://localhost:8000/system/config/certs/delete",
+ "raw": "http://localhost:8000/profiles",
"protocol": "http",
"host": [
"localhost"
],
"port": "8000",
"path": [
- "system",
- "config",
- "certs",
- "delete"
+ "profiles"
]
- }
+ },
+ "description": "Delete an existing risk assessment questionaire response"
},
- "response": []
+ "response": [
+ {
+ "name": "Delete Profile (200)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"New Profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "OK",
+ "code": 200,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"success\": \"Successfully deleted that profile\"\n}"
+ },
+ {
+ "name": "Not Found (404)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Not Found",
+ "code": 404,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"A profile with that name could not be found\"\n}"
+ },
+ {
+ "name": "Internal Serves Error (500)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"name\": \"non-existing profile\"\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Internal Server Error",
+ "code": 500,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"An error occurred whilst deleting that profile\"\n}"
+ },
+ {
+ "name": "Invalid JSON (400)",
+ "originalRequest": {
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://localhost:8000/profiles",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8000",
+ "path": [
+ "profiles"
+ ]
+ }
+ },
+ "status": "Bad Request",
+ "code": 400,
+ "_postman_previewlanguage": "json",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "name": "Content-Type",
+ "description": "",
+ "type": "text"
+ }
+ ],
+ "cookie": [],
+ "body": "{\n \"error\": \"Invalid request received\"\n}"
+ }
+ ]
}
]
}
\ No newline at end of file
diff --git a/docs/get_started.md b/docs/get_started.md
index f9a2aa2f8..41a1d13f8 100644
--- a/docs/get_started.md
+++ b/docs/get_started.md
@@ -1,123 +1,144 @@
+# Get started
-## Getting Started
+This page covers the following topics:
-It is recommended that you run Testrun on a standalone machine running a fresh install of Ubuntu 20.04, 22.04 or 24.04 LTS (laptop or desktop).
+- [Get started](#get-started)
+- [Prerequisites](#prerequisites)
+ - [Hardware](#hardware)
+ - [Software](#software)
+ - [Device](#device)
+- [Installation](#installation)
+- [Testing](#testing)
+ - [Start Testrun](#start-testrun)
+ - [Test your device](#test-your-device)
+- [Additional Configuration Options](/docs/additional_config.md)
+- [Troubleshooting](#troubleshooting)
+- [Review the report](#review-the-report)
+- [Uninstall](#uninstall)
-## Prerequisites
+# Prerequisites
-### Hardware
+We recommend that you run Testrun on a stand-alone machine that has a fresh install of Ubuntu 22.04 or 24.04 LTS (laptop or desktop).
-Before starting with Testrun, ensure you have the following hardware:
+## Hardware
-- PC running Ubuntu LTS (laptop or desktop)
-- 2x USB Ethernet adapter (one may be a built-in Ethernet port)
-- Internet connection
+Before you start, ensure you have the following hardware:
-
+- PC running Ubuntu 22.04 or 24.04 LTS (laptop or desktop)
+- 2x ethernet ports (USB ethernet adapters work too)
+- Internet connection
-**NOTE: Running in a virtual machine? Checkout the virtual machine documentation [here](/docs/virtual_machine.md).**
+
-### Software
+Note: If you're using Testrun in a virtual machine, follow the steps on the [Virtual machine page](/docs/virtual_machine.md).
-Ensure the following software is installed on your Ubuntu LTS PC:
-- Docker - installation guide: [https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository)
-- System dependencies (These will be installed automatically when installing Testrun if not already installed):
- - Python3-dev
- - Python3-venv
- - Openvswitch Common
- - Openvswitch Switch
- - Build Essential
- - Net Tools
- - Ethtool
+## Software
-### Device
-Any device with an ethernet connection, and support for IPv4 DHCP can be tested.
+Install Docker on your Ubuntu LTS PC using [the installation guide](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository). The following system dependencies install automatically when you install Testrun:
-However, to achieve a compliant test outcome, your device must be configured correctly and implement the required security features. These standards are outlined in the [Application Security Requirements for IoT Devices](https://partner-security.withgoogle.com/docs/iot_requirements). but further detail is available in [documentation for each test module](/docs/test/modules.md).
+- Python3-dev
+- Python3-venv
+- Openvswitch Common
+- Openvswitch Switch
+- Build Essential
+- Net Tools
+- Ethtool
-## Installation
+## Device
-1. Download the latest version of the Testrun installer from the [releases page](https://github.com/google/testrun/releases)
+You can test any device with an Ethernet connection and support for IPv4 DHCP. However, to achieve a compliant test outcome, you must configure your device correctly and implement the required security features. The [Application Security Requirements for IoT Devices](https://partner-security.withgoogle.com/docs/iot_requirements) outlines these standards. Additional details are available on the [Test modules page](/docs/test/modules.md).
-2. Open a terminal and navigate to location of the Testrun installer (most likely your downloads folder)
+# Installation
-3. Install the package using ``sudo apt install ./testrun*.deb``
+Follow these steps to install Testrun:
- - Testrun will be installed under the /usr/local/testrun directory.
- - Testing data will be available in the ``local/devices/{device}/reports`` folders
+1. Download the latest version of the Testrun installer from the [Releases page](https://github.com/google/testrun/releases).
+2. Open a terminal and navigate to the location of the Testrun installer (most likely your Downloads folder).
+3. Install the package using `sudo apt install ./testrun*.deb`
- **NOTE: Local CA certificates should be uploaded within Testrun to run TLS server testing**
+Testrun installs under the `/usr/local/testrun` directory. Testing data is available in the `local/devices/{device}/reports` folders.
- 
+
-## Start Testrun
-
-1. Attach network interfaces:
- - Connect one USB Ethernet adapter to the internet source (e.g., router or switch) using an ethernet cable.
- - Connect the other USB Ethernet adapter directly to the IoT device you want to test using an ethernet cable.
-
- Some things to remember:
- - Both adapters should be disabled in the host system (IPv4, IPv6 and general). You can do this by going to Settings > Network
- - The device under test should be powered off until prompted
- - Struggling to identify the correct interfaces? See [this guide](network/identify_interfaces.md).
-
-2. Start Testrun.
-
-Start Testrun with the command `sudo testrun`
-
- - To run Testrun in network-only mode (without running any tests), use the `--net-only` option.
-
- - To run Testrun with just one interface (connected to the device), use the ``--single-intf`` option.
-
-## Test Your Device
-
-1. Once Testrun has started, open your browser to http://localhost:8080.
-
-2. Configure your network interfaces under the settings menu - located in the top right corner of the application. Settings can be changed at any time.
-
- 
-
-3. Navigate to the device repository icon to add a new device for testing.
-
- 
+# Testing
-4. Click the button 'Add Device'.
-
-5. Enter the MAC address, manufacturer name and model number.
-
-6. Select the test modules you wish to enable for this device (Hint: All are required for qualification purposes) and click save.
-
-7. Navigate to the Testrun progress icon and click the button 'Start New Testrun'.
-
- 
-
-8. Select the device you would like to test.
-
-9. Enter the version number of the firmware running on the device.
-
-10. Click 'Start Testrun'
-
- - During testing, if you would like to stop Testrun, click 'Stop' next to the test name.
+## Start Testrun
-11. Once the notification 'Waiting for Device' appears, power on the device under test.
+Follow these steps to start Testrun:
+1. Attach the network interfaces.
+ - Connect one USB Ethernet adapter to the internet source (e.g., router or switch) using an Ethernet cable.
+ - Connect the other USB Ethernet adapter directly to the IoT device you want to test using an Ethernet cable.
+
+ Notes:
+
+ - Disable both adapters in the host system (IPv4, IPv6, and general) by opening **Settings**, then **Network**.
+ - Keep the DUT powered off until prompted. Otherwise, Testrun will not be able to fully capture the device behavior during startup - resulting in inaccurate test results.
+
+1. Start Testrun with the command `sudo testrun`
+ - To run Testrun in network-only mode (without running any tests), use the `--net-only` option.
+ - To run Testrun with just one interface (connected to the device), use the `--single-intf` option.
+
+ **Note**: Tests that require an internet connection (e.g TLS client, DNS) will produce a non-compliant result.
+
+## Test your device
+
+Follow these steps to test your IoT device:
+
+1. Open Testrun by navigating to [http://localhost:8080](http://localhost:8080/) in your browser.
+2. Select the **Settings** menu in the top-right corner. It will open the **General** tab by default. Then select your network interfaces. You can change the settings at any time.
+ 
+3. Select the **Certificates** tab (Settings menu), then upload your local CA certificates for TLS server testing.
+ 
+4. Select the **Device Repository** icon on the left panel to add a new device for testing.
+ 
+
+ Or
+
+ Click the **Actions** button to add a new device for testing.
+ 
+5. Select the **Add Device** button on the Device repository.
+ 
+6. Enter the MAC address, manufacturer name, and model number.
+7. Select Qualification or Pilot program.
+8. Select the test modules you want to enable for this device.
+Note: For qualification purposes, you must select all.
+9. Answer a few questions about your device.
+10. Select **Save**.
+11. Select the Testrun progress icon, then select the **Testing** button.
+
+ Or
+
+ Click the **Actions** button, then select the **Start Testing** button.
+ 
+
+12. Select the device you want to test.
+13. Enter the version number of the firmware running on the device.
+14. Select **Start Testrun**.
+ - If you need to stop Testrun during testing, select **Stop** next to the test name.
+15. Once the Waiting for Device notification appears, power on the device under test.
+ 
+16. While testing is in progress, you could complete a Risk Assessment. To do so, go to the **Risk assessment** tab.
+ 
+17. A report appears under the Reports icon once the test sequence is complete.
+ 
-12. On completion of the test sequence, a report will appear under the history icon.
+# Troubleshooting
- 
+If you encounter any issues, try the following:
-# Troubleshooting
+- Ensure that your computer meets all hardware and software prerequisites.
+- Verify that the network interfaces are connected correctly.
+- Check the configuration settings.
+- Refer to the [Testrun documentation](/docs).
-If you encounter any issues or need assistance, consider the following:
+If you still need assistance, ask a question on the [Issues page](https://github.com/google/testrun/issues).
-- Ensure that all hardware and software prerequisites are met.
-- Verify that the network interfaces are connected correctly.
-- Check the configuration settings.
-- Refer to the Testrun documentation or ask for assistance in the issues page: https://github.com/google/testrun/issues
+# Review the report
-# Reviewing
-Once you have completed a test attempt, you may want to review the test report provided by Testrun. For more information about what Testrun looks for when testing, and what the output means, take a look at the testing documentation: [Testing](/docs/test/README.md).
+Once you complete a test attempt, you can review the test report provided by Testrun. For more information on Testrun requirements and outputs, refer to the [Testing documentation](/docs/test/README.md).
# Uninstall
-To uninstall Testrun, use the built-in dpkg uninstall command to remove Testrun correctly. For Testrun, this would be: ```sudo apt-get remove testrun```.
+
+To uninstall Testrun correctly, use the built-in dpkg uninstall command: `sudo apt-get remove testrun`
diff --git a/docs/network/README.md b/docs/network/README.md
index b5536c30c..0efb5e3ff 100644
--- a/docs/network/README.md
+++ b/docs/network/README.md
@@ -1,45 +1,41 @@
+# Network
-## Network Overview
+This page provides an overview of Testrun's network services. Visit these pages for additional information:
-## Table of Contents
-1) Network Overview (this page)
-2) [How to identify network interfaces](identify_interfaces.md)
-3) [Addresses](addresses.md)
-4) [Add a new network service](add_new_service.md)
+- [Network addresses](/docs/network/addresses.md)
+- [Add a new network service](/docs/network/add_new_service.md)
-Testrun provides several built-in network services that can be utilized for testing purposes. These services are already available and can be used without any additional configuration.
+Testrun provides several built-in network services you can use for testing purposes. These services don't require any additional configuration. Below is a list and brief description of the network services provided.
-The following network services are provided:
-
-### Internet Connectivity (Gateway Service)
+# Internet connectivity (gateway service)
The gateway service provides internet connectivity to the test network. It allows devices in the network to access external resources and communicate with the internet.
-### DHCPv4 Service
+# DHCPv4 service
The DHCPv4 service provides Dynamic Host Configuration Protocol (DHCP) functionality for IPv4 addressing. It includes the following components:
-- Primary DHCP Server: A primary DHCP server is available to assign IPv4 addresses to DHCP clients in the network.
-- Secondary DHCP Server (Failover Configuration): A secondary DHCP server operates in failover configuration with the primary server to provide high availability and redundancy.
+- Primary DHCP server: Assigns IPv4 addresses to DHCP clients in the network.
+- Secondary DHCP server (failover configuration): Operates in failover configuration with the primary server to provide high availability and redundancy.
-#### Configuration
+## Configuration
-The configuration of the DHCPv4 service can be modified using the provided GRPC (gRPC Remote Procedure Call) service.
+You can modify the configuration of the DHCPv4 service using the provided Remote Procedure Call (GRPC) service.
-### IPv6 SLAAC Addressing
+# IPv6 SLAAC addressing
-The primary DHCP server also provides IPv6 Stateless Address Autoconfiguration (SLAAC) addressing for devices in the network. IPv6 addresses are automatically assigned to devices using SLAAC where test devices support it.
+The primary DHCP server provides IPv6 Stateless Address Autoconfiguration (SLAAC) addressing for devices on the network. It automatically assigns IPv6 addresses to devices using SLAAC where test devices support it.
-### NTP Service
+# NTP service
-The Network Time Protocol (NTP) service provides time synchronization for devices in the network. It ensures that all devices have accurate and synchronized time information.
+The Network Time Protocol (NTP) service provides time synchronization for devices on the network. It ensures that all devices have accurate and synchronized time information.
-### DNS Service
+# DNS service
-The DNS (Domain Name System) service resolves domain names to their corresponding IP addresses. It allows devices in the network to access external resources using domain names.
+The Domain Name System (DNS) service resolves domain names to their corresponding IP addresses. It allows devices on the network to access external resources using domain names.
-### 802.1x Authentication (Radius Module)
+# 802.1x authentication (radius module)
-The radius module provides 802.1x authentication for devices in the network. It ensures secure and authenticated access to the network. The issuing CA (Certificate Authority) certificate can be specified by the user if required.
\ No newline at end of file
+The radius module provides 802.1x authentication for devices on the network. It ensures secure and authenticated access to the network. The user can specify the issuing Certificate Authority (CA) certificate if required.
\ No newline at end of file
diff --git a/docs/network/add_new_service.md b/docs/network/add_new_service.md
index 7a07e43be..e6b01e102 100644
--- a/docs/network/add_new_service.md
+++ b/docs/network/add_new_service.md
@@ -1,21 +1,44 @@
-## Adding a New Network Service
+# Add a new network service
-The Testrun framework allows users to add their own network services with ease. A template network service can be used to get started quickly, this can be found at [modules/network/template](../../modules/network/template). Otherwise, see below for details of the requirements for new network services.
+The Testrun framework allows you to easily add your own network services. You can use the template network service at [modules/network/template](/modules/network/template). To add a new network service, follow these steps:
-To add a new network service to Testrun, follow the procedure below:
+1. Create a folder under `modules/network/` with the name of the network service in lowercase using only alphanumeric characters and hyphens (-).
+1. Include the following items in the created folder:
+ - `{module}.Dockerfile`: Dockerfile builds the network service image. Replace `{module}` with the name of the module.
+ - `conf/`: Folder containing the module configuration files.
+ - `bin/`: Folder containing the start-up script for the network service.
+ - Place any additional application code in its own folder.
-1. Create a folder under `modules/network/` with the name of the network service in lowercase, using only alphanumeric characters and hyphens (`-`).
-2. Inside the created folder, include the following files and folders:
- - `{module}.Dockerfile`: Dockerfile for building the network service image. Replace `{module}` with the name of the module.
- - `conf/`: Folder containing the module configuration files.
- - `bin/`: Folder containing the startup script for the network service.
- - Any additional application code can be placed in its own folder.
+Here are some examples:
-### Example `module_config.json`
+## {module}.Dockerfile
-```json
+```
+# Image name: test-run/{module}
+FROM test-run/base:latest
+
+ARG MODULE_NAME={module}
+ARG MODULE_DIR=modules/network/$MODULE_NAME
+
+# Install network service dependencies
+# ...
+
+# Copy over all configuration files
+COPY $MODULE_DIR/conf /testrun/conf
+
+# Copy over all binary files
+COPY $MODULE_DIR/bin /testrun/bin
+
+# Copy over all python files
+COPY $MODULE_DIR/python /testrun/python
+
+# Do not specify a CMD or Entrypoint as Testrun will automatically start your service as required by calling the start_network_service script in the bin folder
+```
+
+## module_config.json
+```
{
"config": {
"meta": {
@@ -42,35 +65,11 @@ To add a new network service to Testrun, follow the procedure below:
}
}
}
-```
-
-### Example of {module}.Dockerfile
-```Dockerfile
-# Image name: test-run/{module}
-FROM test-run/base:latest
-
-ARG MODULE_NAME={module}
-ARG MODULE_DIR=modules/network/$MODULE_NAME
-
-# Install network service dependencies
-# ...
-
-# Copy over all configuration files
-COPY $MODULE_DIR/conf /testrun/conf
-
-# Copy over all binary files
-COPY $MODULE_DIR/bin /testrun/bin
-
-# Copy over all python files
-COPY $MODULE_DIR/python /testrun/python
-
-# Do not specify a CMD or Entrypoint as Test Run will automatically start your service as required
```
-### Example of start_network_service script
-
-```bash
+## start_network_service script
+```
#!/bin/bash
CONFIG_FILE=/etc/network_service/config.conf
@@ -89,8 +88,4 @@ echo "Starting Network Service..."
# Restart the network service when the config changes
# ...
-```
-
-
-
-
+```
\ No newline at end of file
diff --git a/docs/network/addresses.md b/docs/network/addresses.md
index 7fa71d716..261242687 100644
--- a/docs/network/addresses.md
+++ b/docs/network/addresses.md
@@ -1,20 +1,19 @@
-## Network Addresses
+# Network addresses
-Each network service is configured with an IPv4 and IPv6 address. For IPv4 addressing, the last number in the IPv4 address is fixed (ensuring the IP is unique). See below for a table of network addresses:
+Each network service is configured with an IPv4 and IPv6 address. For IPv4 addressing, the last number in the IPv4 address is fixed, ensuring the IP is unique. The table below lists network addresses you might need.
-| Name | Mac address | IPv4 address | IPv6 address |
-|---------------------|----------------------|--------------|------------------------------|
-| Internet gateway | 9a:02:57:1e:8f:01 | 10.10.10.1 | fd10:77be:4186::1 |
-| DHCP primary | 9a:02:57:1e:8f:02 | 10.10.10.2 | fd10:77be:4186::2 |
-| DHCP secondary | 9a:02:57:1e:8f:03 | 10.10.10.3 | fd10:77be:4186::3 |
-| DNS server | 9a:02:57:1e:8f:04 | 10.10.10.4 | fd10:77be:4186::4 |
-| NTP server | 9a:02:57:1e:8f:05 | 10.10.10.5 | fd10:77be:4186::5 |
-| Radius authenticator| 9a:02:57:1e:8f:07 | 10.10.10.7 | fd10:77be:4186::7 |
-| Active test module | 9a:02:57:1e:8f:09 | 10.10.10.9 | fd10:77be:4186::9 |
+| Name | MAC address | IPv4 address | IPv6 address |
+| ----------------- | ----------------- | ------------- | ------------------- |
+| Internet gateway | 9a\:02\:57\:1e\:8f\:01 | 10.10.10.1 | fd10\:77be\:4186\:\:1 |
+| DHCP primary | 9a\:02\:57\:1e\:8f\:02 | 10.10.10.2 | fd10\:77be\:4186\:\:2 |
+| DHCP secondary | 9a\:02\:57\:1e\:8f\:03 | 10.10.10.3 | fd10\:77be\:4186\:\:3 |
+| DNS server | 9a\:02\:57\:1e\:8f\:04 | 10.10.10.4 | fd10\:77be\:4186\:\:4 |
+| NTP server | 9a\:02\:57\:1e\:8f\:05 | 10.10.10.5 | fd10\:77be\:4186\:\:5 |
+| Radius authenticator | 9a\:02\:57\:1e\:8f\:07 | 10.10.10.7 | fd10\:77be\:4186\:\:7 |
+| Active test module | 9a\:02\:57\:1e\:8f\:09 | 10.10.10.9 | fd10\:77be\:4186\:\:9 |
+The default network range is 10.10.10.0/24 and devices are assigned addresses in that range via DHCP. The range may change when requested by a test module. In that case, network services restart and are accessible on the new range with the same final host ID. The default IPv6 network is fd10:77be:4186::/64 and addresses are assigned to devices on the network using IPv6 SLAAC.
-The default network range is 10.10.10.0/24 and devices will be assigned addresses in that range via DHCP. The range may change when requested by a test module. In which case, network services will be restarted and accessible on the new range, with the same final host ID. The default IPv6 network is fd10:77be:4186::/64 and addresses will be assigned to devices on the network using IPv6 SLAAC.
-
-When creating a new network module, please ensure that the ip_index value in the module_config.json is unique otherwise unexpected behaviour will occur.
\ No newline at end of file
+When creating a new network module, ensure that the ip_index value in the module_config.json is unique to prevent unexpected behavior.
\ No newline at end of file
diff --git a/docs/network/identify_interfaces.md b/docs/network/identify_interfaces.md
deleted file mode 100644
index 50e62acd3..000000000
--- a/docs/network/identify_interfaces.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-## Identifying network interfaces
-
-For Testrun to operate correctly, you must select the correct network interfaces within the settings panel of the user interface. There are 2 methods to identify the correct network interfaces:
-
-A) Find the printed MAC address on your interface
-
-Some USB network interfaces will have the MAC address printed on the interface itself. This will look something like: ```00:e0:4c:02:0f:a8```.
-
-Compare this printed MAC address against the MAC address provided in the settings panel in the user interface.
-
-B) Connect your interfaces one at a time
-
- 1) Ensure both interfaces are disconnected from your PC and open the settings panel in the user interface.
-
- 2) Connect your internet interface to your PC and refresh the settings panel. One interface, which was not previously present, should now be visibile.
-
- 3) Repeat the previous step for the devices interface.
-
diff --git a/docs/roadmap.pdf b/docs/roadmap.pdf
index 9f4599ee1..eaaf0ea64 100644
Binary files a/docs/roadmap.pdf and b/docs/roadmap.pdf differ
diff --git a/docs/test/README.md b/docs/test/README.md
index 19aa691d8..19fcf3fd5 100644
--- a/docs/test/README.md
+++ b/docs/test/README.md
@@ -1,8 +1,5 @@
+# Testing
-## Testing
-
-The test requirements that are investigated by Testrun can be found in the [test modules documentation](/docs/test/modules.md).
-
-To understand the testing results, various definitions of test results and requirements are specified in the [statuses documentation](/docs/test/statuses.md).
\ No newline at end of file
+Testrun provides modules for you to test your own device. You can learn more about the requirements for each on the [Test modules page](/docs/test/modules.md). The [Test results page](/docs/test/statuses.md) outlines possible results and how to interpret them.
diff --git a/docs/test/modules.md b/docs/test/modules.md
index 7c5851ba4..ff6bb5c53 100644
--- a/docs/test/modules.md
+++ b/docs/test/modules.md
@@ -1,16 +1,26 @@
-## Test Modules
+# Test modules
-Testrun provides some pre-built test modules for you to use when testing your own device. These test modules are listed below:
+Testrun provides some pre-built modules you can use when testing your own device. The table below lists the test module, its purpose, and a link to additional information.
-| Name | Description | Read more |
-|---|---|---|
-| Base | Template for all test modules | [Base module](/modules/test/base/README.md) |
-| Baseline | A sample test module | [Baseline module](/modules/test/baseline/README.md) |
-| Connection | Verify IP and DHCP based behavior | [Connection module](/modules/test/conn/README.md) |
-| DNS | Verify DNS functionality | [DNS module](/modules/test/dns/README.md) |
-| NMAP | Ensure unsecure services are disabled | [NMAP module](/modules/test/nmap/README.md) |
-| NTP | Verify NTP functionality | [NTP module](/modules/test/ntp/README.md) |
-| Protocol | Inspect BMS protocol implementation | [Protocol Module](/modules/test/protocol/README.md) |
-| TLS | Determine TLS client and server behavior | [TLS module](/modules/test/tls/README.md) |
+| Module name | Purpose | Additional documentation |
+| ------------ | ---------------------------- | ----------------------------- |
+| Base | Template for all test modules | [Base module] |
+| Baseline | Sample test module | [Baseline module] |
+| Connection | Verify IP and DHCP-based behavior | [Connection module] |
+| DNS | Verify DNS functionality | [DNS module] |
+| Services | Ensure unsecure services are disabled | [Services module] |
+| NTP | Verify NTP functionality | [NTP module] |
+| Protocol | Inspect BMS protocol implementation | [Protocol Module] |
+| TLS | Determine TLS client and server behavior | [TLS module] |
+
+
+[Base module]: /modules/test/base/README.md
+[Baseline module]: /modules/test/baseline/README.md
+[Connection module]: /modules/test/conn/README.md
+[DNS module]: /modules/test/dns/README.md
+[Services module]: /modules/test/services/README.md
+[NTP module]: /modules/test/ntp/README.md
+[Protocol Module]: /modules/test/protocol/README.md
+[TLS module]: /modules/test/tls/README.md
diff --git a/docs/test/statuses.md b/docs/test/statuses.md
index d196fa4af..8268b3e88 100644
--- a/docs/test/statuses.md
+++ b/docs/test/statuses.md
@@ -1,33 +1,33 @@
-## Test Statuses
-Testrun will output the result and description of each automated test. The test results will be one of the following:
+# Test results
-| Name | Description | What next? |
-|---|---|---|
-| Compliant | The device implements the required feature correctly | Nothing |
-| Non-Compliant | The device does not support the specified requirements for the test | Modify or implement the required functionality on the device |
-| Feature Not Detected | The device does not implement a feature covered by the test | You may implement the functionality (not required) |
-| Error | An error occured whilst running the test | Create a bug report requesting additional support to diagnose the issue |
+Testrun outputs the result and a description of each automated test. The table below includes the result name, its description, and what your next step should be.
-## Test Requirement
-Testrun also determines whether each test is required for the device to receive an overall compliant result. These rules are:
+| Result name | Description | What next? |
+| --------------------- | ------------------------ | ------------------------ |
+| Compliant | The device implements the required feature correctly. | Nothing. |
+| Non-Compliant | The device doesn’t support the specified requirements for the test. | Modify or implement the required functionality on the device. |
+| Informational | Extra information about the device under test | Nothing. |
+| Feature Not Detected | The device doesn’t implement a feature covered by the test. | You may implement the functionality but it’s not required. |
+| Error | An error occurred while running the test. | Create a bug report requesting additional support to diagnose the issue. |
-| Name | Description |
-|---|---|
-| Required | The device must implement the feature |
-| Recommended | The device should implement the feature, but will not receive an overall Non-Compliant if not implemented |
-| Roadmap | The device should implement this feature in the future, but is not required at the moment |
-| Required If Applicable | If the device implements this feature, it must be implemented correctly (as per the test requirements) |
-## Testrun Statuses
-Once testing is completed, an overall result for the test attempt will be produced. This is calculated by comparing the result of all tests, and whether they are required or not required.
+# Test requirements
-### Compliant
-All required tests are implemented correctly, and all required if applicable tests are implemented correctly (where the feature has been implemented).
+Testrun determines whether the device needs each test to receive an overall compliant result. Here are the rules and what they mean:
-### Non-Compliant
-One or more of the required tests (or required if applicable tests) have produced a non-compliant result.
+- Required: The device must implement the feature.
+- Recommended: The device should implement the feature but won't receive an overall Non-Compliant if it's not implemented.
+- Roadmap: The device should implement this feature in the future, but it's not required at the moment.
+- Required If Applicable: If the device implements this feature, it must be implemented correctly (per the test requirements).
+- Informational: Regardless whether the device implements the feature or not, the test will receive an Informational result.
-### Error
-One of more of the required tests (or required if applicable tests) have not executed correctly. This does not necessarily indicate that the device is compliant or non-compliant.
+# Testrun statuses
+
+Once testing is complete, the program produces an overall status for the test attempt. It's calculated by comparing the results of all tests and whether they're required or not. The possible statuses are:
+
+- Compliant: All required tests are implemented correctly, and all required if applicable tests are implemented correctly (where the feature is implemented).
+- Non-Compliant: One or more of the required tests (or Required If Applicable tests) produced a Non-Compliant result.
+- Error: One or more of the required tests (or Required If Applicable tests) didn't execute correctly. This doesn't necessarily indicate that the device is Compliant or Non-Compliant.
+- Cancelled: Either the device was disconnected during testing or the user requested to cancel the test attempt.
\ No newline at end of file
diff --git a/docs/ui/accessibility.md b/docs/ui/accessibility.md
new file mode 100644
index 000000000..58d948522
--- /dev/null
+++ b/docs/ui/accessibility.md
@@ -0,0 +1,12 @@
+
+
+# Accessibility
+
+We designed Testrun with accessibility at its core. The application provides full support for:
+
+- Screen readers
+- Keyboard navigation
+- Responsive resizing and scaling
+- [Helperbird](https://www.helperbird.com/)
+
+For a more thorough explanation of these features, download the [accessibility video](https://github.com/google/testrun/raw/main/docs/ui/accessibility.mp4). If you require further accommodations when using Testrun, please [raise an issue](https://github.com/google/testrun/issues/new/choose).
\ No newline at end of file
diff --git a/docs/ui/device_icon.png b/docs/ui/device_icon.png
deleted file mode 100644
index 2472f7da2..000000000
Binary files a/docs/ui/device_icon.png and /dev/null differ
diff --git a/docs/ui/getstarted--2dn8vrzsspe.png b/docs/ui/getstarted--2dn8vrzsspe.png
new file mode 100644
index 000000000..302e55302
Binary files /dev/null and b/docs/ui/getstarted--2dn8vrzsspe.png differ
diff --git a/docs/ui/getstarted--3d9k3si3ul1.png b/docs/ui/getstarted--3d9k3si3ul1.png
new file mode 100644
index 000000000..5c762156d
Binary files /dev/null and b/docs/ui/getstarted--3d9k3si3ul1.png differ
diff --git a/docs/ui/getstarted-actions-device.png b/docs/ui/getstarted-actions-device.png
new file mode 100644
index 000000000..4363a194f
Binary files /dev/null and b/docs/ui/getstarted-actions-device.png differ
diff --git a/docs/ui/getstarted-actions-testing.png b/docs/ui/getstarted-actions-testing.png
new file mode 100644
index 000000000..dbffebb14
Binary files /dev/null and b/docs/ui/getstarted-actions-testing.png differ
diff --git a/docs/ui/getstarted-add-device.png b/docs/ui/getstarted-add-device.png
new file mode 100644
index 000000000..5b93b46b4
Binary files /dev/null and b/docs/ui/getstarted-add-device.png differ
diff --git a/docs/ui/getstarted-certificates-menu.png b/docs/ui/getstarted-certificates-menu.png
new file mode 100644
index 000000000..60eceae7a
Binary files /dev/null and b/docs/ui/getstarted-certificates-menu.png differ
diff --git a/docs/ui/getstarted-device-repository.png b/docs/ui/getstarted-device-repository.png
new file mode 100644
index 000000000..236842ce4
Binary files /dev/null and b/docs/ui/getstarted-device-repository.png differ
diff --git a/docs/ui/getstarted-reports.png b/docs/ui/getstarted-reports.png
new file mode 100644
index 000000000..17577b735
Binary files /dev/null and b/docs/ui/getstarted-reports.png differ
diff --git a/docs/ui/getstarted-risk-assessment.png b/docs/ui/getstarted-risk-assessment.png
new file mode 100644
index 000000000..a14b7da10
Binary files /dev/null and b/docs/ui/getstarted-risk-assessment.png differ
diff --git a/docs/ui/getstarted-settings-menu.png b/docs/ui/getstarted-settings-menu.png
new file mode 100644
index 000000000..6c53ed4ec
Binary files /dev/null and b/docs/ui/getstarted-settings-menu.png differ
diff --git a/docs/ui/getstarted-testing.png b/docs/ui/getstarted-testing.png
new file mode 100644
index 000000000..634269e5c
Binary files /dev/null and b/docs/ui/getstarted-testing.png differ
diff --git a/docs/ui/getstarted-waiting-for-device.png b/docs/ui/getstarted-waiting-for-device.png
new file mode 100644
index 000000000..36a307316
Binary files /dev/null and b/docs/ui/getstarted-waiting-for-device.png differ
diff --git a/docs/ui/history_icon.png b/docs/ui/history_icon.png
deleted file mode 100644
index eb95a8663..000000000
Binary files a/docs/ui/history_icon.png and /dev/null differ
diff --git a/docs/ui/progress_icon.png b/docs/ui/progress_icon.png
deleted file mode 100644
index c326d185e..000000000
Binary files a/docs/ui/progress_icon.png and /dev/null differ
diff --git a/docs/ui/settings_icon.png b/docs/ui/settings_icon.png
deleted file mode 100644
index 8fc83b9bb..000000000
Binary files a/docs/ui/settings_icon.png and /dev/null differ
diff --git a/docs/ui/settings_menu.png b/docs/ui/settings_menu.png
deleted file mode 100644
index 046526b25..000000000
Binary files a/docs/ui/settings_menu.png and /dev/null differ
diff --git a/docs/ui/test_name.png b/docs/ui/test_name.png
deleted file mode 100644
index 3d18df19d..000000000
Binary files a/docs/ui/test_name.png and /dev/null differ
diff --git a/docs/virtual_machine.md b/docs/virtual_machine.md
index 2f029b296..827ee36b3 100644
--- a/docs/virtual_machine.md
+++ b/docs/virtual_machine.md
@@ -1,38 +1,41 @@
-## Virtual Machine
+# Run on a virtual machine
-This guide will provide steps to use Testrun within a virtual machine in virtual Box (VMWare and other providers have not yet been tested). You should use this guide alongside the [Get Started guide](/docs/get_started.md) - only differences will be outlined in this guide.
+This page provides steps to use Testrun within a virtual machine in VirtualBox. VMWare and other providers haven't been tested yet. You should use these instructions alongside the [Get started guide](/docs/get_started.md).
-## Prerequisites
+# Prerequisites
-### Hardware
+## Hardware
-Before starting with Testrun, ensure you have the following hardware:
-- PC running any OS that supports Virtual Box
-- 2x USB Ethernet adapter (built in ethernet connections are not supported)
-- Internet connection
+Before you start with Testrun, ensure you have the following hardware:
-### Software
+- PC running any OS that supports VirtualBox
+- 2x USB Ethernet adapter (built-in Ethernet connections aren't supported)
+- Internet connection
-Ensure the following software is installed on the host PC:
- - Virtual Box
+## Software
-Ensure the following software is installed on your virtual machine:
-- Ubuntu LTS (22.04 or 20.04)
-- Docker - installation guide: [https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository)
+Ensure you have VirtualBox installed on the host PC. Then, install the following software on your virtual machine:
-## Installation
+- Ubuntu LTS (22.04 or 24.04)
+- Docker
+ - Refer to the [installation guide](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository) as needed.
-In addition to the install steps provided in the Get Started guide, the default user must be added to the sudo group.
-1. Open a terminal and run ```sudo su``` to login as root (you will be prompted for your password).
-2. Add the default user to the sudo group by running ```adduser {username} sudo```.
-3. Restart the virtual machine.
-4. Continue the installation as per the Get Started guide.
+# Installation
-## Start Testrun
+As part of installation, you must add the default user to the sudo group:
+
+1. Open a terminal and run `sudo su` to log in as root.
+1. Enter your password when prompted.
+1. Add the default user to the sudo group by running `adduser {username} sudo`.
+1. Restart the virtual machine.
+1. Follow the steps in the [Get started guide](/docs/get_started.md) to complete the installation.
+
+# Start Testrun
+
+Follow these steps to start Testrun. Keep in mind that attaching USB Ethernet adapters is different when working in a virtual machine.
-Attaching USB ethernet adapters is different when working in a Virtual Machine.
1. Ensure the 2x adapters are attached to the host PC.
-2. With the virtual machine running, right click the USB icon in the bottom right of the window.
-3. Select the 2x ethernet adapter names and check that these two adapters have now appeared in the virtual machine.
\ No newline at end of file
+1. With the virtual machine running, right-click the **USB** icon in the bottom-right of the window.
+1. Select the 2x Ethernet adapter names. The two adapters should now appear in the virtual machine.
\ No newline at end of file
diff --git a/framework/python/src/api/api.py b/framework/python/src/api/api.py
index aed663ab8..a11b35160 100644
--- a/framework/python/src/api/api.py
+++ b/framework/python/src/api/api.py
@@ -24,10 +24,11 @@
import signal
import threading
import uvicorn
-from urllib.parse import urlparse
+from core import tasks
from common import logger
from common.device import Device
+from common.statuses import TestrunStatus
LOGGER = logger.get_logger("api")
@@ -35,8 +36,17 @@
DEVICE_MANUFACTURER_KEY = "manufacturer"
DEVICE_MODEL_KEY = "model"
DEVICE_TEST_MODULES_KEY = "test_modules"
+DEVICE_TEST_PACK_KEY = "test_pack"
+DEVICE_TYPE_KEY = "type"
+DEVICE_TECH_KEY = "technology"
+DEVICE_ADDITIONAL_INFO_KEY = "additional_info"
+
DEVICES_PATH = "local/devices"
-DEFAULT_DEVICE_INTF = "enx123456789123"
+PROFILES_PATH = "local/risk_profiles"
+
+RESOURCES_PATH = "resources"
+DEVICE_FOLDER_PATH = "devices"
+DEVICE_QUESTIONS_FILE_NAME = "device_profile.json"
LATEST_RELEASE_CHECK = ("https://api.github.com/repos/google/" +
"testrun/releases/latest")
@@ -45,32 +55,45 @@
class Api:
"""Provide REST endpoints to manage Testrun"""
- def __init__(self, test_run):
+ def __init__(self, testrun):
- self._test_run = test_run
+ self._testrun = testrun
self._name = "Testrun API"
self._router = APIRouter()
- self._session = self._test_run.get_session()
+ # Load static JSON resources
+ device_resources = os.path.join(self._testrun.get_root_dir(),
+ RESOURCES_PATH,
+ DEVICE_FOLDER_PATH)
+
+ # Load device profile questions
+ self._device_profile = self._load_json(device_resources,
+ DEVICE_QUESTIONS_FILE_NAME)
+
+ # Fetch Testrun session
+ self._session = self._testrun.get_session()
+ # System endpoints
self._router.add_api_route("/system/interfaces", self.get_sys_interfaces)
self._router.add_api_route("/system/config",
self.post_sys_config,
methods=["POST"])
self._router.add_api_route("/system/config", self.get_sys_config)
self._router.add_api_route("/system/start",
- self.start_test_run,
+ self.start_testrun,
methods=["POST"])
self._router.add_api_route("/system/stop",
- self.stop_test_run,
+ self.stop_testrun,
methods=["POST"])
self._router.add_api_route("/system/status", self.get_status)
self._router.add_api_route("/system/shutdown",
self.shutdown,
methods=["POST"])
-
self._router.add_api_route("/system/version", self.get_version)
+ self._router.add_api_route("/system/modules", self.get_test_modules)
+ self._router.add_api_route("/system/testpacks", self.get_test_packs)
+ # Report endpoints
self._router.add_api_route("/reports", self.get_reports)
self._router.add_api_route("/report",
self.delete_report,
@@ -81,6 +104,7 @@ def __init__(self, test_run):
self.get_results,
methods=["POST"])
+ # Device endpoints
self._router.add_api_route("/devices", self.get_devices)
self._router.add_api_route("/device",
self.delete_device,
@@ -89,10 +113,9 @@ def __init__(self, test_run):
self._router.add_api_route("/device/edit",
self.edit_device,
methods=["POST"])
+ self._router.add_api_route("/devices/format", self.get_devices_profile)
- # Load modules
- self._router.add_api_route("/system/modules", self.get_test_modules)
-
+ # Certificate endpoints
self._router.add_api_route("/system/config/certs", self.get_certs)
self._router.add_api_route("/system/config/certs",
self.upload_cert,
@@ -110,12 +133,23 @@ def __init__(self, test_run):
self._router.add_api_route("/profiles",
self.delete_profile,
methods=["DELETE"])
+ self._router.add_api_route("/profile/{profile_name}",
+ self.export_profile,
+ methods=["POST"])
# Allow all origins to access the API
origins = ["*"]
- self._app = FastAPI()
+ # Scheduler for background periodic tasks
+ self._scheduler = tasks.PeriodicTasks(self._testrun)
+
+ # Init FastAPI
+ self._app = FastAPI(lifespan=self._scheduler.start)
+
+ # Attach router to FastAPI
self._app.include_router(self._router)
+
+ # Attach CORS middleware
self._app.add_middleware(
CORSMiddleware,
allow_origins=origins,
@@ -124,10 +158,27 @@ def __init__(self, test_run):
allow_headers=["*"],
)
+ # Use separate thread for API
self._api_thread = threading.Thread(target=self._start,
name="Testrun API",
daemon=True)
+ def _load_json(self, directory, file_name):
+ """Utility method to load json files' """
+ # Construct the base path relative to the main folder
+ root_dir = self._testrun.get_root_dir()
+
+ # Construct the full file path
+ file_path = os.path.join(root_dir, directory, file_name)
+
+ # Open the file in read mode
+ with open(file_path, "r", encoding="utf-8") as file:
+ # Return the file content
+ return json.load(file)
+
+ def _get_testrun(self):
+ return self._testrun
+
def start(self):
LOGGER.info("Starting API")
self._api_thread.start()
@@ -165,7 +216,19 @@ async def post_sys_config(self, request: Request, response: Response):
try:
config = (await request.body()).decode("UTF-8")
config_json = json.loads(config)
+
+ # Validate req fields
+ if ("network" not in config_json or
+ "device_intf" not in config_json.get("network") or
+ "internet_intf" not in config_json.get("network") or
+ "log_level" not in config_json):
+ response.status_code = status.HTTP_400_BAD_REQUEST
+ return self._generate_msg(
+ False,
+ "Configuration is missing required fields")
+
self._session.set_config(config_json)
+
# Catch JSON Decode error etc
except JSONDecodeError:
response.status_code = status.HTTP_400_BAD_REQUEST
@@ -176,9 +239,12 @@ async def get_sys_config(self):
return self._session.get_config()
async def get_devices(self):
- return self._session.get_device_repository()
+ devices = []
+ for device in self._session.get_device_repository():
+ devices.append(device.to_dict())
+ return devices
- async def start_test_run(self, request: Request, response: Response):
+ async def start_testrun(self, request: Request, response: Response):
LOGGER.debug("Received start command")
@@ -199,9 +265,25 @@ async def start_test_run(self, request: Request, response: Response):
device = self._session.get_device(body_json["device"]["mac_addr"])
+ # Check if requested device is known in the device repository
+ if device is None:
+ response.status_code = status.HTTP_404_NOT_FOUND
+ return self._generate_msg(
+ False, "A device with that MAC address could not be found")
+
+ # Check if device is fully configured
+ if device.status != "Valid":
+ response.status_code = status.HTTP_400_BAD_REQUEST
+ return self._generate_msg(False, "Device configuration is not complete")
+
# Check Testrun is not already running
- if self._test_run.get_session().get_status() in [
- "In Progress", "Waiting for Device", "Monitoring"
+ if self._testrun.get_session().get_status() in [
+ TestrunStatus.IN_PROGRESS,
+ TestrunStatus.WAITING_FOR_DEVICE,
+ TestrunStatus.MONITORING,
+ TestrunStatus.VALIDATING,
+ TestrunStatus.STARTING
+
]:
LOGGER.debug("Testrun is already running. Cannot start another instance")
response.status_code = status.HTTP_409_CONFLICT
@@ -209,40 +291,42 @@ async def start_test_run(self, request: Request, response: Response):
False, "Testrun cannot be started " +
"whilst a test is running on another device")
- # Check if requested device is known in the device repository
- if device is None:
- response.status_code = status.HTTP_404_NOT_FOUND
- return self._generate_msg(
- False, "A device with that MAC address could not be found")
-
device.firmware = body_json["device"]["firmware"]
# Check if config has been updated (device interface not default)
- if (self._test_run.get_session().get_device_interface() ==
- DEFAULT_DEVICE_INTF):
+ if (self._testrun.get_session().get_device_interface() ==
+ ""):
response.status_code = status.HTTP_400_BAD_REQUEST
return self._generate_msg(
False, "Testrun configuration has not yet " + "been completed.")
# Check Testrun is able to start
- if self._test_run.get_net_orc().check_config() is False:
+ if self._testrun.get_net_orc().check_config() is False:
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
return self._generate_msg(
False, "Configured interfaces are not " +
"ready for use. Ensure required interfaces " + "are connected.")
- device.test_modules = body_json["device"]["test_modules"]
+ # UI doesn't send individual test configs so we need to
+ # merge these manually until the UI is updated to handle
+ # the full config file
+ for module_name, module_config in device.test_modules.items():
+ # Check if the module exists in UI test modules
+ if module_name in body_json["device"]["test_modules"]:
+ # Merge the enabled state
+ module_config["enabled"] = body_json[
+ "device"]["test_modules"][module_name]["enabled"]
LOGGER.info("Starting Testrun with device target " +
f"{device.manufacturer} {device.model} with " +
f"MAC address {device.mac_addr}")
- thread = threading.Thread(target=self._start_test_run, name="Testrun")
+ thread = threading.Thread(target=self._start_testrun, name="Testrun")
thread.start()
- self._test_run.get_session().set_target_device(device)
+ self._testrun.get_session().set_target_device(device)
- return self._test_run.get_session().to_json()
+ return self._testrun.get_session().to_json()
def _generate_msg(self, success, message):
msg_type = "success"
@@ -250,24 +334,28 @@ def _generate_msg(self, success, message):
msg_type = "error"
return json.loads('{"' + msg_type + '": "' + message + '"}')
- def _start_test_run(self):
- self._test_run.start()
+ def _start_testrun(self):
+ self._testrun.start()
- async def stop_test_run(self, response: Response):
+ async def stop_testrun(self, response: Response):
LOGGER.debug("Received stop command")
# Check if Testrun is running
- if (self._test_run.get_session().get_status()
- not in ["In Progress", "Waiting for Device", "Monitoring"]):
+ if (self._testrun.get_session().get_status()
+ not in [TestrunStatus.IN_PROGRESS,
+ TestrunStatus.WAITING_FOR_DEVICE,
+ TestrunStatus.MONITORING,
+ TestrunStatus.VALIDATING,
+ TestrunStatus.STARTING]):
response.status_code = 404
return self._generate_msg(False, "Testrun is not currently running")
- self._test_run.stop()
+ self._testrun.stop()
return self._generate_msg(True, "Testrun stopped")
async def get_status(self):
- return self._test_run.get_session().to_json()
+ return self._testrun.get_session().to_json()
def shutdown(self, response: Response):
@@ -275,20 +363,25 @@ def shutdown(self, response: Response):
# Check that Testrun is not currently running
if (self._session.get_status()
- not in ["Cancelled", "Compliant", "Non-Compliant", "Idle"]):
+ not in [TestrunStatus.CANCELLED,
+ TestrunStatus.PROCEED,
+ TestrunStatus.DO_NOT_PROCEED,
+ TestrunStatus.COMPLETE,
+ TestrunStatus.IDLE
+ ]):
LOGGER.debug("Unable to shutdown Testrun as Testrun is in progress")
response.status_code = 400
return self._generate_msg(
False, "Unable to shutdown. A test is currently in progress.")
- self._test_run.shutdown()
+ self._testrun.shutdown()
os.kill(os.getpid(), signal.SIGTERM)
async def get_version(self, response: Response):
# Add defaults
json_response = {}
- json_response["installed_version"] = "v" + self._test_run.get_version()
+ json_response["installed_version"] = "v" + self._testrun.get_version()
json_response["update_available"] = False
json_response["latest_version"] = None
json_response["latest_version_url"] = (
@@ -343,15 +436,13 @@ async def get_reports(self, request: Request):
LOGGER.debug("Received reports list request")
# Resolve the server IP from the request so we
# can fix the report URL
- client_origin = request.headers.get("Origin")
- parsed_url = urlparse(client_origin)
- server_ip = parsed_url.hostname # This will give you the IP address
-
reports = self._session.get_all_reports()
for report in reports:
# report URL is currently hard coded as localhost so we can
# replace that to fix the IP dynamically from the requester
- report["report"] = report["report"].replace("localhost", server_ip)
+ report["report"] = report["report"].replace(
+ "localhost", request.client.host)
+ report["export"] = report["report"].replace("report", "export")
return reports
async def delete_report(self, request: Request, response: Response):
@@ -360,7 +451,7 @@ async def delete_report(self, request: Request, response: Response):
if len(body_raw) == 0:
response.status_code = 400
- return self._generate_msg(False, "Invalid request received")
+ return self._generate_msg(False, "Invalid request received, missing body")
try:
body_json = json.loads(body_raw)
@@ -372,12 +463,18 @@ async def delete_report(self, request: Request, response: Response):
if "mac_addr" not in body_json or "timestamp" not in body_json:
response.status_code = 400
- return self._generate_msg(False, "Invalid request received")
+ return self._generate_msg(False, "Missing mac address or timestamp")
mac_addr = body_json.get("mac_addr").lower()
timestamp = body_json.get("timestamp")
- parsed_timestamp = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")
- timestamp_formatted = parsed_timestamp.strftime("%Y-%m-%dT%H:%M:%S")
+
+ try:
+ parsed_timestamp = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")
+ timestamp_formatted = parsed_timestamp.strftime("%Y-%m-%dT%H:%M:%S")
+
+ except ValueError:
+ response.status_code = 400
+ return self._generate_msg(False, "Incorrect timestamp format")
# Get device from MAC address
device = self._session.get_device(mac_addr)
@@ -386,11 +483,19 @@ async def delete_report(self, request: Request, response: Response):
response.status_code = 404
return self._generate_msg(False, "Could not find device")
- if self._test_run.delete_report(device, timestamp_formatted):
+ # Assign the reports folder path from testrun
+ reports_folder = self._testrun.get_reports_folder(device)
+
+ # Check if reports folder exists
+ if not os.path.exists(reports_folder):
+ response.status_code = 404
+ return self._generate_msg(False, "Report not found")
+
+ if self._testrun.delete_report(device, timestamp_formatted):
return self._generate_msg(True, "Deleted report")
response.status_code = 500
- return self._generate_msg(False, "Error occured whilst deleting report")
+ return self._generate_msg(False, "Error occurred whilst deleting report")
async def delete_device(self, request: Request, response: Response):
@@ -410,7 +515,7 @@ async def delete_device(self, request: Request, response: Response):
mac_addr = device_json.get("mac_addr").lower()
# Check that device exists
- device = self._test_run.get_session().get_device(mac_addr)
+ device = self._testrun.get_session().get_device(mac_addr)
if device is None:
response.status_code = 404
@@ -419,13 +524,18 @@ async def delete_device(self, request: Request, response: Response):
# Check that Testrun is not currently running against this device
if (self._session.get_target_device() == device
and self._session.get_status()
- not in ["Cancelled", "Compliant", "Non-Compliant"]):
+ not in [TestrunStatus.CANCELLED,
+ TestrunStatus.COMPLETE,
+ TestrunStatus.PROCEED,
+ TestrunStatus.DO_NOT_PROCEED
+ ]):
+
response.status_code = 403
return self._generate_msg(
- False, "Cannot delete this device whilst " + "it is being tested")
+ False, "Cannot delete this device whilst it is being tested")
# Delete device
- self._test_run.delete_device(device)
+ self._testrun.delete_device(device)
# Return success response
response.status_code = 200
@@ -436,7 +546,7 @@ async def delete_device(self, request: Request, response: Response):
LOGGER.error(e)
response.status_code = 500
return self._generate_msg(
- False, "An error occured whilst deleting " + "the device")
+ False, "An error occurred whilst deleting the device")
async def save_device(self, request: Request, response: Response):
LOGGER.debug("Received device post request")
@@ -464,6 +574,19 @@ async def save_device(self, request: Request, response: Response):
device_json.get(DEVICE_MODEL_KEY)
)
+ # Check if device folder exists
+ device_folder = os.path.join(self._testrun.get_root_dir(),
+ DEVICES_PATH,
+ device_json.get(DEVICE_MANUFACTURER_KEY) +
+ " " +
+ device_json.get(DEVICE_MODEL_KEY))
+
+ if os.path.exists(device_folder):
+ response.status_code = status.HTTP_409_CONFLICT
+ return self._generate_msg(
+ False, "A folder with that name already exists, " \
+ "please rename the device or folder")
+
if device is None:
# Create new device
@@ -471,10 +594,15 @@ async def save_device(self, request: Request, response: Response):
device.mac_addr = device_json.get(DEVICE_MAC_ADDR_KEY).lower()
device.manufacturer = device_json.get(DEVICE_MANUFACTURER_KEY)
device.model = device_json.get(DEVICE_MODEL_KEY)
+ device.test_pack = device_json.get(DEVICE_TEST_PACK_KEY)
+ device.type = device_json.get(DEVICE_TYPE_KEY)
+ device.technology = device_json.get(DEVICE_TECH_KEY)
+ device.additional_info = device_json.get(DEVICE_ADDITIONAL_INFO_KEY)
+
device.device_folder = device.manufacturer + " " + device.model
device.test_modules = device_json.get(DEVICE_TEST_MODULES_KEY)
- self._test_run.create_device(device)
+ self._testrun.create_device(device)
response.status_code = status.HTTP_201_CREATED
else:
@@ -517,14 +645,18 @@ async def edit_device(self, request: Request, response: Response):
if device is None:
response.status_code = status.HTTP_404_NOT_FOUND
return self._generate_msg(
- False, "A device with that MAC " + "address could not be found")
+ False, "A device with that MAC address could not be found")
if (self._session.get_target_device() == device
and self._session.get_status()
- not in ["Cancelled", "Compliant", "Non-Compliant"]):
+ not in [TestrunStatus.CANCELLED,
+ TestrunStatus.COMPLETE,
+ TestrunStatus.PROCEED,
+ TestrunStatus.DO_NOT_PROCEED
+ ]):
response.status_code = 403
return self._generate_msg(
- False, "Cannot edit this device whilst " + "it is being tested")
+ False, "Cannot edit this device whilst it is being tested")
# Check if a device exists with the new MAC address
check_new_device = self._session.get_device(
@@ -534,15 +666,22 @@ async def edit_device(self, request: Request, response: Response):
!= check_new_device.mac_addr):
response.status_code = status.HTTP_409_CONFLICT
return self._generate_msg(
- False, "A device with that MAC address " + "already exists")
+ False, "A device with that MAC address already exists")
# Update the device
device.mac_addr = device_json.get(DEVICE_MAC_ADDR_KEY).lower()
device.manufacturer = device_json.get(DEVICE_MANUFACTURER_KEY)
device.model = device_json.get(DEVICE_MODEL_KEY)
+ device.test_pack = device_json.get(DEVICE_TEST_PACK_KEY)
+ device.type = device_json.get(DEVICE_TYPE_KEY)
+ device.technology = device_json.get(DEVICE_TECH_KEY)
+ device.additional_info = device_json.get(DEVICE_ADDITIONAL_INFO_KEY)
device.test_modules = device_json.get(DEVICE_TEST_MODULES_KEY)
- self._test_run.save_device(device, device_json)
+ # Update device status to valid now that configuration is complete
+ device.status = "Valid"
+
+ self._testrun.save_device(device)
response.status_code = status.HTTP_200_OK
return device.to_config_json()
@@ -555,6 +694,12 @@ async def edit_device(self, request: Request, response: Response):
async def get_report(self, response: Response, device_name, timestamp):
device = self._session.get_device_by_name(device_name)
+ # If the device not found
+ if device is None:
+ LOGGER.info("Device not found, returning 404")
+ response.status_code = 404
+ return self._generate_msg(False, "Device not found")
+
# 1.3 file path
file_path = os.path.join(
DEVICES_PATH,
@@ -608,47 +753,77 @@ async def get_results(self, request: Request, response: Response, device_name,
return self._generate_msg(False,
"A device with that name could not be found")
- file_path = self._get_test_run().get_test_orc().zip_results(
+ # Check if report exists (1.3 file path)
+ report_file_path = os.path.join(
+ DEVICES_PATH,
+ device_name,
+ "reports",
+ timestamp,"test",
+ device.mac_addr.replace(":",""))
+
+ if not os.path.isdir(report_file_path):
+ # pre 1.3 file path
+ report_file_path = os.path.join(DEVICES_PATH, device_name, "reports",
+ timestamp)
+
+ if not os.path.isdir(report_file_path):
+ LOGGER.info("Report could not be found, returning 404")
+ response.status_code = 404
+ return self._generate_msg(False, "Report could not be found")
+
+ zip_file_path = self._get_testrun().get_test_orc().zip_results(
device, timestamp, profile)
- if file_path is None:
+ if zip_file_path is None:
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
return self._generate_msg(
False, "An error occurred whilst archiving test results")
- if os.path.isfile(file_path):
- return FileResponse(file_path)
+ if os.path.isfile(zip_file_path):
+ return FileResponse(zip_file_path)
else:
LOGGER.info("Test results could not be found, returning 404")
response.status_code = 404
return self._generate_msg(False, "Test results could not be found")
+ async def get_devices_profile(self):
+ """Device profile questions"""
+ return self._device_profile
+
def _validate_device_json(self, json_obj):
# Check all required properties are present
- if not (DEVICE_MAC_ADDR_KEY in json_obj and DEVICE_MANUFACTURER_KEY
- in json_obj and DEVICE_MODEL_KEY in json_obj):
- return False
+ for string in [
+ DEVICE_MAC_ADDR_KEY,
+ DEVICE_MANUFACTURER_KEY,
+ DEVICE_MODEL_KEY,
+ DEVICE_TYPE_KEY,
+ DEVICE_TECH_KEY,
+ DEVICE_ADDITIONAL_INFO_KEY
+ ]:
+ if string not in json_obj:
+ LOGGER.error(f"Missing required key {string} in device configuration")
+ return False
# Check length of strings
if len(json_obj.get(DEVICE_MANUFACTURER_KEY)) > 28 or len(
json_obj.get(DEVICE_MODEL_KEY)) > 28:
+ LOGGER.error("Device manufacturer or model are longer than 28 characters")
return False
disallowed_chars = ["/", "\\", "\'", "\"", ";"]
for char in json_obj.get(DEVICE_MANUFACTURER_KEY):
if char in disallowed_chars:
+ LOGGER.error("Disallowed character in device manufacturer")
return False
for char in json_obj.get(DEVICE_MODEL_KEY):
if char in disallowed_chars:
+ LOGGER.error("Disallowed character in device model")
return False
return True
- def _get_test_run(self):
- return self._test_run
-
# Profiles
def get_profiles_format(self, response: Response):
@@ -670,6 +845,12 @@ async def update_profile(self, request: Request, response: Response):
LOGGER.debug("Received profile update request")
+ # Check if the profiles format was loaded correctly
+ if self.get_session().get_profiles_format() is None:
+ response.status_code = status.HTTP_501_NOT_IMPLEMENTED
+ return self._generate_msg(False,
+ "Risk profiles are not available right now")
+
try:
req_raw = (await request.body()).decode("UTF-8")
req_json = json.loads(req_raw)
@@ -679,12 +860,18 @@ async def update_profile(self, request: Request, response: Response):
response.status_code = status.HTTP_400_BAD_REQUEST
return self._generate_msg(False, "Invalid request received")
+ # Validate json profile
+ if not self.get_session().validate_profile_json(req_json):
+ response.status_code = status.HTTP_400_BAD_REQUEST
+ return self._generate_msg(False, "Invalid request received")
+
profile_name = req_json.get("name")
# Check if profile exists
profile = self.get_session().get_profile(profile_name)
if profile is None:
+
# Create new profile
profile = self.get_session().update_profile(req_json)
@@ -746,6 +933,93 @@ async def delete_profile(self, request: Request, response: Response):
return self._generate_msg(True, "Successfully deleted that profile")
+ async def export_profile(self, request: Request, response: Response,
+ profile_name):
+
+ LOGGER.debug(f"Received get profile request for {profile_name}")
+
+ device = None
+
+ try:
+ req_raw = (await request.body()).decode("UTF-8")
+ req_json = json.loads(req_raw)
+
+ # Check if device mac_addr has been specified
+ if "mac_addr" in req_json and len(req_json.get("mac_addr")) > 0:
+ device_mac_addr = req_json.get("mac_addr")
+ device = self.get_session().get_device(device_mac_addr)
+
+ # If device is not found return 404
+ if device is None:
+ response.status_code = status.HTTP_404_NOT_FOUND
+ return self._generate_msg(
+ False, "A device with that mac address could not be found")
+
+ except JSONDecodeError:
+ # Device not specified
+ pass
+
+ # Retrieve the profile
+ profile = self._session.get_profile(profile_name)
+
+ # If the profile not found return 404
+ if profile is None:
+ LOGGER.info("Profile not found, returning 404")
+ response.status_code = 404
+ return self._generate_msg(False, "Profile could not be found")
+
+ # If device has been added into the body
+ if device:
+
+ try:
+
+ # Path where the PDF will be saved
+ profile_pdf_path = os.path.join(PROFILES_PATH, f"{profile_name}.pdf")
+
+ # Write the PDF content
+ with open(profile_pdf_path, "wb") as f:
+ f.write(profile.to_pdf(device).getvalue())
+
+ # Return the pdf file
+ if os.path.isfile(profile_pdf_path):
+ return FileResponse(profile_pdf_path)
+ else:
+ LOGGER.info("Profile could not be found, returning 404")
+ response.status_code = 404
+ return self._generate_msg(False, "Profile could not be found")
+
+ # Exceptions if the PDF creation fails
+ except Exception as e:
+ LOGGER.error(f"Error creating the profile PDF: {e}")
+ response.status_code = 500
+ return self._generate_msg(False, "Error retrieving the profile PDF")
+
+ # If device not added into the body
+ else:
+
+ try:
+
+ # Path where the PDF will be saved
+ profile_pdf_path = os.path.join(PROFILES_PATH, f"{profile_name}.pdf")
+
+ # Write the PDF content
+ with open(profile_pdf_path, "wb") as f:
+ f.write(profile.to_pdf_no_device().getvalue())
+
+ # Return the pdf file
+ if os.path.isfile(profile_pdf_path):
+ return FileResponse(profile_pdf_path)
+ else:
+ LOGGER.info("Profile could not be found, returning 404")
+ response.status_code = 404
+ return self._generate_msg(False, "Profile could not be found")
+
+ # Exceptions if the PDF creation fails
+ except Exception as e:
+ LOGGER.error(f"Error creating the profile PDF: {e}")
+ response.status_code = 500
+ return self._generate_msg(False, "Error retrieving the profile PDF")
+
# Certificates
def get_certs(self):
LOGGER.debug("Received certs list request")
@@ -798,6 +1072,20 @@ async def upload_cert(self, file: UploadFile, response: Response):
False, "A certificate with that common name already exists."
)
+ # Returned when organization name is missing
+ elif str(e) == "Certificate is missing the organization name":
+ response.status_code = status.HTTP_400_BAD_REQUEST
+ return self._generate_msg(
+ False, "The certificate must contain the organization name"
+ )
+
+ # Returned when common name is missing
+ elif str(e) == "Certificate is missing the common name":
+ response.status_code = status.HTTP_400_BAD_REQUEST
+ return self._generate_msg(
+ False, "The certificate must contain the common name"
+ )
+
# Returned when unable to load PEM file
else:
response.status_code = status.HTTP_400_BAD_REQUEST
@@ -845,7 +1133,13 @@ async def delete_cert(self, request: Request, response: Response):
def get_test_modules(self):
modules = []
- for module in self._test_run.get_test_orc().get_test_modules():
+ for module in self._testrun.get_test_orc().get_test_modules():
if module.enabled and module.enable_container:
modules.append(module.display_name)
return modules
+
+ def get_test_packs(self):
+ test_packs: list[str] = []
+ for test_pack in self._testrun.get_test_orc().get_test_packs():
+ test_packs.append(test_pack.name)
+ return test_packs
diff --git a/framework/python/src/common/device.py b/framework/python/src/common/device.py
index c6a289d2c..d90720d90 100644
--- a/framework/python/src/common/device.py
+++ b/framework/python/src/common/device.py
@@ -14,7 +14,7 @@
"""Track device object information."""
-from typing import Dict, List
+from typing import List, Dict
from dataclasses import dataclass, field
from common.testreport import TestReport
from datetime import datetime
@@ -23,17 +23,21 @@
class Device():
"""Represents a physical device and it's configuration."""
+ status: str = 'Valid'
folder_url: str = None
mac_addr: str = None
manufacturer: str = None
model: str = None
+ type: str = None
+ technology: str = None
+ test_pack: str = 'Device Qualification'
+ additional_info: List[dict] = field(default_factory=list)
test_modules: Dict = field(default_factory=dict)
ip_addr: str = None
firmware: str = None
device_folder: str = None
reports: List[TestReport] = field(default_factory=list)
max_device_reports: int = None
- reports: List[TestReport] = field(default_factory=list)
def add_report(self, report):
self.reports.append(report)
@@ -54,11 +58,18 @@ def to_dict(self):
"""Returns the device as a python dictionary. This is used for the
system status API endpoint and in the report."""
device_json = {}
+ device_json['status'] = self.status
device_json['mac_addr'] = self.mac_addr
device_json['manufacturer'] = self.manufacturer
device_json['model'] = self.model
+ device_json['type'] = self.type
+ device_json['technology'] = self.technology
+ device_json['test_pack'] = self.test_pack
+ device_json['additional_info'] = self.additional_info
+
if self.firmware is not None:
device_json['firmware'] = self.firmware
+
device_json['test_modules'] = self.test_modules
return device_json
@@ -69,5 +80,9 @@ def to_config_json(self):
device_json['mac_addr'] = self.mac_addr
device_json['manufacturer'] = self.manufacturer
device_json['model'] = self.model
+ device_json['type'] = self.type
+ device_json['technology'] = self.technology
+ device_json['test_pack'] = self.test_pack
device_json['test_modules'] = self.test_modules
+ device_json['additional_info'] = self.additional_info
return device_json
diff --git a/framework/python/src/common/docker_util.py b/framework/python/src/common/docker_util.py
new file mode 100644
index 000000000..06b030419
--- /dev/null
+++ b/framework/python/src/common/docker_util.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.
+"""Utility for common docker methods"""
+import docker
+
+def create_private_net(network_name):
+ client = docker.from_env()
+ try:
+ network = client.networks.get(network_name)
+ network.remove()
+ except docker.errors.NotFound:
+ pass
+
+ # TODO: These should be made into variables
+ ipam_pool = docker.types.IPAMPool(subnet='100.100.0.0/16',
+ iprange='100.100.100.0/24')
+
+ ipam_config = docker.types.IPAMConfig(pool_configs=[ipam_pool])
+
+ client.networks.create(network_name,
+ ipam=ipam_config,
+ internal=True,
+ check_duplicate=True,
+ driver='macvlan')
diff --git a/framework/python/src/common/mqtt.py b/framework/python/src/common/mqtt.py
new file mode 100644
index 000000000..b98b4ab1b
--- /dev/null
+++ b/framework/python/src/common/mqtt.py
@@ -0,0 +1,60 @@
+# Copyright 2024 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.
+
+"""MQTT client"""
+import json
+import typing as t
+import paho.mqtt.client as mqtt_client
+from common import logger
+
+LOGGER = logger.get_logger("mqtt")
+WEBSOCKETS_HOST = "localhost"
+WEBSOCKETS_PORT = 1883
+
+class MQTTException(Exception):
+ def __init__(self, message: str) -> None:
+ super().__init__(message)
+
+
+class MQTT:
+ """ MQTT client class"""
+ def __init__(self) -> None:
+ self._host = WEBSOCKETS_HOST
+ self._client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION2)
+
+ def _connect(self):
+ """Establish connection to MQTT broker"""
+ if not self._client.is_connected():
+ try:
+ self._client.connect(self._host, WEBSOCKETS_PORT, 60)
+ except (ValueError, ConnectionRefusedError):
+ LOGGER.error("Cannot connect to MQTT broker")
+
+ def disconnect(self):
+ """Disconnect the local client from the MQTT broker"""
+ if self._client.is_connected():
+ LOGGER.debug("Disconnecting from broker")
+ self._client.disconnect()
+
+ def send_message(self, topic: str, message: t.Union[str, dict]) -> None:
+ """Send message to specific topic
+
+ Args:
+ topic (str): mqtt topic
+ message (t.Union[str, dict]): message
+ """
+ self._connect()
+ if isinstance(message, dict):
+ message = json.dumps(message)
+ self._client.publish(topic, str(message))
diff --git a/framework/python/src/common/risk_profile.py b/framework/python/src/common/risk_profile.py
index 6afb229ac..f2e684710 100644
--- a/framework/python/src/common/risk_profile.py
+++ b/framework/python/src/common/risk_profile.py
@@ -20,10 +20,16 @@
from common import logger
import json
import os
+from jinja2 import Template
+from copy import deepcopy
+import math
PROFILES_PATH = 'local/risk_profiles'
LOGGER = logger.get_logger('risk_profile')
RESOURCES_DIR = 'resources/report'
+TEMPLATE_FILE = 'risk_report_template.html'
+TEMPLATE_STYLES = 'risk_report_styles.css'
+DEVICE_FORMAT_PATH = 'resources/devices/device_profile.json'
# Locate parent directory
current_dir = os.path.dirname(os.path.realpath(__file__))
@@ -43,6 +49,33 @@ class RiskProfile():
def __init__(self, profile_json=None, profile_format=None):
+ # Jinja template
+ with open(os.path.join(report_resource_dir, TEMPLATE_FILE),
+ 'r',
+ encoding='UTF-8'
+ ) as template_file:
+ self._template = Template(template_file.read())
+ with open(os.path.join(report_resource_dir,
+ TEMPLATE_STYLES),
+ 'r',
+ encoding='UTF-8'
+ ) as style_file:
+ self._template_styles = style_file.read()
+
+ # Device profile format
+ self._device_format = []
+ try:
+ with open(os.path.join(root_dir, DEVICE_FORMAT_PATH),
+ 'r',
+ encoding='utf-8') as device_format_file:
+ device_format_json = json.load(device_format_file)
+ self._device_format = device_format_json
+ except (IOError, ValueError) as e:
+ LOGGER.error(
+ 'An error occurred whilst loading the device profile format')
+ LOGGER.debug(e)
+
+
if profile_json is None or profile_format is None:
return
@@ -92,15 +125,16 @@ def update(self, profile_json, profile_format):
self.risk = new_profile.risk
def get_file_path(self):
+ """Returns the file path for the current risk profile json"""
return os.path.join(PROFILES_PATH,
self.name + '.json')
def _validate(self, profile_json, profile_format):
- if self._valid(profile_json, profile_format):
- if self._expired():
- self.status = 'Expired'
+ if self._expired():
+ self.status = 'Expired'
+ elif self._valid(profile_json, profile_format):
# User only wants to save a draft
- elif 'status' in profile_json and profile_json['status'] == 'Draft':
+ if 'status' in profile_json and profile_json['status'] == 'Draft':
self.status = 'Draft'
else:
self.status = 'Valid'
@@ -108,6 +142,7 @@ def _validate(self, profile_json, profile_format):
self.status = 'Draft'
def update_risk(self, profile_format):
+ """Update the calculated risk for the risk profile"""
if self.status == 'Valid':
@@ -176,6 +211,15 @@ def update_risk(self, profile_format):
self.risk = risk
+ def _update_risk_by_device(self):
+ risk = self.risk
+ if self._device and self.status == 'Valid':
+ for question in self._device.additional_info:
+ if 'risk' in question and question['risk'] == 'High':
+ risk = 'High'
+ break
+ return risk
+
def _get_format_question(self, question: str, profile_format: dict):
for q in profile_format:
@@ -281,6 +325,7 @@ def _expired(self):
return today > expiry_date
def to_json(self, pretty=False):
+ """Returns the current risk profile in JSON format"""
json_dict = {
'name': self.name,
'version': self.version,
@@ -293,356 +338,177 @@ def to_json(self, pretty=False):
return json.dumps(json_dict, indent=indent)
def to_html(self, device):
+ """Returns the current risk profile in HTML format"""
+
+ high_risk_message = '''The device has been assessed to be high
+ risk due to the nature of the answers provided
+ about the device functionality.'''
+ limited_risk_message = '''The device has been assessed to be limited risk
+ due to the nature of the answers provided about
+ the device functionality.'''
+ with open(test_run_img_file, 'rb') as f:
+ logo_img_b64 = base64.b64encode(f.read()).decode('utf-8')
+
+ self._device = self._format_device_profile(device)
+ pages = self._generate_report_pages(device)
+ return self._template.render(
+ styles=self._template_styles,
+ manufacturer=self._device.manufacturer,
+ model=self._device.model,
+ logo=logo_img_b64,
+ risk=self._update_risk_by_device(),
+ high_risk_message=high_risk_message,
+ limited_risk_message=limited_risk_message,
+ pages=pages,
+ total_pages=len(pages),
+ version=self.version,
+ created_at=self.created.strftime('%d.%m.%Y')
+ )
+
+ def to_html_no_device(self):
+ """Returns the risk profile in HTML format without device info"""
+
+ high_risk_message = '''The device has been assessed to be high
+ risk due to the nature of the answers provided
+ about the device functionality.'''
+ limited_risk_message = '''The device has been assessed to be limited risk
+ due to the nature of the answers provided about
+ the device functionality.'''
- self._device = device
-
- return f'''
-
-
- {self._generate_head()}
-
{formatted_key}: {value}
\n' - section_content += '| Requests to local DNS server | -Requests to external DNS servers | -Total DNS requests | -Total DNS responses | -
|---|---|---|---|
| {local_requests} | -{external_requests} | -{total_requests} | -{total_responses} | -
| Source | -Destination | -Type | -URL | -
|---|---|---|---|
| {row['Source']} | -{row['Destination']} | -{row['Type']} | -{row['Data']} | -
| {{ header }} | + {% endfor %} +|||||
|---|---|---|---|---|---|
{{ row['src'] }} |
+ {{ row['dst'] }} |
+ {{ row['res_ip'] }} |
+ {{ row['typ'] }} |
+ {{ row['dat'] }} |
+ {{ row['count'] }} |
+
| Requests to local NTP server | -Requests to external NTP servers | -Total NTP requests | -Total NTP responses | -
|---|---|---|---|
| {local_requests} | -{external_requests} | -{total_requests} | -{total_responses} | -
| Source | -Destination | -Type | -Version | -Timestamp | -
|---|---|---|---|---|
| {row['Source']} | -{row['Destination']} | -{row['Type']} | -{row['Version']} | -{formatted_time} | -
| {{ header }} | + {% endfor %} +|||||
|---|---|---|---|---|---|
| {{ row['src'] }} | +{{ row['dst'] }} | +{{ row['typ'] }} | +{{ row['version'] }} | +{{ row['cnt'] }} | +{{ row['avg_fmt'] }} | +
| TCP ports open | -UDP ports open | -Total ports open | -
|---|---|---|
| {tcp_open} | -{udp_open} | -{tcp_open + udp_open} | -
| Port | -State | -Service | -Version | -
|---|---|---|---|
| {row['Port']}/{row['Type']} | -{row['State']} | -{row['Service']} | -{row['Version']} | -
| {{ header }} | + {% endfor %} +|||
|---|---|---|---|
| {{ row['Port'] }} | +{{ row['State'] }} | +{{ row['Service'] }} | +{{ row['Version'] }} | +
| Expiry | - #Length | - #Type | - #Port number | - #Signed by | - #
|---|
| Expiry | - # #Length | - # #Type | - # #Port number | - # #Signed by | - # #
|---|---|---|---|---|
| {not_after} | - #{cert_length} | - #{public_key_type} | - #{port} | - #{signed_by} | - #
| Destination IP | +Port | +
|---|---|
| {ip} | {port} |
| {{ header }} | + {% endfor %} +|
|---|---|
| {{ k }} | +{{ v }} | +
| {{ header }} | + {% endfor %} +|
|---|---|
| {{ k }} | +{{ v }} | +
| Property | +Value | +
|---|---|
| {{ k }} | +{{ v }} | +
| {{ header }} | + {% endfor %} +|
|---|---|
| {{ip}} | +{{port}} | +
+ {{ device.manufacturer }} +
+{{ device.manufacturer }}
++ {{ device.model }} +
+
- Risk profile is required for device verification. Please, consider creating a
- Risk assessment profile for your ZIP report.
+
+ {{ data.testrunStatus.device.manufacturer }}
+ {{ data.testrunStatus.device.model }}
+ {{ data.testrunStatus.device.firmware }}
+
+ {{ data.testrunStatus.device.test_pack }} testing has just finished
+
+ {{ getTestingResult(data.testrunStatus) }}
+
+ {{ data.testrunStatus.description }}
+
+ Risk Profile is required for device verification. Please consider going to + Risk Assessment + and creating a profile to attach to your report. +
+ +1"> + Risk Profile is required for device verification. Please select a profile from + the list, or go to + Risk Assessment + and create a new one to attach to your report.
-
+
{{ data.content }}
-