From 2b3e22a597cffd02054cc7c8f5129611503a2e1b Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 14:58:41 +0000 Subject: [PATCH 01/52] install jinja2 --- modules/test/base/python/requirements.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/test/base/python/requirements.txt b/modules/test/base/python/requirements.txt index 0ed8a792d..5d12be720 100644 --- a/modules/test/base/python/requirements.txt +++ b/modules/test/base/python/requirements.txt @@ -6,4 +6,7 @@ protobuf==5.28.0 # User defined packages grpcio==1.67.1 grpcio-tools==1.67.1 -netifaces==0.11.0 \ No newline at end of file +netifaces==0.11.0 + +# Requirements for reports generation +Jinja2==3.1.4 \ No newline at end of file From 337552f7b18d6235a2f3898d39b02eb1074dab45 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 15:08:20 +0000 Subject: [PATCH 02/52] base module template --- modules/test/base/base.Dockerfile | 8 +++++++ modules/test/base/python/src/test_module.py | 2 ++ resources/report/module_report_base.jinja2 | 24 +++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 resources/report/module_report_base.jinja2 diff --git a/modules/test/base/base.Dockerfile b/modules/test/base/base.Dockerfile index 253270ea9..7a82301a7 100644 --- a/modules/test/base/base.Dockerfile +++ b/modules/test/base/base.Dockerfile @@ -80,5 +80,13 @@ COPY --from=builder /usr/local/etc/oui.txt /usr/local/etc/oui.txt # Activate the virtual environment by setting the PATH ENV PATH="/opt/venv/bin:$PATH" +# Common resource folder +ENV REPORT_TEMPLATE_PATH=/testrun/resources +# Jinja base template +ENV BASE_TEMPLATE_FILE=module_report_base.jinja2 + +# Copy base template +COPY resources/report/$BASE_TEMPLATE_FILE $REPORT_TEMPLATE_PATH/ + # Start the test module ENTRYPOINT [ "/testrun/bin/start" ] \ No newline at end of file diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 21de78143..73187d789 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -42,6 +42,8 @@ def __init__(self, self._ipv6_subnet = os.environ.get('IPV6_SUBNET', '') self._dev_iface_mac = os.environ.get('DEV_IFACE_MAC', '') self._device_test_pack = json.loads(os.environ.get('DEVICE_TEST_PACK', '')) + self._report_template_folder = os.environ.get('REPORT_TEMPLATE_PATH') + self._base_template_file=os.environ.get('BASE_TEMPLATE_FILE') self._add_logger(log_name=log_name) self._config = self._read_config( conf_file=conf_file if conf_file is not None else CONF_FILE) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 new file mode 100644 index 000000000..4edc56e0b --- /dev/null +++ b/resources/report/module_report_base.jinja2 @@ -0,0 +1,24 @@ +
+ {% if module_header %} +

{{ module_header }}

+ + {% else %} +
+ {% endif %} + + + {% for header in summary_headers %} + + {% endfor %} + + + + + {% for cell in summary_data %} + + {% endfor %} + + +
{{ header }}
{{ cell }}
+ {% block content %}{% endblock content %} +
\ No newline at end of file From 0e0c950f7ed98e2e0c118e9b603c33d60320f2f7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 17:42:08 +0000 Subject: [PATCH 03/52] ntp module template --- modules/test/ntp/ntp.Dockerfile | 5 +++- modules/test/ntp/python/src/ntp_module.py | 24 +++++++++++++++++-- .../test/ntp/resources/report_template.jinja2 | 2 ++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 modules/test/ntp/resources/report_template.jinja2 diff --git a/modules/test/ntp/ntp.Dockerfile b/modules/test/ntp/ntp.Dockerfile index 4d9701464..c7ae7fee1 100644 --- a/modules/test/ntp/ntp.Dockerfile +++ b/modules/test/ntp/ntp.Dockerfile @@ -31,4 +31,7 @@ COPY $MODULE_DIR/conf /testrun/conf COPY $MODULE_DIR/bin /testrun/bin # Copy over all python files -COPY $MODULE_DIR/python /testrun/python \ No newline at end of file +COPY $MODULE_DIR/python /testrun/python + +# Copy Jinja template +COPY $MODULE_DIR/resources/report_template.jinja2 $REPORT_TEMPLATE_PATH/ \ No newline at end of file diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 22fbf40ed..bf64c5891 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -16,6 +16,7 @@ from scapy.all import rdpcap, IP, IPv6, NTP, UDP, Ether import os from collections import defaultdict +from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' MODULE_REPORT_FILE_NAME = 'ntp_report.html' @@ -23,6 +24,7 @@ STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' LOGGER = None +REPORT_TEMPLATE_FILE = 'report_template.jinja2' class NTPModule(TestModule): @@ -49,11 +51,20 @@ def __init__(self, LOGGER = self._get_logger() def generate_module_report(self): + # Load Jinja2 template + loader=FileSystemLoader(self._report_template_folder) + template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + module_header="NTP Module" + summmary_headers = [ + 'Requests to local NTP server', + 'Requests to external NTP servers', + 'Total NTP requests', + 'Total NTP responses' + ] + # Extract NTP data from the pcap file ntp_table_data = self.extract_ntp_data() - html_content = '

NTP Module

' - # Set the summary variables local_requests = sum( 1 for row in ntp_table_data @@ -67,6 +78,15 @@ def generate_module_report(self): total_responses = sum(1 for row in ntp_table_data if row['Type'] == 'Server') + summary_data = [ + local_requests, + external_requests, + total_requests, + total_responses + ] + + html_content = '

NTP Module

' + # Initialize a dictionary to store timestamps for each unique combination timestamps = defaultdict(list) diff --git a/modules/test/ntp/resources/report_template.jinja2 b/modules/test/ntp/resources/report_template.jinja2 new file mode 100644 index 000000000..793b65df2 --- /dev/null +++ b/modules/test/ntp/resources/report_template.jinja2 @@ -0,0 +1,2 @@ +{% extends base_template %} +{% block content %}{% endblock %} \ No newline at end of file From 99fa744311a68476f559652550505cb34e8b6d50 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 18:56:22 +0100 Subject: [PATCH 04/52] add page header and footer to the base_template --- resources/report/module_report_base.jinja2 | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 index 4edc56e0b..009b7646d 100644 --- a/resources/report/module_report_base.jinja2 +++ b/resources/report/module_report_base.jinja2 @@ -1,3 +1,26 @@ +{% raw %} +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+{% endraw %}
{% if module_header %}

{{ module_header }}

@@ -21,4 +44,12 @@ {% block content %}{% endblock content %} -
\ No newline at end of file +
+{% raw %} + + +
+{% endraw %} \ No newline at end of file From 9c100eed0580feb6d4e8a5a4b33cb3d15b0b7ccf Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:00:48 +0100 Subject: [PATCH 05/52] generate ntp module report using Jinja --- modules/test/ntp/python/src/ntp_module.py | 119 ++++++++---------- .../test/ntp/resources/report_template.jinja2 | 31 ++++- 2 files changed, 83 insertions(+), 67 deletions(-) diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index bf64c5891..411776899 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -19,7 +19,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' -MODULE_REPORT_FILE_NAME = 'ntp_report.html' +MODULE_REPORT_FILE_NAME = 'ntp_report.jinja2' NTP_SERVER_CAPTURE_FILE = '/runtime/network/ntp.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' @@ -52,16 +52,30 @@ def __init__(self, def generate_module_report(self): # Load Jinja2 template + page_max_height = 910 + header_height = 48 + summary_height = 135 + row_height = 42 loader=FileSystemLoader(self._report_template_folder) template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) - module_header="NTP Module" - summmary_headers = [ + module_header='NTP Module' + # Summary table headers + summary_headers = [ 'Requests to local NTP server', 'Requests to external NTP servers', 'Total NTP requests', 'Total NTP responses' ] - + # Module data Headers + module_data_headers = [ + 'Source', + 'Destination', + 'Type', + 'Version', + 'Count', + 'Sync Request Average', + ] + # Extract NTP data from the pcap file ntp_table_data = self.extract_ntp_data() @@ -78,14 +92,13 @@ def generate_module_report(self): total_responses = sum(1 for row in ntp_table_data if row['Type'] == 'Server') + # Summary table data summary_data = [ local_requests, external_requests, total_requests, total_responses ] - - html_content = '

NTP Module

' # Initialize a dictionary to store timestamps for each unique combination timestamps = defaultdict(list) @@ -114,42 +127,9 @@ def generate_module_report(self): average_time_between_requests[key] = avg_diff - # Add summary table - html_content += (f''' - - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
{local_requests}{external_requests}{total_requests}{total_responses}
- ''') - + # Module table data + module_table_data = [] if total_requests + total_responses > 0: - table_content = ''' - - - - - - - - - - - - ''' # Generate the HTML table with the count column for (src, dst, typ, @@ -164,37 +144,44 @@ def generate_module_report(self): else: avg_formatted_time = 'N/A' - table_content += f''' - - - - - - - - ''' - - table_content += ''' - -
SourceDestinationTypeVersionCountSync Request Average
{src}{dst}{typ}{version}{cnt}{avg_formatted_time}
- ''' - html_content += table_content - - else: - html_content += (''' -
-
- No NTP traffic detected from the device -
''') - - LOGGER.debug('Module report:\n' + html_content) + module_table_data.append({ + 'src': src, + 'dst': dst, + 'typ': typ, + 'version': version, + 'cnt': cnt, + 'avg_fmt': avg_formatted_time + }) + + # Handling the possible table split + table_height = (len(module_table_data) + 1) * row_height + page_useful_space = page_max_height - header_height - summary_height + pages = table_height // (page_useful_space) + rows_on_page = ((page_useful_space) // row_height) - 1 + start = 0 + report_html = '' + for page in range(pages+1): + end = start + min(len(module_table_data), rows_on_page) + module_header_repr = module_header if page == 0 else None + page_html = template.render( + base_template=self._base_template_file, + module_header=module_header_repr, + summary_headers=summary_headers, + summary_data=summary_data, + module_data_headers=module_data_headers, + module_data=module_table_data[start:end] + ) + report_html += page_html + start = end + + LOGGER.debug('Module report:\n' + report_html) # Use os.path.join to create the complete file path report_path = os.path.join(self._results_dir, MODULE_REPORT_FILE_NAME) # Write the content to a file with open(report_path, 'w', encoding='utf-8') as file: - file.write(html_content) + file.write(report_html) LOGGER.info('Module report generated at: ' + str(report_path)) diff --git a/modules/test/ntp/resources/report_template.jinja2 b/modules/test/ntp/resources/report_template.jinja2 index 793b65df2..c3601b67b 100644 --- a/modules/test/ntp/resources/report_template.jinja2 +++ b/modules/test/ntp/resources/report_template.jinja2 @@ -1,2 +1,31 @@ {% extends base_template %} -{% block content %}{% endblock %} \ No newline at end of file +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + + + {% endfor %} + +
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
+{% else %} +
+
+ No NTP traffic detected from the device +
+{% endif %} +{% endblock %} \ No newline at end of file From 6756d23309f6baa5d827496e9344461738009161 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:03:05 +0100 Subject: [PATCH 06/52] refactor adding modules reports to the main report --- framework/python/src/common/testreport.py | 47 +++++++++++++------ framework/python/src/core/session.py | 10 ++++ .../python/src/test_orc/test_orchestrator.py | 14 +++++- modules/test/base/python/src/test_module.py | 1 + resources/report/test_report_template.html | 20 ++++---- 5 files changed, 66 insertions(+), 26 deletions(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index 6acc6eac0..4507096a9 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -16,12 +16,12 @@ from datetime import datetime from weasyprint import HTML from io import BytesIO -from common import util +from common import util, logger from common.statuses import TestrunStatus import base64 import os from test_orc.test_case import TestCase -from jinja2 import Environment, FileSystemLoader +from jinja2 import Environment, FileSystemLoader, BaseLoader from collections import OrderedDict import re from bs4 import BeautifulSoup @@ -34,6 +34,8 @@ TEST_REPORT_STYLES = 'test_report_styles.css' TEST_REPORT_TEMPLATE = 'test_report_template.html' +LOGGER = logger.get_logger('REPORT') + # Locate parent directory current_dir = os.path.dirname(os.path.realpath(__file__)) @@ -65,12 +67,16 @@ def __init__(self, self._total_tests = total_tests self._results = [] self._module_reports = [] + self._module_templates = [] self._report_url = '' self._cur_page = 0 def add_module_reports(self, module_reports): self._module_reports = module_reports + def add_module_templates(self, module_templates): + self._module_templates = module_templates + def get_status(self): return self._status @@ -243,16 +249,20 @@ def to_html(self): optional_steps_to_resolve = self._get_optional_steps_to_resolve(json_data) module_reports = self._get_module_pages() + env_module = Environment(loader=BaseLoader()) pages_num = self._pages_num(json_data) - total_pages = pages_num + len(module_reports) + 1 - if len(steps_to_resolve) > 0: - total_pages += 1 - if (len(optional_steps_to_resolve) > 0 - and json_data['device']['test_pack'] == 'Pilot Assessment' - ): - total_pages += 1 - - return template.render(styles=styles, + module_templates = [ + env_module.from_string(s).render( + json_data=json_data, + device=json_data['device'], + logo=logo, + icon_qualification=icon_qualification, + icon_pilot=icon_pilot, + version=self._version, + ) for s in self._module_templates + ] + + return self._add_page_counter(template.render(styles=styles, logo=logo, icon_qualification=icon_qualification, icon_pilot=icon_pilot, @@ -269,10 +279,19 @@ def to_html(self): optional_steps_to_resolve=optional_steps_to_resolve, module_reports=module_reports, pages_num=pages_num, - total_pages=total_pages, tests_first_page=TESTS_FIRST_PAGE, tests_per_page=TESTS_PER_PAGE, - ) + module_templates=module_templates + )) + + def _add_page_counter(self, html): + # Add page nums and total page + soup = BeautifulSoup(html, features='html5lib') + page_index_divs = soup.find_all('div', class_='page-index') + total_pages = len(page_index_divs) + for index, div in enumerate(page_index_divs): + div.string = f'Page {index+1}/{total_pages}' + return soup.prettify() def _pages_num(self, json_data): @@ -391,7 +410,7 @@ def _get_module_pages(self): if el.name == 'h1': current_size += 40 + h1_padding # Calculating the height of paired tables - elif (el.name == 'div' + elif (el.name == 'div' and el.has_attr('style') and el['style'] == 'display:flex;justify-content:space-between;'): tables = el.findChildren('table', recursive=True) current_size = max( diff --git a/framework/python/src/core/session.py b/framework/python/src/core/session.py index f2e5466d3..29a841c55 100644 --- a/framework/python/src/core/session.py +++ b/framework/python/src/core/session.py @@ -100,6 +100,9 @@ def __init__(self, root_dir): # All historical reports self._module_reports = [] + # Module report templates + self._module_templates = [] + # Parameters specified when starting Testrun self._runtime_params = [] @@ -398,6 +401,9 @@ def get_test_results(self): def get_module_reports(self): return self._module_reports + def get_module_templates(self): + return self._module_templates + def get_report_tests(self): """Returns the current test results in JSON-friendly format (in Python dictionary)""" @@ -465,6 +471,9 @@ def set_test_result_error(self, result): def add_module_report(self, module_report): self._module_reports.append(module_report) + def add_module_template(self, module_template): + self._module_templates.append(module_template) + def get_all_reports(self): reports = [] @@ -789,6 +798,7 @@ def reset(self): self._report_url = None self._total_tests = 0 self._module_reports = [] + self._module_templates = [] self._results = [] self._started = None self._finished = None diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index d133fefd9..13338f24f 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -177,6 +177,7 @@ def run_test_modules(self): generated_report_json = self._generate_report() report.from_json(generated_report_json) report.add_module_reports(self.get_session().get_module_reports()) + report.add_module_templates(self.get_session().get_module_templates()) device.add_report(report) self._write_reports(report) @@ -549,8 +550,19 @@ def _run_test_module(self, module): self.get_session().add_module_report(module_report) except (FileNotFoundError, PermissionError): LOGGER.debug("Test module did not produce a html module report") + # Get the Jinja report + jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" + try: + import shutil + shutil.copy2(jinja_file, '/home/user/projects/testrun/runtime/example.jinja2') + with open(jinja_file, "r", encoding="utf-8") as f: + module_template = f.read() + LOGGER.debug(f"Adding module template for module {module.name}") + self.get_session().add_module_template(module_template) + except (FileNotFoundError, PermissionError): + LOGGER.debug("Test module did not produce a module template") - LOGGER.info(f"Test module {module.name} has finished") + # LOGGER.info(f"Test module {module.name} has finished") def _get_container_logs(self, log_stream): """Resolve all current log data in the containers log_stream diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 73187d789..8d59f975e 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -18,6 +18,7 @@ import util from datetime import datetime import traceback +import re from common.statuses import TestResult diff --git a/resources/report/test_report_template.html b/resources/report/test_report_template.html index fbd8d1c68..0d08f65ea 100644 --- a/resources/report/test_report_template.html +++ b/resources/report/test_report_template.html @@ -10,10 +10,8 @@ - {% set page_index = namespace(value=0) %} {# Test Results #} {% for page in range(pages_num) %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(loop.first, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}} {% if loop.first %} @@ -105,14 +103,13 @@

Results List ({{ successful_tests }}/{{ tot

{% endfor %} {# Steps to resolve Device qualification #} {% if steps_to_resolve|length > 0 and json_data['device']['test_pack'] == 'Device Qualification' %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Non-compliant tests and suggested steps to resolve

@@ -137,13 +134,16 @@

Non-compliant tests and suggested steps to resolve

{% endfor %}
{% endif %} + {# Modules reports Jinja #} + {% for template in module_templates %} + {{ template }} + {% endfor %} {# Modules reports #} {% for module in module_reports %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}
@@ -151,13 +151,12 @@

Non-compliant tests and suggested steps to resolve

{% endfor %} {# Device profile #} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Device profile

@@ -186,13 +185,12 @@

Device profile

{# Pilot steps to resolve#} {% if json_data['device']['test_pack'] == 'Pilot Assessment' and optional_steps_to_resolve|length > 0 %} - {% set page_index.value = page_index.value + 1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Recommendations for Device Qualification

@@ -233,7 +231,7 @@

Recommendations for Device Qualification

{% endfor %}
{% endif %} From fb30203f497289b606ff3a394796386abd89e80b Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:18:59 +0100 Subject: [PATCH 07/52] pylint --- framework/python/src/core/session.py | 4 ++-- framework/python/src/test_orc/test_orchestrator.py | 2 -- modules/test/base/python/src/test_module.py | 7 +++---- modules/test/ntp/python/src/ntp_module.py | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/framework/python/src/core/session.py b/framework/python/src/core/session.py index 29a841c55..ffbb9d26f 100644 --- a/framework/python/src/core/session.py +++ b/framework/python/src/core/session.py @@ -667,7 +667,7 @@ def _remove_invalid_questions(self, questions): valid_questions.append(question) else: - LOGGER.debug(f'Removed unrecognised question: {question["question"]}') + LOGGER.debug(f'Removed unrecognised question: {question["question"]}') # pylint: disable=W1405 # Return the list of valid questions return valid_questions @@ -713,7 +713,7 @@ def validate_profile_json(self, profile_json): question.get('question')) if format_q is None: - LOGGER.error(f'Unrecognised question: {question.get("question")}') + LOGGER.error(f'Unrecognised question: {question.get("question")}') # pylint: disable=W1405 # Just ignore additional questions continue diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 13338f24f..8309a48d1 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -553,8 +553,6 @@ def _run_test_module(self, module): # Get the Jinja report jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" try: - import shutil - shutil.copy2(jinja_file, '/home/user/projects/testrun/runtime/example.jinja2') with open(jinja_file, "r", encoding="utf-8") as f: module_template = f.read() LOGGER.debug(f"Adding module template for module {module.name}") diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 8d59f975e..42ee3ff85 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -18,7 +18,6 @@ import util from datetime import datetime import traceback -import re from common.statuses import TestResult @@ -140,14 +139,14 @@ def run_tests(self): else: result = getattr(self, test_method_name)() except Exception as e: # pylint: disable=W0718 - LOGGER.error(f'An error occurred whilst running {test["name"]}') + LOGGER.error(f'An error occurred whilst running {test["name"]}') # pylint: disable=W1405 LOGGER.error(e) traceback.print_exc() else: - LOGGER.error(f'Test {test["name"]} has not been implemented') + LOGGER.error(f'Test {test["name"]} has not been implemented') # pylint: disable=W1405 result = TestResult.ERROR, 'This test could not be found' else: - LOGGER.debug(f'Test {test["name"]} is disabled') + LOGGER.debug(f'Test {test["name"]} is disabled') # pylint: disable=W1405 result = (TestResult.DISABLED, 'This test did not run because it is disabled') diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 411776899..17f63960a 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -30,7 +30,7 @@ class NTPModule(TestModule): """NTP Test module""" - def __init__(self, + def __init__(self, # pylint: disable=R0917 module, conf_file=None, results_dir=None, From d87f567fcf59d630a9cd35cb45751a0939d0e1d7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Sat, 14 Dec 2024 18:03:18 +0100 Subject: [PATCH 08/52] generating module report using jinja --- .../services/python/src/services_module.py | 101 ++++++++---------- .../services/resources/report_template.jinja2 | 29 +++++ modules/test/services/services.Dockerfile | 5 +- 3 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 modules/test/services/resources/report_template.jinja2 diff --git a/modules/test/services/python/src/services_module.py b/modules/test/services/python/src/services_module.py index 1a783e7dc..b059d02b0 100644 --- a/modules/test/services/python/src/services_module.py +++ b/modules/test/services/python/src/services_module.py @@ -19,17 +19,19 @@ import xmltodict from test_module import TestModule import os +from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_services' -MODULE_REPORT_FILE_NAME = 'services_report.html' +MODULE_REPORT_FILE_NAME = 'services_report.jinja2' NMAP_SCAN_RESULTS_SCAN_FILE = 'services_scan_results.json' LOGGER = None +REPORT_TEMPLATE_FILE = 'report_template.jinja2' class ServicesModule(TestModule): """Services Test module""" - def __init__(self, + def __init__(self, # pylint: disable=R0917 module, conf_file=None, results_dir=None, @@ -52,6 +54,22 @@ def __init__(self, self._run_nmap() def generate_module_report(self): + # Load Jinja2 template + loader=FileSystemLoader(self._report_template_folder) + template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + module_header = 'Services Module' + summary_headers = [ + 'TCP ports open', + 'UDP ports open', + 'Total ports open', + ] + module_data_headers = [ + 'Port', + 'State', + 'Service', + 'Version', + ] + # Use os.path.join to create the complete file path nmap_scan_results_file = os.path.join(self._nmap_scan_results_path, NMAP_SCAN_RESULTS_SCAN_FILE) @@ -81,65 +99,32 @@ def generate_module_report(self): else: udp_open += 1 - html_content = '

Services Module

' - - # Add summary table - html_content += (f''' - - - - - - - - - - - - - - -
TCP ports openUDP ports openTotal ports open
{tcp_open}{udp_open}{tcp_open + udp_open}
- ''') + summary_data = [ + tcp_open, + udp_open, + tcp_open + udp_open, + ] + module_data = [] if (tcp_open + udp_open) > 0: - - table_content = ''' - - - - - - - - - - ''' - for row in nmap_table_data: - - table_content += (f''' - - - - - - ''') - - table_content += ''' - -
PortStateServiceVersion
{row['Port']}/{row['Type']}{row['State']}{row['Service']}{row['Version']}
- ''' - - html_content += table_content - - else: - - html_content += (''' -
-
- No open ports detected -
''') + port = row['Port'] + type_ = row['Type'] + module_data.append({ + 'Port': f'{port}/{type_}', + 'State': row['State'], + 'Service': row['Service'], + 'Version': row['Version'], + }) + + html_content = template.render( + base_template=self._base_template_file, + module_header=module_header, + summary_headers=summary_headers, + summary_data=summary_data, + module_data_headers=module_data_headers, + module_data=module_data, + ) LOGGER.debug('Module report:\n' + html_content) diff --git a/modules/test/services/resources/report_template.jinja2 b/modules/test/services/resources/report_template.jinja2 new file mode 100644 index 000000000..cb42385fc --- /dev/null +++ b/modules/test/services/resources/report_template.jinja2 @@ -0,0 +1,29 @@ +{% extends base_template %} +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + {% endfor %} + +
{{ header }}
{{ row['Port'] }}{{ row['State'] }}{{ row['Service'] }}{{ row['Version'] }}
+{% else %} +
+
+ No open ports detected +
+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/modules/test/services/services.Dockerfile b/modules/test/services/services.Dockerfile index 8dcaafcc1..1277c2f8d 100644 --- a/modules/test/services/services.Dockerfile +++ b/modules/test/services/services.Dockerfile @@ -31,4 +31,7 @@ COPY $MODULE_DIR/conf /testrun/conf COPY $MODULE_DIR/bin /testrun/bin # Copy over all python files -COPY $MODULE_DIR/python /testrun/python \ No newline at end of file +COPY $MODULE_DIR/python /testrun/python + +# Copy Jinja template +COPY $MODULE_DIR/resources/report_template.jinja2 $REPORT_TEMPLATE_PATH/ \ No newline at end of file From 098538123db6899b530aa5f342939aacd7beca8f Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Tue, 17 Dec 2024 21:49:59 +0100 Subject: [PATCH 09/52] rename jinja report --- framework/python/src/test_orc/test_orchestrator.py | 2 +- modules/test/ntp/python/src/ntp_module.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 8309a48d1..787de08e6 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -551,7 +551,7 @@ def _run_test_module(self, module): except (FileNotFoundError, PermissionError): LOGGER.debug("Test module did not produce a html module report") # Get the Jinja report - jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" + jinja_file = f"{module.container_runtime_dir}/{module.name}_report.j2.html" try: with open(jinja_file, "r", encoding="utf-8") as f: module_template = f.read() diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 17f63960a..6a64b101c 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -19,7 +19,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' -MODULE_REPORT_FILE_NAME = 'ntp_report.jinja2' +MODULE_REPORT_FILE_NAME = 'ntp_report.j2.html' NTP_SERVER_CAPTURE_FILE = '/runtime/network/ntp.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' From 6c666e4c81a80f50e44c5bbdec18f41c142867b7 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:14:46 +0100 Subject: [PATCH 10/52] unit tests --- testing/unit/ntp/ntp_module_test.py | 13 +- .../unit/ntp/reports/ntp_report_local.html | 294 +++++++++++------- .../ntp/reports/ntp_report_local_no_ntp.html | 95 ++++-- .../ntp/templates/module_report_base.jinja2 | 55 ++++ .../unit/ntp/templates/report_template.jinja2 | 31 ++ 5 files changed, 346 insertions(+), 142 deletions(-) create mode 100644 testing/unit/ntp/templates/module_report_base.jinja2 create mode 100644 testing/unit/ntp/templates/report_template.jinja2 diff --git a/testing/unit/ntp/ntp_module_test.py b/testing/unit/ntp/ntp_module_test.py index 52bd32aa9..1dda0340e 100644 --- a/testing/unit/ntp/ntp_module_test.py +++ b/testing/unit/ntp/ntp_module_test.py @@ -26,6 +26,10 @@ OUTPUT_DIR = os.path.join(TEST_FILES_DIR,'output/') REPORTS_DIR = os.path.join(TEST_FILES_DIR,'reports/') CAPTURES_DIR = os.path.join(TEST_FILES_DIR,'captures/') +MODULE_FILES_DIR = 'modules/test/' + MODULE +TEMPLATE_DIR = os.path.join(TEST_FILES_DIR, 'templates') +TEMPLATE_FILE = 'report_template.jinja2' +BASE_TEMPLATE_FILE = 'module_report_base.jinja2' LOCAL_REPORT = os.path.join(REPORTS_DIR,'ntp_report_local.html') LOCAL_REPORT_NO_NTP = os.path.join(REPORTS_DIR,'ntp_report_local_no_ntp.html') @@ -52,6 +56,8 @@ def ntp_module_report_test(self): ntp_server_capture_file=NTP_SERVER_CAPTURE_FILE, startup_capture_file=STARTUP_CAPTURE_FILE, monitor_capture_file=MONITOR_CAPTURE_FILE) + setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) + setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) report_out_path = ntp_module.generate_module_report() @@ -67,7 +73,6 @@ def ntp_module_report_test(self): new_report_name = 'ntp_local.html' new_report_path = os.path.join(OUTPUT_DIR, new_report_name) shutil.copy(report_out_path, new_report_path) - self.assertEqual(report_out, report_local) # Test the module report generation if no DNS traffic @@ -100,12 +105,15 @@ def ntp_module_report_no_ntp_test(self): wrpcap(startup_cap_file, packets_startup) wrpcap(monitor_cap_file, packets_monitor) - ntp_module = NTPModule(module='dns', + ntp_module = NTPModule(module=MODULE, results_dir=OUTPUT_DIR, ntp_server_capture_file=ntp_server_cap_file, startup_capture_file=startup_cap_file, monitor_capture_file=monitor_cap_file) + setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) + setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) + report_out_path = ntp_module.generate_module_report() # Read the generated report @@ -136,4 +144,3 @@ def ntp_module_report_no_ntp_test(self): if not test_result.wasSuccessful(): sys.exit(1) # Return a non-zero exit code for failures sys.exit(0) # Return zero for success - \ No newline at end of file diff --git a/testing/unit/ntp/reports/ntp_report_local.html b/testing/unit/ntp/reports/ntp_report_local.html index 1fe5e3f3a..89e736e17 100644 --- a/testing/unit/ntp/reports/ntp_report_local.html +++ b/testing/unit/ntp/reports/ntp_report_local.html @@ -1,115 +1,179 @@ -

NTP Module

- - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SourceDestinationTypeVersionCountSync Request Average
10.10.10.15216.239.35.12Client4837.942 seconds
216.239.35.1210.10.10.15Server48N/A
10.10.10.15216.239.35.4Client4837.834 seconds
216.239.35.410.10.10.15Server48N/A
10.10.10.15216.239.35.8Client4838.056 seconds
216.239.35.810.10.10.15Server48N/A
10.10.10.15216.239.35.0Client41420.601 seconds
216.239.35.010.10.10.15Server417N/A
10.10.10.1510.10.10.5Client46313.057 seconds
10.10.10.510.10.10.15Server463N/A
- \ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

NTP Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SourceDestinationTypeVersionCountSync Request Average
10.10.10.15216.239.35.12Client4837.942 seconds
216.239.35.1210.10.10.15Server48N/A
10.10.10.15216.239.35.4Client4837.834 seconds
216.239.35.410.10.10.15Server48N/A
10.10.10.15216.239.35.8Client4838.056 seconds
216.239.35.810.10.10.15Server48N/A
10.10.10.15216.239.35.0Client41420.601 seconds
216.239.35.010.10.10.15Server417N/A
10.10.10.1510.10.10.5Client46313.057 seconds
10.10.10.510.10.10.15Server463N/A
+ + +
+ + +
+
diff --git a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html index c93ab885f..abe616a38 100644 --- a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html +++ b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html @@ -1,24 +1,71 @@ -

NTP Module

- - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
- -
-
- No NTP traffic detected from the device -
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

NTP Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
+ + +
+
+ No NTP traffic detected from the device +
+ + +
+ + +
+
diff --git a/testing/unit/ntp/templates/module_report_base.jinja2 b/testing/unit/ntp/templates/module_report_base.jinja2 new file mode 100644 index 000000000..009b7646d --- /dev/null +++ b/testing/unit/ntp/templates/module_report_base.jinja2 @@ -0,0 +1,55 @@ +{% raw %} +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+{% endraw %} +
+ {% if module_header %} +

{{ module_header }}

+ + {% else %} +
+ {% endif %} + + + {% for header in summary_headers %} + + {% endfor %} + + + + + {% for cell in summary_data %} + + {% endfor %} + + +
{{ header }}
{{ cell }}
+ {% block content %}{% endblock content %} +
+{% raw %} + +
+
+{% endraw %} \ No newline at end of file diff --git a/testing/unit/ntp/templates/report_template.jinja2 b/testing/unit/ntp/templates/report_template.jinja2 new file mode 100644 index 000000000..c3601b67b --- /dev/null +++ b/testing/unit/ntp/templates/report_template.jinja2 @@ -0,0 +1,31 @@ +{% extends base_template %} +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + + + {% endfor %} + +
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
+{% else %} +
+
+ No NTP traffic detected from the device +
+{% endif %} +{% endblock %} \ No newline at end of file From 83d1c92ea2ecea3b11598e3b00dc646e641eb8bd Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:50:29 +0100 Subject: [PATCH 11/52] remove unused mocks --- testing/unit/ntp/ntp_module_test.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/testing/unit/ntp/ntp_module_test.py b/testing/unit/ntp/ntp_module_test.py index 1dda0340e..3b865d531 100644 --- a/testing/unit/ntp/ntp_module_test.py +++ b/testing/unit/ntp/ntp_module_test.py @@ -27,9 +27,6 @@ REPORTS_DIR = os.path.join(TEST_FILES_DIR,'reports/') CAPTURES_DIR = os.path.join(TEST_FILES_DIR,'captures/') MODULE_FILES_DIR = 'modules/test/' + MODULE -TEMPLATE_DIR = os.path.join(TEST_FILES_DIR, 'templates') -TEMPLATE_FILE = 'report_template.jinja2' -BASE_TEMPLATE_FILE = 'module_report_base.jinja2' LOCAL_REPORT = os.path.join(REPORTS_DIR,'ntp_report_local.html') LOCAL_REPORT_NO_NTP = os.path.join(REPORTS_DIR,'ntp_report_local_no_ntp.html') @@ -56,8 +53,6 @@ def ntp_module_report_test(self): ntp_server_capture_file=NTP_SERVER_CAPTURE_FILE, startup_capture_file=STARTUP_CAPTURE_FILE, monitor_capture_file=MONITOR_CAPTURE_FILE) - setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) - setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) report_out_path = ntp_module.generate_module_report() @@ -111,9 +106,6 @@ def ntp_module_report_no_ntp_test(self): startup_capture_file=startup_cap_file, monitor_capture_file=monitor_cap_file) - setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) - setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) - report_out_path = ntp_module.generate_module_report() # Read the generated report From 831e5c055464c5d21361657dde44360bd169c543 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:57:41 +0100 Subject: [PATCH 12/52] delete unused templates --- .../ntp/templates/module_report_base.jinja2 | 55 ------------------- .../unit/ntp/templates/report_template.jinja2 | 31 ----------- 2 files changed, 86 deletions(-) delete mode 100644 testing/unit/ntp/templates/module_report_base.jinja2 delete mode 100644 testing/unit/ntp/templates/report_template.jinja2 diff --git a/testing/unit/ntp/templates/module_report_base.jinja2 b/testing/unit/ntp/templates/module_report_base.jinja2 deleted file mode 100644 index 009b7646d..000000000 --- a/testing/unit/ntp/templates/module_report_base.jinja2 +++ /dev/null @@ -1,55 +0,0 @@ -{% raw %} -
-
-
- {# Badge #} -

- {% if json_data['device']['test_pack'] == 'Device Qualification' %} - - Device Qualification - {% else %} - - Pilot Assessment - {% endif %} -

- {{ title }} -
- - {{ device['manufacturer'] }} - {{ device['model']}} - - Testrun -
-{% endraw %} -
- {% if module_header %} -

{{ module_header }}

- - {% else %} -
- {% endif %} - - - {% for header in summary_headers %} - - {% endfor %} - - - - - {% for cell in summary_data %} - - {% endfor %} - - -
{{ header }}
{{ cell }}
- {% block content %}{% endblock content %} -
-{% raw %} - -
-
-{% endraw %} \ No newline at end of file diff --git a/testing/unit/ntp/templates/report_template.jinja2 b/testing/unit/ntp/templates/report_template.jinja2 deleted file mode 100644 index c3601b67b..000000000 --- a/testing/unit/ntp/templates/report_template.jinja2 +++ /dev/null @@ -1,31 +0,0 @@ -{% extends base_template %} -{% block content %} -{% if module_data %} - - - - {% for header in module_data_headers %} - - {% endfor %} - - - - {% for row in module_data %} - - - - - - - - - {% endfor %} - -
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
-{% else %} -
-
- No NTP traffic detected from the device -
-{% endif %} -{% endblock %} \ No newline at end of file From 524aedb97b97a931b549e43373b6c5f01b8986fe Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 02:04:34 +0100 Subject: [PATCH 13/52] change style to id for paired tables --- framework/python/src/common/testreport.py | 4 ++-- modules/test/tls/python/src/tls_module.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index 4507096a9..df31a5441 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -410,8 +410,8 @@ def _get_module_pages(self): if el.name == 'h1': current_size += 40 + h1_padding # Calculating the height of paired tables - elif (el.name == 'div' and el.has_attr('style') - and el['style'] == 'display:flex;justify-content:space-between;'): + elif (el.name == 'div' and el.has_attr('id') + and el['id'] == 'paired'): tables = el.findChildren('table', recursive=True) current_size = max( map(lambda t: len( diff --git a/modules/test/tls/python/src/tls_module.py b/modules/test/tls/python/src/tls_module.py index 1405ad31c..ca7bed260 100644 --- a/modules/test/tls/python/src/tls_module.py +++ b/modules/test/tls/python/src/tls_module.py @@ -229,7 +229,7 @@ def generate_module_report(self): summary_table = f'\n{summary_table}' summary_table += f''' -
+
Certificate Information
{cert_table} From daa69ad6768dc2d623d7c985d2ee0c3635eb39f5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 02:32:35 +0100 Subject: [PATCH 14/52] fix tls unit tests --- testing/unit/tls/reports/tls_report_ext_local.html | 4 ++-- testing/unit/tls/reports/tls_report_local.html | 8 ++++---- testing/unit/tls/reports/tls_report_single.html | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/testing/unit/tls/reports/tls_report_ext_local.html b/testing/unit/tls/reports/tls_report_ext_local.html index 7e691f66a..bde424a0e 100644 --- a/testing/unit/tls/reports/tls_report_ext_local.html +++ b/testing/unit/tls/reports/tls_report_ext_local.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -121,7 +121,7 @@
Certificate Extensions
-
+
Certificate Information
diff --git a/testing/unit/tls/reports/tls_report_local.html b/testing/unit/tls/reports/tls_report_local.html index 72b2e5a1a..4d1af0404 100644 --- a/testing/unit/tls/reports/tls_report_local.html +++ b/testing/unit/tls/reports/tls_report_local.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -151,7 +151,7 @@
Certificate Extensions
-
+
Certificate Information
@@ -281,7 +281,7 @@
Certificate Extensions
-
+
Certificate Information
@@ -411,7 +411,7 @@
Certificate Extensions
-
+
Certificate Information
diff --git a/testing/unit/tls/reports/tls_report_single.html b/testing/unit/tls/reports/tls_report_single.html index 36adffb94..0f6a5a4d6 100644 --- a/testing/unit/tls/reports/tls_report_single.html +++ b/testing/unit/tls/reports/tls_report_single.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -125,7 +125,7 @@
Subject Information
-
+
Certificate Information
From af2d320f660992f5ecb5bee7f473c4ad019d6cd2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 11:52:55 +0100 Subject: [PATCH 15/52] table width --- modules/test/services/resources/report_template.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/services/resources/report_template.jinja2 b/modules/test/services/resources/report_template.jinja2 index cb42385fc..c9b1438ae 100644 --- a/modules/test/services/resources/report_template.jinja2 +++ b/modules/test/services/resources/report_template.jinja2 @@ -1,7 +1,7 @@ {% extends base_template %} {% block content %} {% if module_data %} - +
{% for header in module_data_headers %} From 0ad5b2ee3afdb268295b43d278c47c76cc6704c8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 11:53:10 +0100 Subject: [PATCH 16/52] rename report file --- modules/test/services/python/src/services_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/services/python/src/services_module.py b/modules/test/services/python/src/services_module.py index b059d02b0..b6787e124 100644 --- a/modules/test/services/python/src/services_module.py +++ b/modules/test/services/python/src/services_module.py @@ -22,7 +22,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_services' -MODULE_REPORT_FILE_NAME = 'services_report.jinja2' +MODULE_REPORT_FILE_NAME = 'services_report.j2.html' NMAP_SCAN_RESULTS_SCAN_FILE = 'services_scan_results.json' LOGGER = None REPORT_TEMPLATE_FILE = 'report_template.jinja2' From f1ae16da4a6748bcd36df5d66ed506ecbb3ea765 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 14:58:41 +0000 Subject: [PATCH 17/52] install jinja2 --- modules/test/base/python/requirements.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/test/base/python/requirements.txt b/modules/test/base/python/requirements.txt index 0ed8a792d..5d12be720 100644 --- a/modules/test/base/python/requirements.txt +++ b/modules/test/base/python/requirements.txt @@ -6,4 +6,7 @@ protobuf==5.28.0 # User defined packages grpcio==1.67.1 grpcio-tools==1.67.1 -netifaces==0.11.0 \ No newline at end of file +netifaces==0.11.0 + +# Requirements for reports generation +Jinja2==3.1.4 \ No newline at end of file From 44cbee6d2248cce9aa1075cc6c553df1a6ccbf9b Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 15:08:20 +0000 Subject: [PATCH 18/52] base module template --- modules/test/base/base.Dockerfile | 8 +++++++ modules/test/base/python/src/test_module.py | 2 ++ resources/report/module_report_base.jinja2 | 24 +++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 resources/report/module_report_base.jinja2 diff --git a/modules/test/base/base.Dockerfile b/modules/test/base/base.Dockerfile index 253270ea9..7a82301a7 100644 --- a/modules/test/base/base.Dockerfile +++ b/modules/test/base/base.Dockerfile @@ -80,5 +80,13 @@ COPY --from=builder /usr/local/etc/oui.txt /usr/local/etc/oui.txt # Activate the virtual environment by setting the PATH ENV PATH="/opt/venv/bin:$PATH" +# Common resource folder +ENV REPORT_TEMPLATE_PATH=/testrun/resources +# Jinja base template +ENV BASE_TEMPLATE_FILE=module_report_base.jinja2 + +# Copy base template +COPY resources/report/$BASE_TEMPLATE_FILE $REPORT_TEMPLATE_PATH/ + # Start the test module ENTRYPOINT [ "/testrun/bin/start" ] \ No newline at end of file diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 21de78143..73187d789 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -42,6 +42,8 @@ def __init__(self, self._ipv6_subnet = os.environ.get('IPV6_SUBNET', '') self._dev_iface_mac = os.environ.get('DEV_IFACE_MAC', '') self._device_test_pack = json.loads(os.environ.get('DEVICE_TEST_PACK', '')) + self._report_template_folder = os.environ.get('REPORT_TEMPLATE_PATH') + self._base_template_file=os.environ.get('BASE_TEMPLATE_FILE') self._add_logger(log_name=log_name) self._config = self._read_config( conf_file=conf_file if conf_file is not None else CONF_FILE) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 new file mode 100644 index 000000000..4edc56e0b --- /dev/null +++ b/resources/report/module_report_base.jinja2 @@ -0,0 +1,24 @@ +
+ {% if module_header %} +

{{ module_header }}

+
+ {% else %} +
+ {% endif %} + + + {% for header in summary_headers %} + + {% endfor %} + + + + + {% for cell in summary_data %} + + {% endfor %} + + +
{{ header }}
{{ cell }}
+ {% block content %}{% endblock content %} +
\ No newline at end of file From 3a69e4d18830cebe3548de57ff90c968ceea167d Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 11 Dec 2024 17:42:08 +0000 Subject: [PATCH 19/52] ntp module template --- modules/test/ntp/ntp.Dockerfile | 5 +++- modules/test/ntp/python/src/ntp_module.py | 24 +++++++++++++++++-- .../test/ntp/resources/report_template.jinja2 | 2 ++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 modules/test/ntp/resources/report_template.jinja2 diff --git a/modules/test/ntp/ntp.Dockerfile b/modules/test/ntp/ntp.Dockerfile index 4d9701464..c7ae7fee1 100644 --- a/modules/test/ntp/ntp.Dockerfile +++ b/modules/test/ntp/ntp.Dockerfile @@ -31,4 +31,7 @@ COPY $MODULE_DIR/conf /testrun/conf COPY $MODULE_DIR/bin /testrun/bin # Copy over all python files -COPY $MODULE_DIR/python /testrun/python \ No newline at end of file +COPY $MODULE_DIR/python /testrun/python + +# Copy Jinja template +COPY $MODULE_DIR/resources/report_template.jinja2 $REPORT_TEMPLATE_PATH/ \ No newline at end of file diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 22fbf40ed..bf64c5891 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -16,6 +16,7 @@ from scapy.all import rdpcap, IP, IPv6, NTP, UDP, Ether import os from collections import defaultdict +from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' MODULE_REPORT_FILE_NAME = 'ntp_report.html' @@ -23,6 +24,7 @@ STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' LOGGER = None +REPORT_TEMPLATE_FILE = 'report_template.jinja2' class NTPModule(TestModule): @@ -49,11 +51,20 @@ def __init__(self, LOGGER = self._get_logger() def generate_module_report(self): + # Load Jinja2 template + loader=FileSystemLoader(self._report_template_folder) + template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + module_header="NTP Module" + summmary_headers = [ + 'Requests to local NTP server', + 'Requests to external NTP servers', + 'Total NTP requests', + 'Total NTP responses' + ] + # Extract NTP data from the pcap file ntp_table_data = self.extract_ntp_data() - html_content = '

NTP Module

' - # Set the summary variables local_requests = sum( 1 for row in ntp_table_data @@ -67,6 +78,15 @@ def generate_module_report(self): total_responses = sum(1 for row in ntp_table_data if row['Type'] == 'Server') + summary_data = [ + local_requests, + external_requests, + total_requests, + total_responses + ] + + html_content = '

NTP Module

' + # Initialize a dictionary to store timestamps for each unique combination timestamps = defaultdict(list) diff --git a/modules/test/ntp/resources/report_template.jinja2 b/modules/test/ntp/resources/report_template.jinja2 new file mode 100644 index 000000000..793b65df2 --- /dev/null +++ b/modules/test/ntp/resources/report_template.jinja2 @@ -0,0 +1,2 @@ +{% extends base_template %} +{% block content %}{% endblock %} \ No newline at end of file From 4daa4203ae2d73286c9759c4fc2cbe679ac2c803 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 18:56:22 +0100 Subject: [PATCH 20/52] add page header and footer to the base_template --- resources/report/module_report_base.jinja2 | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 index 4edc56e0b..009b7646d 100644 --- a/resources/report/module_report_base.jinja2 +++ b/resources/report/module_report_base.jinja2 @@ -1,3 +1,26 @@ +{% raw %} +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+{% endraw %}
{% if module_header %}

{{ module_header }}

@@ -21,4 +44,12 @@ {% block content %}{% endblock content %} -
\ No newline at end of file +
+{% raw %} + +
+
+{% endraw %} \ No newline at end of file From 6ce39fc94a5ac0ec7b1a004bbc6fa06a3401d992 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:00:48 +0100 Subject: [PATCH 21/52] generate ntp module report using Jinja --- modules/test/ntp/python/src/ntp_module.py | 119 ++++++++---------- .../test/ntp/resources/report_template.jinja2 | 31 ++++- 2 files changed, 83 insertions(+), 67 deletions(-) diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index bf64c5891..411776899 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -19,7 +19,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' -MODULE_REPORT_FILE_NAME = 'ntp_report.html' +MODULE_REPORT_FILE_NAME = 'ntp_report.jinja2' NTP_SERVER_CAPTURE_FILE = '/runtime/network/ntp.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' @@ -52,16 +52,30 @@ def __init__(self, def generate_module_report(self): # Load Jinja2 template + page_max_height = 910 + header_height = 48 + summary_height = 135 + row_height = 42 loader=FileSystemLoader(self._report_template_folder) template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) - module_header="NTP Module" - summmary_headers = [ + module_header='NTP Module' + # Summary table headers + summary_headers = [ 'Requests to local NTP server', 'Requests to external NTP servers', 'Total NTP requests', 'Total NTP responses' ] - + # Module data Headers + module_data_headers = [ + 'Source', + 'Destination', + 'Type', + 'Version', + 'Count', + 'Sync Request Average', + ] + # Extract NTP data from the pcap file ntp_table_data = self.extract_ntp_data() @@ -78,14 +92,13 @@ def generate_module_report(self): total_responses = sum(1 for row in ntp_table_data if row['Type'] == 'Server') + # Summary table data summary_data = [ local_requests, external_requests, total_requests, total_responses ] - - html_content = '

NTP Module

' # Initialize a dictionary to store timestamps for each unique combination timestamps = defaultdict(list) @@ -114,42 +127,9 @@ def generate_module_report(self): average_time_between_requests[key] = avg_diff - # Add summary table - html_content += (f''' - - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
{local_requests}{external_requests}{total_requests}{total_responses}
- ''') - + # Module table data + module_table_data = [] if total_requests + total_responses > 0: - table_content = ''' - - - - - - - - - - - - ''' # Generate the HTML table with the count column for (src, dst, typ, @@ -164,37 +144,44 @@ def generate_module_report(self): else: avg_formatted_time = 'N/A' - table_content += f''' - - - - - - - - ''' - - table_content += ''' - -
SourceDestinationTypeVersionCountSync Request Average
{src}{dst}{typ}{version}{cnt}{avg_formatted_time}
- ''' - html_content += table_content - - else: - html_content += (''' -
-
- No NTP traffic detected from the device -
''') - - LOGGER.debug('Module report:\n' + html_content) + module_table_data.append({ + 'src': src, + 'dst': dst, + 'typ': typ, + 'version': version, + 'cnt': cnt, + 'avg_fmt': avg_formatted_time + }) + + # Handling the possible table split + table_height = (len(module_table_data) + 1) * row_height + page_useful_space = page_max_height - header_height - summary_height + pages = table_height // (page_useful_space) + rows_on_page = ((page_useful_space) // row_height) - 1 + start = 0 + report_html = '' + for page in range(pages+1): + end = start + min(len(module_table_data), rows_on_page) + module_header_repr = module_header if page == 0 else None + page_html = template.render( + base_template=self._base_template_file, + module_header=module_header_repr, + summary_headers=summary_headers, + summary_data=summary_data, + module_data_headers=module_data_headers, + module_data=module_table_data[start:end] + ) + report_html += page_html + start = end + + LOGGER.debug('Module report:\n' + report_html) # Use os.path.join to create the complete file path report_path = os.path.join(self._results_dir, MODULE_REPORT_FILE_NAME) # Write the content to a file with open(report_path, 'w', encoding='utf-8') as file: - file.write(html_content) + file.write(report_html) LOGGER.info('Module report generated at: ' + str(report_path)) diff --git a/modules/test/ntp/resources/report_template.jinja2 b/modules/test/ntp/resources/report_template.jinja2 index 793b65df2..c3601b67b 100644 --- a/modules/test/ntp/resources/report_template.jinja2 +++ b/modules/test/ntp/resources/report_template.jinja2 @@ -1,2 +1,31 @@ {% extends base_template %} -{% block content %}{% endblock %} \ No newline at end of file +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + + + {% endfor %} + +
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
+{% else %} +
+
+ No NTP traffic detected from the device +
+{% endif %} +{% endblock %} \ No newline at end of file From a1296cb219b9087220cb183233538eb0cf038f59 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:03:05 +0100 Subject: [PATCH 22/52] refactor adding modules reports to the main report --- framework/python/src/common/testreport.py | 47 +++++++++++++------ framework/python/src/core/session.py | 10 ++++ .../python/src/test_orc/test_orchestrator.py | 14 +++++- modules/test/base/python/src/test_module.py | 1 + resources/report/test_report_template.html | 20 ++++---- 5 files changed, 66 insertions(+), 26 deletions(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index 6acc6eac0..4507096a9 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -16,12 +16,12 @@ from datetime import datetime from weasyprint import HTML from io import BytesIO -from common import util +from common import util, logger from common.statuses import TestrunStatus import base64 import os from test_orc.test_case import TestCase -from jinja2 import Environment, FileSystemLoader +from jinja2 import Environment, FileSystemLoader, BaseLoader from collections import OrderedDict import re from bs4 import BeautifulSoup @@ -34,6 +34,8 @@ TEST_REPORT_STYLES = 'test_report_styles.css' TEST_REPORT_TEMPLATE = 'test_report_template.html' +LOGGER = logger.get_logger('REPORT') + # Locate parent directory current_dir = os.path.dirname(os.path.realpath(__file__)) @@ -65,12 +67,16 @@ def __init__(self, self._total_tests = total_tests self._results = [] self._module_reports = [] + self._module_templates = [] self._report_url = '' self._cur_page = 0 def add_module_reports(self, module_reports): self._module_reports = module_reports + def add_module_templates(self, module_templates): + self._module_templates = module_templates + def get_status(self): return self._status @@ -243,16 +249,20 @@ def to_html(self): optional_steps_to_resolve = self._get_optional_steps_to_resolve(json_data) module_reports = self._get_module_pages() + env_module = Environment(loader=BaseLoader()) pages_num = self._pages_num(json_data) - total_pages = pages_num + len(module_reports) + 1 - if len(steps_to_resolve) > 0: - total_pages += 1 - if (len(optional_steps_to_resolve) > 0 - and json_data['device']['test_pack'] == 'Pilot Assessment' - ): - total_pages += 1 - - return template.render(styles=styles, + module_templates = [ + env_module.from_string(s).render( + json_data=json_data, + device=json_data['device'], + logo=logo, + icon_qualification=icon_qualification, + icon_pilot=icon_pilot, + version=self._version, + ) for s in self._module_templates + ] + + return self._add_page_counter(template.render(styles=styles, logo=logo, icon_qualification=icon_qualification, icon_pilot=icon_pilot, @@ -269,10 +279,19 @@ def to_html(self): optional_steps_to_resolve=optional_steps_to_resolve, module_reports=module_reports, pages_num=pages_num, - total_pages=total_pages, tests_first_page=TESTS_FIRST_PAGE, tests_per_page=TESTS_PER_PAGE, - ) + module_templates=module_templates + )) + + def _add_page_counter(self, html): + # Add page nums and total page + soup = BeautifulSoup(html, features='html5lib') + page_index_divs = soup.find_all('div', class_='page-index') + total_pages = len(page_index_divs) + for index, div in enumerate(page_index_divs): + div.string = f'Page {index+1}/{total_pages}' + return soup.prettify() def _pages_num(self, json_data): @@ -391,7 +410,7 @@ def _get_module_pages(self): if el.name == 'h1': current_size += 40 + h1_padding # Calculating the height of paired tables - elif (el.name == 'div' + elif (el.name == 'div' and el.has_attr('style') and el['style'] == 'display:flex;justify-content:space-between;'): tables = el.findChildren('table', recursive=True) current_size = max( diff --git a/framework/python/src/core/session.py b/framework/python/src/core/session.py index f2e5466d3..29a841c55 100644 --- a/framework/python/src/core/session.py +++ b/framework/python/src/core/session.py @@ -100,6 +100,9 @@ def __init__(self, root_dir): # All historical reports self._module_reports = [] + # Module report templates + self._module_templates = [] + # Parameters specified when starting Testrun self._runtime_params = [] @@ -398,6 +401,9 @@ def get_test_results(self): def get_module_reports(self): return self._module_reports + def get_module_templates(self): + return self._module_templates + def get_report_tests(self): """Returns the current test results in JSON-friendly format (in Python dictionary)""" @@ -465,6 +471,9 @@ def set_test_result_error(self, result): def add_module_report(self, module_report): self._module_reports.append(module_report) + def add_module_template(self, module_template): + self._module_templates.append(module_template) + def get_all_reports(self): reports = [] @@ -789,6 +798,7 @@ def reset(self): self._report_url = None self._total_tests = 0 self._module_reports = [] + self._module_templates = [] self._results = [] self._started = None self._finished = None diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index d133fefd9..13338f24f 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -177,6 +177,7 @@ def run_test_modules(self): generated_report_json = self._generate_report() report.from_json(generated_report_json) report.add_module_reports(self.get_session().get_module_reports()) + report.add_module_templates(self.get_session().get_module_templates()) device.add_report(report) self._write_reports(report) @@ -549,8 +550,19 @@ def _run_test_module(self, module): self.get_session().add_module_report(module_report) except (FileNotFoundError, PermissionError): LOGGER.debug("Test module did not produce a html module report") + # Get the Jinja report + jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" + try: + import shutil + shutil.copy2(jinja_file, '/home/user/projects/testrun/runtime/example.jinja2') + with open(jinja_file, "r", encoding="utf-8") as f: + module_template = f.read() + LOGGER.debug(f"Adding module template for module {module.name}") + self.get_session().add_module_template(module_template) + except (FileNotFoundError, PermissionError): + LOGGER.debug("Test module did not produce a module template") - LOGGER.info(f"Test module {module.name} has finished") + # LOGGER.info(f"Test module {module.name} has finished") def _get_container_logs(self, log_stream): """Resolve all current log data in the containers log_stream diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 73187d789..8d59f975e 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -18,6 +18,7 @@ import util from datetime import datetime import traceback +import re from common.statuses import TestResult diff --git a/resources/report/test_report_template.html b/resources/report/test_report_template.html index fbd8d1c68..0d08f65ea 100644 --- a/resources/report/test_report_template.html +++ b/resources/report/test_report_template.html @@ -10,10 +10,8 @@ - {% set page_index = namespace(value=0) %} {# Test Results #} {% for page in range(pages_num) %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(loop.first, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}} {% if loop.first %} @@ -105,14 +103,13 @@

Results List ({{ successful_tests }}/{{ tot

{% endfor %} {# Steps to resolve Device qualification #} {% if steps_to_resolve|length > 0 and json_data['device']['test_pack'] == 'Device Qualification' %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Non-compliant tests and suggested steps to resolve

@@ -137,13 +134,16 @@

Non-compliant tests and suggested steps to resolve

{% endfor %}
{% endif %} + {# Modules reports Jinja #} + {% for template in module_templates %} + {{ template }} + {% endfor %} {# Modules reports #} {% for module in module_reports %} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}
@@ -151,13 +151,12 @@

Non-compliant tests and suggested steps to resolve

{% endfor %} {# Device profile #} - {% set page_index.value = page_index.value+1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Device profile

@@ -186,13 +185,12 @@

Device profile

{# Pilot steps to resolve#} {% if json_data['device']['test_pack'] == 'Pilot Assessment' and optional_steps_to_resolve|length > 0 %} - {% set page_index.value = page_index.value + 1 %}
{{ header_macros.header(False, "Testrun report", json_data, device, logo, icon_qualification, icon_pilot)}}

Recommendations for Device Qualification

@@ -233,7 +231,7 @@

Recommendations for Device Qualification

{% endfor %}
{% endif %} From e2c3328b14aca52b0cd3c5ff7b74c284777698eb Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Fri, 13 Dec 2024 19:18:59 +0100 Subject: [PATCH 23/52] pylint --- framework/python/src/core/session.py | 4 ++-- framework/python/src/test_orc/test_orchestrator.py | 2 -- modules/test/base/python/src/test_module.py | 7 +++---- modules/test/ntp/python/src/ntp_module.py | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/framework/python/src/core/session.py b/framework/python/src/core/session.py index 29a841c55..ffbb9d26f 100644 --- a/framework/python/src/core/session.py +++ b/framework/python/src/core/session.py @@ -667,7 +667,7 @@ def _remove_invalid_questions(self, questions): valid_questions.append(question) else: - LOGGER.debug(f'Removed unrecognised question: {question["question"]}') + LOGGER.debug(f'Removed unrecognised question: {question["question"]}') # pylint: disable=W1405 # Return the list of valid questions return valid_questions @@ -713,7 +713,7 @@ def validate_profile_json(self, profile_json): question.get('question')) if format_q is None: - LOGGER.error(f'Unrecognised question: {question.get("question")}') + LOGGER.error(f'Unrecognised question: {question.get("question")}') # pylint: disable=W1405 # Just ignore additional questions continue diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 13338f24f..8309a48d1 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -553,8 +553,6 @@ def _run_test_module(self, module): # Get the Jinja report jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" try: - import shutil - shutil.copy2(jinja_file, '/home/user/projects/testrun/runtime/example.jinja2') with open(jinja_file, "r", encoding="utf-8") as f: module_template = f.read() LOGGER.debug(f"Adding module template for module {module.name}") diff --git a/modules/test/base/python/src/test_module.py b/modules/test/base/python/src/test_module.py index 8d59f975e..42ee3ff85 100644 --- a/modules/test/base/python/src/test_module.py +++ b/modules/test/base/python/src/test_module.py @@ -18,7 +18,6 @@ import util from datetime import datetime import traceback -import re from common.statuses import TestResult @@ -140,14 +139,14 @@ def run_tests(self): else: result = getattr(self, test_method_name)() except Exception as e: # pylint: disable=W0718 - LOGGER.error(f'An error occurred whilst running {test["name"]}') + LOGGER.error(f'An error occurred whilst running {test["name"]}') # pylint: disable=W1405 LOGGER.error(e) traceback.print_exc() else: - LOGGER.error(f'Test {test["name"]} has not been implemented') + LOGGER.error(f'Test {test["name"]} has not been implemented') # pylint: disable=W1405 result = TestResult.ERROR, 'This test could not be found' else: - LOGGER.debug(f'Test {test["name"]} is disabled') + LOGGER.debug(f'Test {test["name"]} is disabled') # pylint: disable=W1405 result = (TestResult.DISABLED, 'This test did not run because it is disabled') diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 411776899..17f63960a 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -30,7 +30,7 @@ class NTPModule(TestModule): """NTP Test module""" - def __init__(self, + def __init__(self, # pylint: disable=R0917 module, conf_file=None, results_dir=None, From 07a239f72a5a96b4726b2a424a0c6df8f2ff355f Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Tue, 17 Dec 2024 21:49:59 +0100 Subject: [PATCH 24/52] rename jinja report --- framework/python/src/test_orc/test_orchestrator.py | 2 +- modules/test/ntp/python/src/ntp_module.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/python/src/test_orc/test_orchestrator.py b/framework/python/src/test_orc/test_orchestrator.py index 8309a48d1..787de08e6 100644 --- a/framework/python/src/test_orc/test_orchestrator.py +++ b/framework/python/src/test_orc/test_orchestrator.py @@ -551,7 +551,7 @@ def _run_test_module(self, module): except (FileNotFoundError, PermissionError): LOGGER.debug("Test module did not produce a html module report") # Get the Jinja report - jinja_file = f"{module.container_runtime_dir}/{module.name}_report.jinja2" + jinja_file = f"{module.container_runtime_dir}/{module.name}_report.j2.html" try: with open(jinja_file, "r", encoding="utf-8") as f: module_template = f.read() diff --git a/modules/test/ntp/python/src/ntp_module.py b/modules/test/ntp/python/src/ntp_module.py index 17f63960a..6a64b101c 100644 --- a/modules/test/ntp/python/src/ntp_module.py +++ b/modules/test/ntp/python/src/ntp_module.py @@ -19,7 +19,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_ntp' -MODULE_REPORT_FILE_NAME = 'ntp_report.jinja2' +MODULE_REPORT_FILE_NAME = 'ntp_report.j2.html' NTP_SERVER_CAPTURE_FILE = '/runtime/network/ntp.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' From 788dc6734e37b8c24f93883385cabce657fc9202 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:14:46 +0100 Subject: [PATCH 25/52] unit tests --- testing/unit/ntp/ntp_module_test.py | 13 +- .../unit/ntp/reports/ntp_report_local.html | 294 +++++++++++------- .../ntp/reports/ntp_report_local_no_ntp.html | 95 ++++-- .../ntp/templates/module_report_base.jinja2 | 55 ++++ .../unit/ntp/templates/report_template.jinja2 | 31 ++ 5 files changed, 346 insertions(+), 142 deletions(-) create mode 100644 testing/unit/ntp/templates/module_report_base.jinja2 create mode 100644 testing/unit/ntp/templates/report_template.jinja2 diff --git a/testing/unit/ntp/ntp_module_test.py b/testing/unit/ntp/ntp_module_test.py index 52bd32aa9..1dda0340e 100644 --- a/testing/unit/ntp/ntp_module_test.py +++ b/testing/unit/ntp/ntp_module_test.py @@ -26,6 +26,10 @@ OUTPUT_DIR = os.path.join(TEST_FILES_DIR,'output/') REPORTS_DIR = os.path.join(TEST_FILES_DIR,'reports/') CAPTURES_DIR = os.path.join(TEST_FILES_DIR,'captures/') +MODULE_FILES_DIR = 'modules/test/' + MODULE +TEMPLATE_DIR = os.path.join(TEST_FILES_DIR, 'templates') +TEMPLATE_FILE = 'report_template.jinja2' +BASE_TEMPLATE_FILE = 'module_report_base.jinja2' LOCAL_REPORT = os.path.join(REPORTS_DIR,'ntp_report_local.html') LOCAL_REPORT_NO_NTP = os.path.join(REPORTS_DIR,'ntp_report_local_no_ntp.html') @@ -52,6 +56,8 @@ def ntp_module_report_test(self): ntp_server_capture_file=NTP_SERVER_CAPTURE_FILE, startup_capture_file=STARTUP_CAPTURE_FILE, monitor_capture_file=MONITOR_CAPTURE_FILE) + setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) + setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) report_out_path = ntp_module.generate_module_report() @@ -67,7 +73,6 @@ def ntp_module_report_test(self): new_report_name = 'ntp_local.html' new_report_path = os.path.join(OUTPUT_DIR, new_report_name) shutil.copy(report_out_path, new_report_path) - self.assertEqual(report_out, report_local) # Test the module report generation if no DNS traffic @@ -100,12 +105,15 @@ def ntp_module_report_no_ntp_test(self): wrpcap(startup_cap_file, packets_startup) wrpcap(monitor_cap_file, packets_monitor) - ntp_module = NTPModule(module='dns', + ntp_module = NTPModule(module=MODULE, results_dir=OUTPUT_DIR, ntp_server_capture_file=ntp_server_cap_file, startup_capture_file=startup_cap_file, monitor_capture_file=monitor_cap_file) + setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) + setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) + report_out_path = ntp_module.generate_module_report() # Read the generated report @@ -136,4 +144,3 @@ def ntp_module_report_no_ntp_test(self): if not test_result.wasSuccessful(): sys.exit(1) # Return a non-zero exit code for failures sys.exit(0) # Return zero for success - \ No newline at end of file diff --git a/testing/unit/ntp/reports/ntp_report_local.html b/testing/unit/ntp/reports/ntp_report_local.html index 1fe5e3f3a..89e736e17 100644 --- a/testing/unit/ntp/reports/ntp_report_local.html +++ b/testing/unit/ntp/reports/ntp_report_local.html @@ -1,115 +1,179 @@ -

NTP Module

- - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SourceDestinationTypeVersionCountSync Request Average
10.10.10.15216.239.35.12Client4837.942 seconds
216.239.35.1210.10.10.15Server48N/A
10.10.10.15216.239.35.4Client4837.834 seconds
216.239.35.410.10.10.15Server48N/A
10.10.10.15216.239.35.8Client4838.056 seconds
216.239.35.810.10.10.15Server48N/A
10.10.10.15216.239.35.0Client41420.601 seconds
216.239.35.010.10.10.15Server417N/A
10.10.10.1510.10.10.5Client46313.057 seconds
10.10.10.510.10.10.15Server463N/A
- \ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

NTP Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SourceDestinationTypeVersionCountSync Request Average
10.10.10.15216.239.35.12Client4837.942 seconds
216.239.35.1210.10.10.15Server48N/A
10.10.10.15216.239.35.4Client4837.834 seconds
216.239.35.410.10.10.15Server48N/A
10.10.10.15216.239.35.8Client4838.056 seconds
216.239.35.810.10.10.15Server48N/A
10.10.10.15216.239.35.0Client41420.601 seconds
216.239.35.010.10.10.15Server417N/A
10.10.10.1510.10.10.5Client46313.057 seconds
10.10.10.510.10.10.15Server463N/A
+ + +
+ + +
+
diff --git a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html index c93ab885f..abe616a38 100644 --- a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html +++ b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html @@ -1,24 +1,71 @@ -

NTP Module

- - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
- -
-
- No NTP traffic detected from the device -
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

NTP Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
+ + +
+
+ No NTP traffic detected from the device +
+ + +
+ + +
+
diff --git a/testing/unit/ntp/templates/module_report_base.jinja2 b/testing/unit/ntp/templates/module_report_base.jinja2 new file mode 100644 index 000000000..009b7646d --- /dev/null +++ b/testing/unit/ntp/templates/module_report_base.jinja2 @@ -0,0 +1,55 @@ +{% raw %} +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+{% endraw %} +
+ {% if module_header %} +

{{ module_header }}

+ + {% else %} +
+ {% endif %} + + + {% for header in summary_headers %} + + {% endfor %} + + + + + {% for cell in summary_data %} + + {% endfor %} + + +
{{ header }}
{{ cell }}
+ {% block content %}{% endblock content %} +
+{% raw %} + +
+
+{% endraw %} \ No newline at end of file diff --git a/testing/unit/ntp/templates/report_template.jinja2 b/testing/unit/ntp/templates/report_template.jinja2 new file mode 100644 index 000000000..c3601b67b --- /dev/null +++ b/testing/unit/ntp/templates/report_template.jinja2 @@ -0,0 +1,31 @@ +{% extends base_template %} +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + + + {% endfor %} + +
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
+{% else %} +
+
+ No NTP traffic detected from the device +
+{% endif %} +{% endblock %} \ No newline at end of file From b6d364a1ad97b0bd8c8bb0510ce45d67fe09c9d3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:50:29 +0100 Subject: [PATCH 26/52] remove unused mocks --- testing/unit/ntp/ntp_module_test.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/testing/unit/ntp/ntp_module_test.py b/testing/unit/ntp/ntp_module_test.py index 1dda0340e..3b865d531 100644 --- a/testing/unit/ntp/ntp_module_test.py +++ b/testing/unit/ntp/ntp_module_test.py @@ -27,9 +27,6 @@ REPORTS_DIR = os.path.join(TEST_FILES_DIR,'reports/') CAPTURES_DIR = os.path.join(TEST_FILES_DIR,'captures/') MODULE_FILES_DIR = 'modules/test/' + MODULE -TEMPLATE_DIR = os.path.join(TEST_FILES_DIR, 'templates') -TEMPLATE_FILE = 'report_template.jinja2' -BASE_TEMPLATE_FILE = 'module_report_base.jinja2' LOCAL_REPORT = os.path.join(REPORTS_DIR,'ntp_report_local.html') LOCAL_REPORT_NO_NTP = os.path.join(REPORTS_DIR,'ntp_report_local_no_ntp.html') @@ -56,8 +53,6 @@ def ntp_module_report_test(self): ntp_server_capture_file=NTP_SERVER_CAPTURE_FILE, startup_capture_file=STARTUP_CAPTURE_FILE, monitor_capture_file=MONITOR_CAPTURE_FILE) - setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) - setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) report_out_path = ntp_module.generate_module_report() @@ -111,9 +106,6 @@ def ntp_module_report_no_ntp_test(self): startup_capture_file=startup_cap_file, monitor_capture_file=monitor_cap_file) - setattr(ntp_module, '_report_template_folder', TEMPLATE_DIR) - setattr(ntp_module, '_base_template_file', BASE_TEMPLATE_FILE) - report_out_path = ntp_module.generate_module_report() # Read the generated report From b287a93ca2a469d40f80369315435d3701f49806 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 01:57:41 +0100 Subject: [PATCH 27/52] delete unused templates --- .../ntp/templates/module_report_base.jinja2 | 55 ------------------- .../unit/ntp/templates/report_template.jinja2 | 31 ----------- 2 files changed, 86 deletions(-) delete mode 100644 testing/unit/ntp/templates/module_report_base.jinja2 delete mode 100644 testing/unit/ntp/templates/report_template.jinja2 diff --git a/testing/unit/ntp/templates/module_report_base.jinja2 b/testing/unit/ntp/templates/module_report_base.jinja2 deleted file mode 100644 index 009b7646d..000000000 --- a/testing/unit/ntp/templates/module_report_base.jinja2 +++ /dev/null @@ -1,55 +0,0 @@ -{% raw %} -
-
-
- {# Badge #} -

- {% if json_data['device']['test_pack'] == 'Device Qualification' %} - - Device Qualification - {% else %} - - Pilot Assessment - {% endif %} -

- {{ title }} -
- - {{ device['manufacturer'] }} - {{ device['model']}} - - Testrun -
-{% endraw %} -
- {% if module_header %} -

{{ module_header }}

- - {% else %} -
- {% endif %} - - - {% for header in summary_headers %} - - {% endfor %} - - - - - {% for cell in summary_data %} - - {% endfor %} - - -
{{ header }}
{{ cell }}
- {% block content %}{% endblock content %} -
-{% raw %} - -
-
-{% endraw %} \ No newline at end of file diff --git a/testing/unit/ntp/templates/report_template.jinja2 b/testing/unit/ntp/templates/report_template.jinja2 deleted file mode 100644 index c3601b67b..000000000 --- a/testing/unit/ntp/templates/report_template.jinja2 +++ /dev/null @@ -1,31 +0,0 @@ -{% extends base_template %} -{% block content %} -{% if module_data %} - - - - {% for header in module_data_headers %} - - {% endfor %} - - - - {% for row in module_data %} - - - - - - - - - {% endfor %} - -
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['typ'] }}{{ row['version'] }}{{ row['cnt'] }}{{ row['avg_fmt'] }}
-{% else %} -
-
- No NTP traffic detected from the device -
-{% endif %} -{% endblock %} \ No newline at end of file From 35d555d5d07fcb4db196f057e136c093b795a655 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 02:04:34 +0100 Subject: [PATCH 28/52] change style to id for paired tables --- framework/python/src/common/testreport.py | 4 ++-- modules/test/tls/python/src/tls_module.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index 4507096a9..df31a5441 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -410,8 +410,8 @@ def _get_module_pages(self): if el.name == 'h1': current_size += 40 + h1_padding # Calculating the height of paired tables - elif (el.name == 'div' and el.has_attr('style') - and el['style'] == 'display:flex;justify-content:space-between;'): + elif (el.name == 'div' and el.has_attr('id') + and el['id'] == 'paired'): tables = el.findChildren('table', recursive=True) current_size = max( map(lambda t: len( diff --git a/modules/test/tls/python/src/tls_module.py b/modules/test/tls/python/src/tls_module.py index 1405ad31c..ca7bed260 100644 --- a/modules/test/tls/python/src/tls_module.py +++ b/modules/test/tls/python/src/tls_module.py @@ -229,7 +229,7 @@ def generate_module_report(self): summary_table = f'\n{summary_table}' summary_table += f''' -
+
Certificate Information
{cert_table} From 01c12808eb39a426dca029fe36187bf25e945770 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 02:32:35 +0100 Subject: [PATCH 29/52] fix tls unit tests --- testing/unit/tls/reports/tls_report_ext_local.html | 4 ++-- testing/unit/tls/reports/tls_report_local.html | 8 ++++---- testing/unit/tls/reports/tls_report_single.html | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/testing/unit/tls/reports/tls_report_ext_local.html b/testing/unit/tls/reports/tls_report_ext_local.html index 7e691f66a..bde424a0e 100644 --- a/testing/unit/tls/reports/tls_report_ext_local.html +++ b/testing/unit/tls/reports/tls_report_ext_local.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -121,7 +121,7 @@
Certificate Extensions
-
+
Certificate Information
diff --git a/testing/unit/tls/reports/tls_report_local.html b/testing/unit/tls/reports/tls_report_local.html index 72b2e5a1a..4d1af0404 100644 --- a/testing/unit/tls/reports/tls_report_local.html +++ b/testing/unit/tls/reports/tls_report_local.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -151,7 +151,7 @@
Certificate Extensions
-
+
Certificate Information
@@ -281,7 +281,7 @@
Certificate Extensions
-
+
Certificate Information
@@ -411,7 +411,7 @@
Certificate Extensions
-
+
Certificate Information
diff --git a/testing/unit/tls/reports/tls_report_single.html b/testing/unit/tls/reports/tls_report_single.html index 36adffb94..0f6a5a4d6 100644 --- a/testing/unit/tls/reports/tls_report_single.html +++ b/testing/unit/tls/reports/tls_report_single.html @@ -22,7 +22,7 @@

TLS Module

-
+
Certificate Information
@@ -125,7 +125,7 @@
Subject Information
-
+
Certificate Information
From ff35c944f22f9f73d8a2ab58612f946972bfa618 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 12:33:50 +0100 Subject: [PATCH 30/52] unit tests --- .../services_report_all_closed_local.html | 88 +++++++--- .../reports/services_report_local.html | 150 ++++++++++++------ 2 files changed, 169 insertions(+), 69 deletions(-) diff --git a/testing/unit/services/reports/services_report_all_closed_local.html b/testing/unit/services/reports/services_report_all_closed_local.html index 356a82d35..01ac50676 100644 --- a/testing/unit/services/reports/services_report_all_closed_local.html +++ b/testing/unit/services/reports/services_report_all_closed_local.html @@ -1,21 +1,67 @@ -

Services Module

- - - - - - - - - - - - - - -
TCP ports openUDP ports openTotal ports open
000
- -
-
- No open ports detected -
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

Services Module

+ + + + + + + + + + + + + + + + + + + + + + + + +
TCP ports openUDP ports openTotal ports open
000
+ + +
+
+ No open ports detected +
+ + +
+ + +
+
diff --git a/testing/unit/services/reports/services_report_local.html b/testing/unit/services/reports/services_report_local.html index ce601cfc0..71f929aeb 100644 --- a/testing/unit/services/reports/services_report_local.html +++ b/testing/unit/services/reports/services_report_local.html @@ -1,48 +1,102 @@ -

Services Module

- - - - - - - - - - - - - - -
TCP ports openUDP ports openTotal ports open
303
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PortStateServiceVersion
22/tcpopenssh8.8 protocol 2.0
443/tcpopenhttp
502/tcpopenmbap
- \ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

Services Module

+ + + + + + + + + + + + + + + + + + + + + + + + +
TCP ports openUDP ports openTotal ports open
303
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortStateServiceVersion
22/tcpopenssh8.8 protocol 2.0
443/tcpopenhttp
502/tcpopenmbap
+ + +
+ + +
+
From c68914f923d0066665eaabe7852e550491fbfdd8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Mon, 16 Dec 2024 15:17:32 +0100 Subject: [PATCH 31/52] generating dns module report using jinja --- modules/test/dns/dns.Dockerfile | 6 +- modules/test/dns/python/src/dns_module.py | 130 +++++++++--------- .../test/dns/resources/report_template.jinja2 | 31 +++++ 3 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 modules/test/dns/resources/report_template.jinja2 diff --git a/modules/test/dns/dns.Dockerfile b/modules/test/dns/dns.Dockerfile index 461e87899..53f8f31f8 100644 --- a/modules/test/dns/dns.Dockerfile +++ b/modules/test/dns/dns.Dockerfile @@ -31,4 +31,8 @@ COPY $MODULE_DIR/conf /testrun/conf COPY $MODULE_DIR/bin /testrun/bin # Copy over all python files -COPY $MODULE_DIR/python /testrun/python \ No newline at end of file +COPY $MODULE_DIR/python /testrun/python + +# Copy Jinja template +COPY $MODULE_DIR/resources/report_template.jinja2 $REPORT_TEMPLATE_PATH/ + diff --git a/modules/test/dns/python/src/dns_module.py b/modules/test/dns/python/src/dns_module.py index fe244f0a7..42133d5c0 100644 --- a/modules/test/dns/python/src/dns_module.py +++ b/modules/test/dns/python/src/dns_module.py @@ -17,19 +17,21 @@ from test_module import TestModule import os from collections import Counter +from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_dns' -MODULE_REPORT_FILE_NAME = 'dns_report.html' +MODULE_REPORT_FILE_NAME = 'dns_report.jinja2' DNS_SERVER_CAPTURE_FILE = '/runtime/network/dns.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' LOGGER = None +REPORT_TEMPLATE_FILE = 'report_template.jinja2' class DNSModule(TestModule): """DNS Test module""" - def __init__(self, + def __init__(self, # pylint: disable=R0917 module, conf_file=None, results_dir=None, @@ -48,11 +50,33 @@ def __init__(self, LOGGER = self._get_logger() def generate_module_report(self): + # Load Jinja2 template + page_max_height = 910 + header_height = 48 + summary_height = 135 + row_height = 42 + loader=FileSystemLoader(self._report_template_folder) + template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + module_header='DNS Module' + # Summary table headers + summary_headers = [ + 'Requests to local DNS server', + 'Requests to external DNS servers', + 'Total DNS requests', + 'Total DNS responses', + ] + # Module data Headers + module_data_headers = [ + 'Source', + 'Destination', + 'Resolved IP', + 'Type', + 'URL', + 'Count', + ] # Extract DNS data from the pcap file dns_table_data = self.extract_dns_data() - html_content = '

DNS Module

' - # Set the summary variables local_requests = sum( 1 for row in dns_table_data @@ -67,79 +91,59 @@ def generate_module_report(self): if row['Type'] == 'Response') # Add summary table - html_content += (f''' - - - - - - - - - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
{local_requests}{external_requests}{total_requests}{total_responses}
- ''') - + summary_data = [ + local_requests, + external_requests, + total_requests, + total_responses, + ] + + module_data = [] if (total_requests + total_responses) > 0: - table_content = ''' - - - - - - - - - - - - ''' - # Count unique combinations counter = Counter((row['Source'], row['Destination'], row['ResolvedIP'], row['Type'], row['Data']) for row in dns_table_data) # Generate the HTML table with the count column for (src, dst, res_ip, typ, dat), count in counter.items(): - table_content += f''' - - - - - - - - ''' - - table_content += ''' - -
SourceDestinationResolved IPTypeURLCount
{src}{dst}{res_ip}{typ}{dat}{count}
''' - - html_content += table_content - - else: - html_content += (''' -
-
- No DNS traffic detected from the device -
''') - - LOGGER.debug('Module report:\n' + html_content) + module_data.append({ + 'src': src, + 'dst': dst, + 'res_ip': res_ip, + 'typ': typ, + 'dat': dat, + 'count': count, + }) + # Handling the possible table split + table_height = (len(module_data) + 1) * row_height + page_useful_space = page_max_height - header_height - summary_height + pages = table_height // (page_useful_space) + rows_on_page = (page_useful_space) // row_height + start = 0 + report_html = '' + for page in range(pages+1): + end = start + min(len(module_data), rows_on_page) + module_header_repr = module_header if page == 0 else None + page_html = template.render( + base_template=self._base_template_file, + module_header=module_header_repr, + summary_headers=summary_headers, + summary_data=summary_data, + module_data_headers=module_data_headers, + module_data=module_data[start:end] + ) + report_html += page_html + start = end + + LOGGER.debug('Module report:\n' + report_html) # Use os.path.join to create the complete file path report_path = os.path.join(self._results_dir, MODULE_REPORT_FILE_NAME) # Write the content to a file with open(report_path, 'w', encoding='utf-8') as file: - file.write(html_content) + file.write(report_html) LOGGER.info('Module report generated at: ' + str(report_path)) diff --git a/modules/test/dns/resources/report_template.jinja2 b/modules/test/dns/resources/report_template.jinja2 new file mode 100644 index 000000000..2d4dd99b2 --- /dev/null +++ b/modules/test/dns/resources/report_template.jinja2 @@ -0,0 +1,31 @@ +{% extends base_template %} +{% block content %} +{% if module_data %} + + + + {% for header in module_data_headers %} + + {% endfor %} + + + + {% for row in module_data %} + + + + + + + + + {% endfor %} + +
{{ header }}
{{ row['src'] }}{{ row['dst'] }}{{ row['res_ip'] }}{{ row['typ'] }}{{ row['dat'] }}{{ row['count'] }}
+{% else %} +
+
+ No DNS traffic detected from the device +
+{% endif %} +{% endblock %} \ No newline at end of file From 38653d4cd72d106482e16700c3e7403fd7d641f1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 18 Dec 2024 13:32:40 +0100 Subject: [PATCH 32/52] rename report file --- modules/test/dns/python/src/dns_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/dns/python/src/dns_module.py b/modules/test/dns/python/src/dns_module.py index 42133d5c0..47cdb82b4 100644 --- a/modules/test/dns/python/src/dns_module.py +++ b/modules/test/dns/python/src/dns_module.py @@ -20,7 +20,7 @@ from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_dns' -MODULE_REPORT_FILE_NAME = 'dns_report.jinja2' +MODULE_REPORT_FILE_NAME = 'dns_report.j2.html' DNS_SERVER_CAPTURE_FILE = '/runtime/network/dns.pcap' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' From 53b9e39a79a89742a44cc8c03db3ac105091ce3b Mon Sep 17 00:00:00 2001 From: Volha Mardvilka Date: Thu, 19 Dec 2024 12:38:40 +0000 Subject: [PATCH 33/52] 382100926: (feat) change styles for dns module table --- .../test/dns/resources/report_template.jinja2 | 16 ++++++++-------- resources/report/test_report_styles.css | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/modules/test/dns/resources/report_template.jinja2 b/modules/test/dns/resources/report_template.jinja2 index 2d4dd99b2..2a690f610 100644 --- a/modules/test/dns/resources/report_template.jinja2 +++ b/modules/test/dns/resources/report_template.jinja2 @@ -1,7 +1,7 @@ {% extends base_template %} {% block content %} {% if module_data %} - +
{% for header in module_data_headers %} @@ -12,13 +12,13 @@ {% for row in module_data %} - - - - - - - + + + + + + + {% endfor %}
{{ row['src'] }}{{ row['dst'] }}{{ row['res_ip'] }}{{ row['typ'] }}{{ row['dat'] }}{{ row['count'] }}
{{ row['src'] }}
>
{{ row['dst'] }}
{{ row['res_ip'] }}
{{ row['typ'] }}
{{ row['dat'] }}
{{ row['count'] }}
diff --git a/resources/report/test_report_styles.css b/resources/report/test_report_styles.css index b1ed9d33c..3971bd2eb 100644 --- a/resources/report/test_report_styles.css +++ b/resources/report/test_report_styles.css @@ -222,6 +222,24 @@ word-break: break-word; } + .module-data.dns-module thead tr th, + .module-data.dns-module tbody tr td { + padding: 12px 16px; + } + + .module-data.dns-module tbody tr td { + max-width: 1px; + } + + .module-data.dns-module tbody tr td.count-row { + width: 1px; + } + + .module-data.dns-module tbody tr td div { + word-wrap: break-word; + overflow-wrap: break-word; + } + div.steps-to-resolve { background-color: #F8F9FA; margin-bottom: 30px; From 701b8b9d79b989663938e8fe0f404f1e03e3f5a6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 19 Dec 2024 14:39:41 +0100 Subject: [PATCH 34/52] fix tag --- modules/test/dns/resources/report_template.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/test/dns/resources/report_template.jinja2 b/modules/test/dns/resources/report_template.jinja2 index 2a690f610..8e701a8e3 100644 --- a/modules/test/dns/resources/report_template.jinja2 +++ b/modules/test/dns/resources/report_template.jinja2 @@ -12,7 +12,7 @@ {% for row in module_data %} -
{{ row['src'] }}
> +
{{ row['src'] }}
{{ row['dst'] }}
{{ row['res_ip'] }}
{{ row['typ'] }}
From fbefc96d142815c1dd9c6316788ea4162ddca819 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 19 Dec 2024 15:10:03 +0100 Subject: [PATCH 35/52] unit tests --- .../unit/dns/reports/dns_report_local.html | 258 +++++++++++------- .../dns/reports/dns_report_local_no_dns.html | 94 +++++-- 2 files changed, 232 insertions(+), 120 deletions(-) diff --git a/testing/unit/dns/reports/dns_report_local.html b/testing/unit/dns/reports/dns_report_local.html index ce5a535fd..9c4e6c90a 100644 --- a/testing/unit/dns/reports/dns_report_local.html +++ b/testing/unit/dns/reports/dns_report_local.html @@ -1,97 +1,161 @@ -

DNS Module

- - - - - - - - - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
7107184
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SourceDestinationResolved IPTypeURLCount
10.10.10.1410.10.10.4N/AQuerymqtt.googleapis.com64
10.10.10.410.10.10.14173.194.195.206Responsemqtt.googleapis.com38
10.10.10.410.10.10.142607:f8b0:4001:c11::ceResponsemqtt.googleapis.com32
10.10.10.1410.10.10.4N/AQuerypool.ntp.org7
10.10.10.410.10.10.14N/AResponsepool.ntp.org4
10.10.10.410.10.10.145.78.89.3Responsepool.ntp.org2
10.10.10.410.10.10.14199.68.201.234Responsepool.ntp.org2
10.10.10.410.10.10.142607:f8b0:4001:c08::ceResponsemqtt.googleapis.com6
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

DNS Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
7107184
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SourceDestinationResolved IPTypeURLCount
10.10.10.14
10.10.10.4
N/A
Query
mqtt.googleapis.com
64
10.10.10.4
10.10.10.14
173.194.195.206
Response
mqtt.googleapis.com
38
10.10.10.4
10.10.10.14
2607:f8b0:4001:c11::ce
Response
mqtt.googleapis.com
32
10.10.10.14
10.10.10.4
N/A
Query
pool.ntp.org
7
10.10.10.4
10.10.10.14
N/A
Response
pool.ntp.org
4
10.10.10.4
10.10.10.14
5.78.89.3
Response
pool.ntp.org
2
10.10.10.4
10.10.10.14
199.68.201.234
Response
pool.ntp.org
2
10.10.10.4
10.10.10.14
2607:f8b0:4001:c08::ce
Response
mqtt.googleapis.com
6
+ + +
+ + +
+
diff --git a/testing/unit/dns/reports/dns_report_local_no_dns.html b/testing/unit/dns/reports/dns_report_local_no_dns.html index f144655f5..f9dee2020 100644 --- a/testing/unit/dns/reports/dns_report_local_no_dns.html +++ b/testing/unit/dns/reports/dns_report_local_no_dns.html @@ -1,23 +1,71 @@ -

DNS Module

- - - - - - - - - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
0000
- -
-
- No DNS traffic detected from the device -
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

DNS Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
0000
+ + +
+
+ No DNS traffic detected from the device +
+ + +
+ + +
+
From f410d2e6805c7e315cf92b283ce097b704b99309 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Mon, 13 Jan 2025 21:20:07 +0000 Subject: [PATCH 36/52] report template --- .../test/tls/resources/report_template.jinja2 | 90 +++++++++++++++++++ modules/test/tls/tls.Dockerfile | 3 + 2 files changed, 93 insertions(+) create mode 100644 modules/test/tls/resources/report_template.jinja2 diff --git a/modules/test/tls/resources/report_template.jinja2 b/modules/test/tls/resources/report_template.jinja2 new file mode 100644 index 000000000..ecb137df5 --- /dev/null +++ b/modules/test/tls/resources/report_template.jinja2 @@ -0,0 +1,90 @@ +{% extends base_template %} +{% block content %} +{% if cert_info_data and subject_data %} +
+
+
Certificate Information
+ + + + {% for header in cert_table_headers%} + + {% endfor %} + + + + {% for k,v in cert_info_data.items() %} + + + + + {% endfor %} + +
{{ header }}
{{ k }}{{ v }}
+
+
+
Subject Information
+ + + + {% for header in cert_table_headers%} + + {% endfor %} + + + + {% for k,v in subject_data.items() %} + + + + + {% endfor %} + +
{{ header }}
{{ k }}{{ v }}
+
+
+ {% if cert_ext %} +
Certificate Extensions
+ + + + + + + + + {% for k,v in cert_ext.items()%} + + + + + {% endfor %} + +
PropertyValue
{{ k }}{{ v }}
+ {% endif %} +{% elif ountbound_headers %} +

Outbound Connections

+ + + + {% for header in ountbound_headers%} + + {% endfor %} + + + + {% for ip, port in outbound_conns%} + + + + + {% endfor %} + +
{{ header }}
{{ip}}{{port}}
+ + {#
+
+ No NTP traffic detected from the device +
#} +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/modules/test/tls/tls.Dockerfile b/modules/test/tls/tls.Dockerfile index c448c8478..3d6d66544 100644 --- a/modules/test/tls/tls.Dockerfile +++ b/modules/test/tls/tls.Dockerfile @@ -51,3 +51,6 @@ RUN pip3 install -r /testrun/python/requirements-test.txt # Create a directory inside the container to store the root certificates RUN mkdir -p /testrun/root_certs + +# Copy Jinja template +COPY $MODULE_DIR/resources/report_template.jinja2 $REPORT_TEMPLATE_PATH/ From a1e5d5461a32bcd102d864228ef1b2324f48e93f Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Tue, 14 Jan 2025 18:27:57 +0000 Subject: [PATCH 37/52] tls jinja report --- modules/test/tls/python/src/tls_module.py | 233 ++++++------------ .../test/tls/resources/report_template.jinja2 | 8 +- 2 files changed, 85 insertions(+), 156 deletions(-) diff --git a/modules/test/tls/python/src/tls_module.py b/modules/test/tls/python/src/tls_module.py index ca7bed260..ace90ceaf 100644 --- a/modules/test/tls/python/src/tls_module.py +++ b/modules/test/tls/python/src/tls_module.py @@ -25,15 +25,16 @@ from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec from cryptography.x509 import AuthorityKeyIdentifier, SubjectKeyIdentifier, BasicConstraints, KeyUsage from cryptography.x509 import GeneralNames, DNSName, ExtendedKeyUsage, ObjectIdentifier, SubjectAlternativeName +from jinja2 import Environment, FileSystemLoader LOG_NAME = 'test_tls' -MODULE_REPORT_FILE_NAME = 'tls_report.html' +MODULE_REPORT_FILE_NAME = 'tls_report.j2.html' STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap' MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap' TLS_CAPTURE_FILE = '/runtime/output/tls.pcap' GATEWAY_CAPTURE_FILE = '/runtime/network/gateway.pcap' LOGGER = None - +REPORT_TEMPLATE_FILE = 'report_template.jinja2' class TLSModule(TestModule): """The TLS testing module.""" @@ -57,7 +58,24 @@ def __init__(self, self._tls_util = TLSUtil(LOGGER) def generate_module_report(self): - html_content = '

TLS Module

' + # Load Jinja2 template + loader=FileSystemLoader(self._report_template_folder) + template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + module_header='TLS Module' + # Summary table headers + summary_headers = [ + 'Expiry', + 'Length', + 'Type', + 'Port number', + 'Signed by', + ] + # Cert table headers + cert_table_headers = ['Property', 'Value'] + # Outbound connections table headers + outbound_headers = ['Destination IP', 'Port'] + pages = {} + outbound_conns = None # List of capture files to scan pcap_files = [ @@ -66,39 +84,12 @@ def generate_module_report(self): ] certificates = self.extract_certificates_from_pcap(pcap_files, self._device_mac) - if len(certificates) > 0: - cert_tables = [] # pylint: disable=W0612 for cert_num, ((ip_address, port), cert) in enumerate(certificates.items()): - - # Add summary table - summary_table = ''' - - - - - - - - - - - - ''' - - # Generate the certificate table - cert_table = ''' -
ExpiryLengthTypePort numberSigned by
- - - - - - - ''' + pages[cert_num] = {} # Extract certificate data not_valid_before = cert.not_valid_before @@ -124,50 +115,18 @@ def generate_module_report(self): cert.public_bytes(encoding=serialization.Encoding.DER)) # Append certification information - cert_table += f''' - - - - - - - - - - - - - - - - - -
PropertyValue
Version{version_value}
Signature Alg.{signature_alg_value}
Validity from{not_before}
Valid to{not_after}
- ''' - - subject_table = ''' - - - - - - - - ''' + pages[cert_num]['cert_info_data'] = { + 'Version': version_value, + 'Signature Alg.': signature_alg_value, + 'Validity from': not_before, + 'Valid to': not_after, + } # Append the subject information + pages[cert_num]['subject_data'] = {} for val in cert.subject.rdns: dn = val.rfc4514_string().split('=') - subject_table += f''' - - - - - ''' - - subject_table += ''' - -
PropertyValue
{dn[0]}{dn[1]}
''' + pages[cert_num]['subject_data'][dn[0]] = dn[1] # Append issuer information for val in cert.issuer.rdns: @@ -175,102 +134,72 @@ def generate_module_report(self): if 'CN' in dn[0]: signed_by = dn[1] - ext_table = '' - # Append extensions information if cert.extensions: - - ext_table = ''' -
Certificate Extensions
- - - - - - - - ''' - + pages[cert_num]['cert_ext'] = {} for extension in cert.extensions: if isinstance(extension.value, list): for extension_value in extension.value: - ext_table += f''' - - - - - ''' + extension_name = extension.oid._name + formatted_value = self.format_extension_value( + extension_value.value) + pages[cert_num]['cert_ext'][extension_name] = formatted_value else: - ext_table += f''' - - - - - ''' - - ext_table += ''' - -
PropertyValue
{extension.oid._name}{self.format_extension_value(extension_value.value)}
{extension.oid._name}{self.format_extension_value(extension.value)}
''' - - # Add summary table row - summary_table += f''' - - {not_after} - {cert_length} - {public_key_type} - {port} - {signed_by} - - - - ''' - - # Merge all table HTML - summary_table = f'\n{summary_table}' - - summary_table += f''' -
-
-
Certificate Information
- {cert_table} -
-
-
Subject Information
- {subject_table} -
-
''' - - if ext_table is not None: - summary_table += f'\n\n{ext_table}' - - cert_tables.append(summary_table) + formatted_value = self.format_extension_value( + extension.value) + pages[cert_num]['cert_ext'][extension.oid._name] = formatted_value + + pages[cert_num]['summary_data'] = [ + not_after, + cert_length, + public_key_type, + port, + signed_by + ] outbound_conns = self._tls_util.get_all_outbound_connections( device_mac=self._device_mac, capture_files=pcap_files) - conn_table = self.generate_outbound_connection_table(outbound_conns) - - html_content += summary_table + '\n'.join('\n' + tables - for tables in cert_tables) - html_content += conn_table + report_jinja = '' + if pages: + for num,page in pages.items(): + module_header_repr = module_header if num == 0 else None + cert_ext=page['cert_ext'] if 'cert_ext' in page else None + page_html = template.render( + base_template=self._base_template_file, + module_header=module_header_repr, + summary_headers=summary_headers, + summary_data=page['summary_data'], + cert_info_data=page['cert_info_data'], + subject_data=page['subject_data'], + cert_table_headers=cert_table_headers, + cert_ext=cert_ext, + ountbound_headers=outbound_headers, + ) + report_jinja += page_html + if outbound_conns: + out_page = template.render( + base_template=self._base_template_file, + ountbound_headers=outbound_headers, + outbound_conns=outbound_conns + ) + report_jinja += out_page else: - html_content += (''' -
-
- No TLS certificates found on the device -
''') - - LOGGER.debug('Module report:\n' + html_content) + report_jinja = template.render( + base_template=self._base_template_file, + module_header = module_header, + ) + LOGGER.debug('Module report:\n' + report_jinja) # Use os.path.join to create the complete file path - report_path = os.path.join(self._results_dir, MODULE_REPORT_FILE_NAME) + jinja_path = os.path.join(self._results_dir, MODULE_REPORT_FILE_NAME) # Write the content to a file - with open(report_path, 'w', encoding='utf-8') as file: - file.write(html_content) + with open(jinja_path, 'w', encoding='utf-8') as file: + file.write(report_jinja) - LOGGER.info('Module report generated at: ' + str(report_path)) - return report_path + LOGGER.info('Module report generated at: ' + str(jinja_path)) + return jinja_path def format_extension_value(self, value): if isinstance(value, bytes): @@ -533,5 +462,5 @@ def _validate_tls_client(self, def _resolve_device_ip(self): # If the ipv4 address wasn't resolved yet, try again - if self._device_ipv4_addr is None: + if self._device_ipv4_addr is None: # pylint: disable=E0203 self._device_ipv4_addr = self._get_device_ipv4() diff --git a/modules/test/tls/resources/report_template.jinja2 b/modules/test/tls/resources/report_template.jinja2 index ecb137df5..e7ddf1cf5 100644 --- a/modules/test/tls/resources/report_template.jinja2 +++ b/modules/test/tls/resources/report_template.jinja2 @@ -81,10 +81,10 @@ {% endfor %} - - {#
+{% else %} +
- No NTP traffic detected from the device -
#} + No TLS certificates found on the device +
{% endif %} {% endblock %} \ No newline at end of file From b269850488173d61344b5a6c9a86806ae231bfda Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Tue, 14 Jan 2025 18:28:22 +0000 Subject: [PATCH 38/52] unit tests --- .../tls/reports/tls_report_ext_local.html | 306 ++--- .../unit/tls/reports/tls_report_local.html | 1006 +++++++++-------- .../tls/reports/tls_report_no_cert_local.html | 60 +- .../unit/tls/reports/tls_report_single.html | 320 +++--- 4 files changed, 871 insertions(+), 821 deletions(-) diff --git a/testing/unit/tls/reports/tls_report_ext_local.html b/testing/unit/tls/reports/tls_report_ext_local.html index bde424a0e..b46e31925 100644 --- a/testing/unit/tls/reports/tls_report_ext_local.html +++ b/testing/unit/tls/reports/tls_report_ext_local.html @@ -1,186 +1,132 @@ -

TLS Module

- - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2027-07-25 15:33:09888EC443Sub CA
+
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

TLS Module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + -
-
-
Certificate Information
+
+ + + +
ExpiryLengthTypePort numberSigned by
2027-07-25 15:33:09888EC443Sub CA
+ + +
+
+
Certificate Information
+ + + + + + + + + + + -
PropertyValue
- - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueVersion3 (0x2)
Version3 (0x2)
Signature Alg.sha256WithRSAEncryption
Validity from2022-07-26 15:33:09
Valid to2027-07-25 15:33:09
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - -
PropertyValueSignature Alg.sha256WithRSAEncryption
CUS
CNapc27D605.nam.gad.schneider-electric.com
-
-
- - -
Certificate Extensions
- - - - - - - - - - - - - - -
PropertyValue
subjectAltNameap9643_qa1941270129.nam.gad.schneider-electric.com
- - - - + - - - - - + + - - - + + - - - - - -
ExpiryLengthTypePort numberSigned byValidity from2022-07-26 15:33:09
Valid to 2027-07-25 15:33:09888EC443Sub CA
- -
-
-
Certificate Information
- - + +
+
+
+
Subject Information
+ + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValue
PropertyValueCUS
Version3 (0x2)
Signature Alg.sha256WithRSAEncryption
Validity from2022-07-26 15:33:09
Valid to2027-07-25 15:33:09
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - +
PropertyValueCNapc27D605.nam.gad.schneider-electric.com
CUS
CNapc27D605.nam.gad.schneider-electric.com
- - -
Certificate Extensions
+ +
Certificate Extensions
@@ -189,24 +135,22 @@
Certificate Extensions
- - - - - - -
subjectAltNameap9643_qa1941270129.nam.gad.schneider-electric.com
-

Outbound Connections

- - - - - - - - - - - -
Destination IPPort
- \ No newline at end of file + + + subjectAltName + ap9643_qa1941270129.nam.gad.schneider-electric.com + + + + + + + +
+ + +
+
diff --git a/testing/unit/tls/reports/tls_report_local.html b/testing/unit/tls/reports/tls_report_local.html index 4d1af0404..403cccf67 100644 --- a/testing/unit/tls/reports/tls_report_local.html +++ b/testing/unit/tls/reports/tls_report_local.html @@ -1,231 +1,147 @@ -

TLS Module

- - +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

TLS Module

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC35288None
+ + +
+
+
Certificate Information
+ + + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byVersion3 (0x2)
2049-12-31 23:59:59779EC47188NoneSignature Alg.sha256WithRSAEncryption
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueValidity from2023-03-29 18:37:51
Version3 (0x2)
Signature Alg.sha256WithRSAEncryption
Validity from2023-03-29 18:37:51
Valid to2049-12-31 23:59:59
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - +
PropertyValueValid to2049-12-31 23:59:59
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
CNathena04E580B9
-
-
- - -
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyValue
authorityKeyIdentifierkey_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None
subjectKeyIdentifierdigest=37d90a274635e963081520f98411bda240d30252
basicConstraintsca=False, path_length=None
keyUsagedigital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False
- - - - + +
+
Subject Information
+
+ + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byCUS
2049-12-31 23:59:59779EC35288NoneSTPennsylvania
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueLCoopersburg
Version3 (0x2)
Signature Alg.sha256WithRSAEncryption
Validity from2023-03-29 18:37:51
Valid to2049-12-31 23:59:59
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + +
PropertyValueOLutron Electronics Co.\, Inc.
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
CNathena04E580B9
CNathena04E580B9
- - -
Certificate Extensions
+ +
Certificate Extensions
@@ -234,128 +150,183 @@
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - -
authorityKeyIdentifierkey_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None
subjectKeyIdentifierdigest=37d90a274635e963081520f98411bda240d30252
basicConstraintsca=False, path_length=None
keyUsagedigital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False
+ + + authorityKeyIdentifier + key_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None + + + + subjectKeyIdentifier + digest=37d90a274635e963081520f98411bda240d30252 + + + + basicConstraints + ca=False, path_length=None + + + + keyUsage + digital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False + + + + + + + +
+ +
+
+
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
- - +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpiryLengthTypePort numberSigned by
2119-02-05 00:00:00619EC443AthenaProcessor685E1CCB6ECB
+ + +
+
+
Certificate Information
+ + + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byVersion3 (0x2)
2119-02-05 00:00:00619EC443AthenaProcessor685E1CCB6ECBSignature Alg.ecdsa-with-SHA256
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueValidity from2019-03-01 00:00:00
Version3 (0x2)
Signature Alg.ecdsa-with-SHA256
Validity from2019-03-01 00:00:00
Valid to2119-02-05 00:00:00
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - + + +
PropertyValueValid to2119-02-05 00:00:00
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
+
+
+
Subject Information
+ + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CNIPLServer4E580B9PropertyValue
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
CNIPLServer4E580B9
- - -
Certificate Extensions
+ +
Certificate Extensions
@@ -364,128 +335,183 @@
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - -
keyUsagedigital_signature=True, key_cert_sign=False, key_encipherment=True, crl_sign=False
extendedKeyUsageserverAuth, Unknown OID
authorityKeyIdentifierkey_identifier=dff100033b0ab36497bbcd2f3e0515ea7b2f7ea0, authority_cert_issuer=None, authority_cert_serial_number=None
subjectAltNameIPLServer4E580B9
+ + + keyUsage + digital_signature=True, key_cert_sign=False, key_encipherment=True, crl_sign=False + + + + extendedKeyUsage + serverAuth, Unknown OID + + + + authorityKeyIdentifier + key_identifier=dff100033b0ab36497bbcd2f3e0515ea7b2f7ea0, authority_cert_issuer=None, authority_cert_serial_number=None + + + + subjectAltName + IPLServer4E580B9 + + + + + +
- - + + +
+ +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC47188None
+ + +
+
+
Certificate Information
+ + + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byVersion3 (0x2)
2049-12-31 23:59:59779EC47188NoneSignature Alg.sha256WithRSAEncryption
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueValidity from2023-03-29 18:37:51
Version3 (0x2)
Signature Alg.sha256WithRSAEncryption
Validity from2023-03-29 18:37:51
Valid to2049-12-31 23:59:59
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - + + +
PropertyValueValid to2049-12-31 23:59:59
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
+
+
+
Subject Information
+ + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CNathena04E580B9PropertyValue
CUS
STPennsylvania
LCoopersburg
OLutron Electronics Co.\, Inc.
CNathena04E580B9
- - -
Certificate Extensions
+ +
Certificate Extensions
@@ -494,45 +520,137 @@
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - -
authorityKeyIdentifierkey_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None
subjectKeyIdentifierdigest=37d90a274635e963081520f98411bda240d30252
basicConstraintsca=False, path_length=None
keyUsagedigital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False
-

Outbound Connections

+ + + authorityKeyIdentifier + key_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None + + + + subjectKeyIdentifier + digest=37d90a274635e963081520f98411bda240d30252 + + + + basicConstraints + ca=False, path_length=None + + + + keyUsage + digital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False + + + + + + + +
+ + +
+
+ +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ + + + + + + + + + + + + +
+ + +

Outbound Connections

- - + + + + + - - - - - - - - - - -
Destination IPPortDestination IPPort
224.0.0.2515353
209.244.0.3Unknown
3.227.250.136443
3.227.203.88443
34.226.101.2528883
3.227.250.208443
52.94.225.110443
- \ No newline at end of file + + + + 224.0.0.251 + 5353 + + + + 209.244.0.3 + Unknown + + + + 3.227.250.136 + 443 + + + + 3.227.203.88 + 443 + + + + 34.226.101.252 + 8883 + + + + 3.227.250.208 + 443 + + + + 52.94.225.110 + 443 + + + + + + +
+ + +
+
diff --git a/testing/unit/tls/reports/tls_report_no_cert_local.html b/testing/unit/tls/reports/tls_report_no_cert_local.html index c025ee9e8..ccc3172fd 100644 --- a/testing/unit/tls/reports/tls_report_no_cert_local.html +++ b/testing/unit/tls/reports/tls_report_no_cert_local.html @@ -1,5 +1,55 @@ -

TLS Module

-
-
- No TLS certificates found on the device -
\ No newline at end of file + +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

TLS Module

+ + + + + + + + + + + + +
+ + +
+
+ No TLS certificates found on the device +
+ + +
+ + +
+
diff --git a/testing/unit/tls/reports/tls_report_single.html b/testing/unit/tls/reports/tls_report_single.html index 0f6a5a4d6..a009ab286 100644 --- a/testing/unit/tls/reports/tls_report_single.html +++ b/testing/unit/tls/reports/tls_report_single.html @@ -1,220 +1,158 @@ -

TLS Module

- - +
+
+
+ {# Badge #} +

+ {% if json_data['device']['test_pack'] == 'Device Qualification' %} + + Device Qualification + {% else %} + + Pilot Assessment + {% endif %} +

+ {{ title }} +
+ + {{ device['manufacturer'] }} + {{ device['model']}} + + Testrun +
+ +
+ +

TLS Module

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpiryLengthTypePort numberSigned by
2027-09-21 19:57:57901RSA443BuildingsIoT RSA Signing CA
+ + +
+
+
Certificate Information
+ + + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byVersion1 (0x0)
2027-09-21 19:57:57901RSA443BuildingsIoT RSA Signing CASignature Alg.sha256WithRSAEncryption
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueValidity from2022-09-21 19:57:57
Version1 (0x0)
Signature Alg.sha256WithRSAEncryption
Validity from2022-09-21 19:57:57
Valid to2027-09-21 19:57:57
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
PropertyValueValid to2027-09-21 19:57:57
CUS
STCalifornia
LConcord
OBuildingsIoT
OUSoftware
CNEasyIO_FS-32
-
-
- - - - - - + +
+
Subject Information
+
+ + + + + + + + + + + - - - - - + + - - - + - - - - - + + - -
PropertyValue
ExpiryLengthTypePort numberSigned byCUS
2027-09-21 19:57:57901RSA443BuildingsIoT RSA Signing CASTCalifornia
- -
-
-
Certificate Information
- - - - + + - - - - - - - - - - - - - - - - - - - -
PropertyValueLConcord
Version1 (0x0)
Signature Alg.sha256WithRSAEncryption
Validity from2022-09-21 19:57:57
Valid to2027-09-21 19:57:57
- -
-
-
Subject Information
- - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + +
PropertyValueOBuildingsIoT
CUS
STCalifornia
LConcord
OBuildingsIoT
OUSoftware
CNEasyIO_FS-32
OUSoftware
CNEasyIO_FS-32
+ -

Outbound Connections

- - - - - - - - - - - -
Destination IPPort
- \ No newline at end of file +
+ + +
+
From e7e3834904250956a51be2b3829a2d7a7322cee4 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 15 Jan 2025 14:07:17 +0100 Subject: [PATCH 39/52] fix outbound xonnections empty summary table --- resources/report/module_report_base.jinja2 | 34 +-- .../tls/reports/tls_report_ext_local.html | 64 +++--- .../unit/tls/reports/tls_report_local.html | 206 +++++++++--------- .../tls/reports/tls_report_no_cert_local.html | 12 +- .../unit/tls/reports/tls_report_single.html | 64 +++--- 5 files changed, 185 insertions(+), 195 deletions(-) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 index 009b7646d..2f7a02228 100644 --- a/resources/report/module_report_base.jinja2 +++ b/resources/report/module_report_base.jinja2 @@ -25,24 +25,26 @@ {% if module_header %}

{{ module_header }}

- {% else %} + {% elif summary_headers %}
{% endif %} - - - {% for header in summary_headers %} - - {% endfor %} - - - - - {% for cell in summary_data %} - - {% endfor %} - - -
{{ header }}
{{ cell }}
+ {% if summary_headers %} + + + {% for header in summary_headers %} + {{ header }} + {% endfor %} + + + + + {% for cell in summary_data %} + {{ cell }} + {% endfor %} + + + + {% endif %} {% block content %}{% endblock content %}
{% raw %} diff --git a/testing/unit/tls/reports/tls_report_ext_local.html b/testing/unit/tls/reports/tls_report_ext_local.html index b46e31925..88a1033af 100644 --- a/testing/unit/tls/reports/tls_report_ext_local.html +++ b/testing/unit/tls/reports/tls_report_ext_local.html @@ -26,37 +26,39 @@

TLS Module

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2027-07-25 15:33:09888EC443Sub CA
+ + + + + Expiry + + Length + + Type + + Port number + + Signed by + + + + + + + 2027-07-25 15:33:09 + + 888 + + EC + + 443 + + Sub CA + + + + +
diff --git a/testing/unit/tls/reports/tls_report_local.html b/testing/unit/tls/reports/tls_report_local.html index 403cccf67..810745253 100644 --- a/testing/unit/tls/reports/tls_report_local.html +++ b/testing/unit/tls/reports/tls_report_local.html @@ -26,37 +26,39 @@

TLS Module

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC35288None
+ + + + + Expiry + + Length + + Type + + Port number + + Signed by + + + + + + + 2049-12-31 23:59:59 + + 779 + + EC + + 35288 + + None + + + + +
@@ -211,37 +213,39 @@
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2119-02-05 00:00:00619EC443AthenaProcessor685E1CCB6ECB
+ + + + + Expiry + + Length + + Type + + Port number + + Signed by + + + + + + + 2119-02-05 00:00:00 + + 619 + + EC + + 443 + + AthenaProcessor685E1CCB6ECB + + + + +
@@ -396,37 +400,39 @@
Certificate Extensions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC47188None
+ + + + + Expiry + + Length + + Type + + Port number + + Signed by + + + + + + + 2049-12-31 23:59:59 + + 779 + + EC + + 47188 + + None + + + + +
@@ -579,19 +585,7 @@
Certificate Extensions
- - - - - - - - - - - - -
+

Outbound Connections

diff --git a/testing/unit/tls/reports/tls_report_no_cert_local.html b/testing/unit/tls/reports/tls_report_no_cert_local.html index ccc3172fd..2ab01ec15 100644 --- a/testing/unit/tls/reports/tls_report_no_cert_local.html +++ b/testing/unit/tls/reports/tls_report_no_cert_local.html @@ -26,17 +26,7 @@

TLS Module

- - - - - - - - - - -
+
diff --git a/testing/unit/tls/reports/tls_report_single.html b/testing/unit/tls/reports/tls_report_single.html index a009ab286..e1c9f9584 100644 --- a/testing/unit/tls/reports/tls_report_single.html +++ b/testing/unit/tls/reports/tls_report_single.html @@ -26,37 +26,39 @@

TLS Module

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2027-09-21 19:57:57901RSA443BuildingsIoT RSA Signing CA
+ + + + + Expiry + + Length + + Type + + Port number + + Signed by + + + + + + + 2027-09-21 19:57:57 + + 901 + + RSA + + 443 + + BuildingsIoT RSA Signing CA + + + + +
From 2faf145b7091a4f381e2e100959c00665780b8e8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 15 Jan 2025 14:22:50 +0100 Subject: [PATCH 40/52] ntp unit tests fix --- .../unit/ntp/reports/ntp_report_local.html | 56 ++++++++++--------- .../ntp/reports/ntp_report_local_no_ntp.html | 56 ++++++++++--------- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/testing/unit/ntp/reports/ntp_report_local.html b/testing/unit/ntp/reports/ntp_report_local.html index 89e736e17..d4a6e9d99 100644 --- a/testing/unit/ntp/reports/ntp_report_local.html +++ b/testing/unit/ntp/reports/ntp_report_local.html @@ -26,33 +26,35 @@

NTP Module

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
+ + + + + Requests to local NTP server + + Requests to external NTP servers + + Total NTP requests + + Total NTP responses + + + + + + + 63 + + 38 + + 101 + + 104 + + + + + diff --git a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html index abe616a38..a47d73614 100644 --- a/testing/unit/ntp/reports/ntp_report_local_no_ntp.html +++ b/testing/unit/ntp/reports/ntp_report_local_no_ntp.html @@ -26,33 +26,35 @@

NTP Module

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
+ + + + + Requests to local NTP server + + Requests to external NTP servers + + Total NTP requests + + Total NTP responses + + + + + + + 0 + + 0 + + 0 + + 0 + + + + +
From b69fd5c43640b50f824a7507496ec76a4c81ccd9 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 15 Jan 2025 14:29:31 +0100 Subject: [PATCH 41/52] dns module unit tests fix --- .../unit/dns/reports/dns_report_local.html | 56 ++++++++++--------- .../dns/reports/dns_report_local_no_dns.html | 56 ++++++++++--------- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/testing/unit/dns/reports/dns_report_local.html b/testing/unit/dns/reports/dns_report_local.html index 9c4e6c90a..f02b144a0 100644 --- a/testing/unit/dns/reports/dns_report_local.html +++ b/testing/unit/dns/reports/dns_report_local.html @@ -26,33 +26,35 @@

DNS Module

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
7107184
+ + + + + Requests to local DNS server + + Requests to external DNS servers + + Total DNS requests + + Total DNS responses + + + + + + + 71 + + 0 + + 71 + + 84 + + + + + diff --git a/testing/unit/dns/reports/dns_report_local_no_dns.html b/testing/unit/dns/reports/dns_report_local_no_dns.html index f9dee2020..183b8c97a 100644 --- a/testing/unit/dns/reports/dns_report_local_no_dns.html +++ b/testing/unit/dns/reports/dns_report_local_no_dns.html @@ -26,33 +26,35 @@

DNS Module

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
0000
+ + + + + Requests to local DNS server + + Requests to external DNS servers + + Total DNS requests + + Total DNS responses + + + + + + + 0 + + 0 + + 0 + + 0 + + + + +
From 951de1b3402c2c020807ddf5de548c5c5199fd97 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 15 Jan 2025 14:36:51 +0100 Subject: [PATCH 42/52] services unit tests fix --- .../services_report_all_closed_local.html | 48 ++++++++++--------- .../reports/services_report_local.html | 48 ++++++++++--------- 2 files changed, 50 insertions(+), 46 deletions(-) diff --git a/testing/unit/services/reports/services_report_all_closed_local.html b/testing/unit/services/reports/services_report_all_closed_local.html index 01ac50676..f2c20aef0 100644 --- a/testing/unit/services/reports/services_report_all_closed_local.html +++ b/testing/unit/services/reports/services_report_all_closed_local.html @@ -26,29 +26,31 @@

Services Module

- - - - - - - - - - - - - - - - - - - - - - -
TCP ports openUDP ports openTotal ports open
000
+ + + + + TCP ports open + + UDP ports open + + Total ports open + + + + + + + 0 + + 0 + + 0 + + + + +
diff --git a/testing/unit/services/reports/services_report_local.html b/testing/unit/services/reports/services_report_local.html index 71f929aeb..899bb4692 100644 --- a/testing/unit/services/reports/services_report_local.html +++ b/testing/unit/services/reports/services_report_local.html @@ -26,29 +26,31 @@

Services Module

- - - - - - - - - - - - - - - - - - - - - - -
TCP ports openUDP ports openTotal ports open
303
+ + + + + TCP ports open + + UDP ports open + + Total ports open + + + + + + + 3 + + 0 + + 3 + + + + + From 95dbf1325dfe0119a6c9921da1d9f23d76fac6f9 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 23 Jan 2025 11:11:18 +0100 Subject: [PATCH 43/52] remove unused table tag --- resources/report/module_report_base.jinja2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/report/module_report_base.jinja2 b/resources/report/module_report_base.jinja2 index 2f7a02228..c86c94b16 100644 --- a/resources/report/module_report_base.jinja2 +++ b/resources/report/module_report_base.jinja2 @@ -24,7 +24,9 @@
{% if module_header %}

{{ module_header }}

-
+ {% if summary_headers %} +
+ {% endif %} {% elif summary_headers %}
{% endif %} From 5773df1fc34b03769ea586c699056e9032ad3790 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 23 Jan 2025 11:13:33 +0100 Subject: [PATCH 44/52] remove unused code --- framework/python/src/common/testreport.py | 115 +--------------------- 1 file changed, 1 insertion(+), 114 deletions(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index df31a5441..1ee4baedf 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -248,7 +248,7 @@ def to_html(self): # Obtain optional recommendations optional_steps_to_resolve = self._get_optional_steps_to_resolve(json_data) - module_reports = self._get_module_pages() + module_reports = self._module_reports env_module = Environment(loader=BaseLoader()) pages_num = self._pages_num(json_data) module_templates = [ @@ -351,116 +351,3 @@ def _get_optional_steps_to_resolve(self, json_data): tests_with_recommendations.append(test) return tests_with_recommendations - - - def _split_module_report_to_pages(self, reports): - """Split report to pages by headers""" - reports_transformed = [] - - for report in reports: - if len(re.findall('
1: - indices = [] - index = report.find('
= content_max_size: - str_el = '' - if current_size > (content_max_size - 85 - module_summary_padding): - rows = el.findChildren('tr', recursive=True) - table_header = str(rows.pop(0)) - table_1 = table_2 = f''' -
- {table_header}''' - rows_count = (content_max_size - 85 - module_summary_padding) // 42 - table_1 += ''.join(map(str, rows[:rows_count-1])) - table_1 += '
' - table_2 += ''.join(map(str, rows[rows_count-1:])) - table_2 += '' - page_content += table_1 - reports.append(page_content) - page_content = table_2 - current_size = len(rows[rows_count:]) * 42 - else: - if el.name == 'table': - el_header = children[index-1] - if el_header.name.startswith('h'): - page_content = ''.join(page_content.rsplit(str(el_header), 1)) - str_el = str(el_header) + str(el) - content_size = current_size + 50 - else: - str_el = str(el) - content_size = current_size - reports.append(page_content) - page_content = str_el - else: - page_content += str(el) - content_size += current_size - reports.append(page_content) - return reports From 29d7cb2d70ccf0882deeccd5725ecb538d6a5e7a Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 23 Jan 2025 11:19:09 +0100 Subject: [PATCH 45/52] pylint --- framework/python/src/common/testreport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/python/src/common/testreport.py b/framework/python/src/common/testreport.py index 1ee4baedf..4dd029d8f 100644 --- a/framework/python/src/common/testreport.py +++ b/framework/python/src/common/testreport.py @@ -23,7 +23,6 @@ from test_orc.test_case import TestCase from jinja2 import Environment, FileSystemLoader, BaseLoader from collections import OrderedDict -import re from bs4 import BeautifulSoup From a6b441d7e599a063b8d7e3bb80fd41face06e65b Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Thu, 23 Jan 2025 13:20:18 +0100 Subject: [PATCH 46/52] remove blank lines from tls module report --- modules/test/tls/python/src/tls_module.py | 8 +- .../tls/reports/tls_report_ext_local.html | 41 +---- .../unit/tls/reports/tls_report_local.html | 155 +----------------- .../tls/reports/tls_report_no_cert_local.html | 9 - .../unit/tls/reports/tls_report_single.html | 42 +---- 5 files changed, 9 insertions(+), 246 deletions(-) diff --git a/modules/test/tls/python/src/tls_module.py b/modules/test/tls/python/src/tls_module.py index ace90ceaf..f169460f7 100644 --- a/modules/test/tls/python/src/tls_module.py +++ b/modules/test/tls/python/src/tls_module.py @@ -39,7 +39,7 @@ class TLSModule(TestModule): """The TLS testing module.""" - def __init__(self, + def __init__(self, # pylint: disable=R0917 module, conf_file=None, results_dir=None, @@ -60,7 +60,11 @@ def __init__(self, def generate_module_report(self): # Load Jinja2 template loader=FileSystemLoader(self._report_template_folder) - template = Environment(loader=loader).get_template(REPORT_TEMPLATE_FILE) + template = Environment( + loader=loader, + trim_blocks=True, + lstrip_blocks=True + ).get_template(REPORT_TEMPLATE_FILE) module_header='TLS Module' # Summary table headers summary_headers = [ diff --git a/testing/unit/tls/reports/tls_report_ext_local.html b/testing/unit/tls/reports/tls_report_ext_local.html index 88a1033af..41d112699 100644 --- a/testing/unit/tls/reports/tls_report_ext_local.html +++ b/testing/unit/tls/reports/tls_report_ext_local.html @@ -20,82 +20,55 @@ Testrun
-
-

TLS Module

- - - +
- - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2027-07-25 15:33:09888EC443Sub CA
- - -
Certificate Information
- - - - - - - -
PropertyValue
Version 3 (0x2)
Signature Alg. sha256WithRSAEncryption
Validity from 2022-07-26 15:33:09
Valid to 2027-07-25 15:33:09
@@ -104,30 +77,23 @@
Subject Information
- - - - - -
PropertyValue
C US
CN apc27D605.nam.gad.schneider-electric.com
-
Certificate Extensions
@@ -137,17 +103,12 @@
Certificate Extensions
- -
subjectAltName ap9643_qa1941270129.nam.gad.schneider-electric.com
- - -
-
-

TLS Module

- - - +
- - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC35288None
- - -
Certificate Information
- - - - - - - -
PropertyValue
Version 3 (0x2)
Signature Alg. sha256WithRSAEncryption
Validity from 2023-03-29 18:37:51
Valid to 2049-12-31 23:59:59
@@ -104,45 +77,35 @@
Subject Information
- - - - - - - - -
PropertyValue
C US
ST Pennsylvania
L Coopersburg
O Lutron Electronics Co.\, Inc.
CN athena04E580B9
-
Certificate Extensions
@@ -152,32 +115,24 @@
Certificate Extensions
- - - - -
authorityKeyIdentifier key_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None
subjectKeyIdentifier digest=37d90a274635e963081520f98411bda240d30252
basicConstraints ca=False, path_length=None
keyUsage digital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False
- - -
-
- - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2119-02-05 00:00:00619EC443AthenaProcessor685E1CCB6ECB
- - -
Certificate Information
- - - - - - - -
PropertyValue
Version 3 (0x2)
Signature Alg. ecdsa-with-SHA256
Validity from 2019-03-01 00:00:00
Valid to 2119-02-05 00:00:00
@@ -291,45 +219,35 @@
Subject Information
- - - - - - - - -
PropertyValue
C US
ST Pennsylvania
L Coopersburg
O Lutron Electronics Co.\, Inc.
CN IPLServer4E580B9
-
Certificate Extensions
@@ -339,32 +257,24 @@
Certificate Extensions
- - - - -
keyUsage digital_signature=True, key_cert_sign=False, key_encipherment=True, crl_sign=False
extendedKeyUsage serverAuth, Unknown OID
authorityKeyIdentifier key_identifier=dff100033b0ab36497bbcd2f3e0515ea7b2f7ea0, authority_cert_issuer=None, authority_cert_serial_number=None
subjectAltName IPLServer4E580B9
- - -
-
- - - - - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2049-12-31 23:59:59779EC47188None
- - -
Certificate Information
- - - - - - - -
PropertyValue
Version 3 (0x2)
Signature Alg. sha256WithRSAEncryption
Validity from 2023-03-29 18:37:51
Valid to 2049-12-31 23:59:59
@@ -478,45 +361,35 @@
Subject Information
- - - - - - - - -
PropertyValue
C US
ST Pennsylvania
L Coopersburg
O Lutron Electronics Co.\, Inc.
CN athena04E580B9
-
Certificate Extensions
@@ -526,32 +399,24 @@
Certificate Extensions
- - - - -
authorityKeyIdentifier key_identifier=accca4f9bd2a47dae81a8f4c87ed2c8edcfd07bf, authority_cert_issuer=None, authority_cert_serial_number=None
subjectKeyIdentifier digest=37d90a274635e963081520f98411bda240d30252
basicConstraints ca=False, path_length=None
keyUsage digital_signature=True, key_cert_sign=False, key_encipherment=False, crl_sign=False
- - -
-
- - - -

Outbound Connections

- - - - - - - - - - -
Destination IPPort
224.0.0.251 5353
209.244.0.3 Unknown
3.227.250.136 443
3.227.203.88 443
34.226.101.252 8883
3.227.250.208 443
52.94.225.110 443
- -
-
-

TLS Module

- - - - -
No TLS certificates found on the device
- - -
-

TLS Module

-
- - +
- - - - - - - - - - - -
ExpiryLengthTypePort numberSigned by
2027-09-21 19:57:57901RSA443BuildingsIoT RSA Signing CA
- - -
Certificate Information
- - - - - - - -
PropertyValue
Version 1 (0x0)
Signature Alg. sha256WithRSAEncryption
Validity from 2022-09-21 19:57:57
Valid to 2027-09-21 19:57:57
@@ -104,52 +77,39 @@
Subject Information
- - - - - - - - - -
PropertyValue
C US
ST California
L Concord
O BuildingsIoT
OU Software
CN EasyIO_FS-32
- - -
-
-

Services Module

- - - +
- - - - - - - -
TCP ports openUDP ports openTotal ports open
000
- - -
No open ports detected
- -
-
-

Services Module

- - - +
- - - - - - - -
TCP ports openUDP ports openTotal ports open
303
- - - - - - - - - - - -
PortStateServiceVersion
22/tcp open ssh 8.8 protocol 2.0
443/tcp open http
502/tcp open mbap
- -
-
-

NTP Module

- - - +
- - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
6338101104
- - - - - - - - - - - @@ -85,7 +60,6 @@

NTP Module

- @@ -94,7 +68,6 @@

NTP Module

- @@ -103,7 +76,6 @@

NTP Module

- @@ -112,7 +84,6 @@

NTP Module

- @@ -121,7 +92,6 @@

NTP Module

- @@ -130,7 +100,6 @@

NTP Module

- @@ -139,7 +108,6 @@

NTP Module

- @@ -148,7 +116,6 @@

NTP Module

- @@ -157,7 +124,6 @@

NTP Module

- @@ -166,11 +132,8 @@

NTP Module

-
SourceDestinationTypeVersionCountSync Request Average
10.10.10.15 216.239.35.12 8 37.942 seconds
216.239.35.12 10.10.10.15 8 N/A
10.10.10.15 216.239.35.4 8 37.834 seconds
216.239.35.4 10.10.10.15 8 N/A
10.10.10.15 216.239.35.8 8 38.056 seconds
216.239.35.8 10.10.10.15 8 N/A
10.10.10.15 216.239.35.0 14 20.601 seconds
216.239.35.0 10.10.10.15 17 N/A
10.10.10.15 10.10.10.5 63 13.057 seconds
10.10.10.5 10.10.10.15 63 N/A
- -
-
-

NTP Module

- - - +
- - - - - - - - - -
Requests to local NTP serverRequests to external NTP serversTotal NTP requestsTotal NTP responses
0000
- - -
No NTP traffic detected from the device
- -
-
-

DNS Module

- - - +
- - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
7107184
- - - - - - - - - - - @@ -85,7 +60,6 @@

DNS Module

- @@ -94,7 +68,6 @@

DNS Module

- @@ -103,7 +76,6 @@

DNS Module

- @@ -112,7 +84,6 @@

DNS Module

- @@ -121,7 +92,6 @@

DNS Module

- @@ -130,7 +100,6 @@

DNS Module

- @@ -139,7 +108,6 @@

DNS Module

- @@ -148,11 +116,8 @@

DNS Module

-
SourceDestinationResolved IPTypeURLCount
10.10.10.14
10.10.10.4
mqtt.googleapis.com
64
10.10.10.4
10.10.10.14
mqtt.googleapis.com
38
10.10.10.4
10.10.10.14
mqtt.googleapis.com
32
10.10.10.14
10.10.10.4
pool.ntp.org
7
10.10.10.4
10.10.10.14
pool.ntp.org
4
10.10.10.4
10.10.10.14
pool.ntp.org
2
10.10.10.4
10.10.10.14
pool.ntp.org
2
10.10.10.4
10.10.10.14
mqtt.googleapis.com
6
- -
-
-

DNS Module

- - - +
- - - - - - - - - -
Requests to local DNS serverRequests to external DNS serversTotal DNS requestsTotal DNS responses
0000
- - -
No DNS traffic detected from the device
- -