Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 205 additions & 113 deletions framework/python/src/common/testreport.py

Large diffs are not rendered by default.

26 changes: 17 additions & 9 deletions framework/python/src/test_orc/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,16 +452,24 @@ def _run_test_module(self, module):
f"Error occurred whilst obtaining results for module {module.name}")
LOGGER.error(results_error)

# Get report from the module
report_file = f"{container_runtime_dir}/{module.name}_report.md"
# Get the markdown report from the module if generated
markdown_file = f"{container_runtime_dir}/{module.name}_report.md"
try:
with open(report_file, "r", encoding="utf-8") as f:
with open(markdown_file, "r", encoding="utf-8") as f:
module_report = f.read()
self._session.add_module_report(module_report)
except (FileNotFoundError, PermissionError) as report_error:
LOGGER.error(
f"Error occurred whilst obtaining report for module {module.name}")
LOGGER.error(report_error)
except (FileNotFoundError, PermissionError):
LOGGER.debug("Test module did not produce a markdown module report")

# Get the HTML report from the module if generated
html_file = f"{container_runtime_dir}/{module.name}_report.html"
try:
with open(html_file, "r", encoding="utf-8") as f:
module_report = f.read()
LOGGER.debug(f"Adding module report for module {module.name}")
self._session.add_module_report(module_report)
except (FileNotFoundError, PermissionError):
LOGGER.debug("Test module did not produce a html module report")

LOGGER.info(f"Test module {module.name} has finished")

Expand Down Expand Up @@ -516,8 +524,8 @@ def _load_test_modules(self):
# Check if the directory protocol exists and move it to the beginning
# protocol should always be run first so BACnet binding doesn't get
# corrupted during DHCP changes in the conn module
if 'protocol' in module_dirs:
module_dirs.insert(0, module_dirs.pop(module_dirs.index('protocol')))
if "protocol" in module_dirs:
module_dirs.insert(0, module_dirs.pop(module_dirs.index("protocol")))

for module_dir in module_dirs:

Expand Down
90 changes: 60 additions & 30 deletions modules/test/dns/python/src/dns_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import os

LOG_NAME = 'test_dns'
MODULE_REPORT_FILE_NAME='dns_report.md'
MODULE_REPORT_FILE_NAME='dns_report.html'
DNS_SERVER_CAPTURE_FILE = '/runtime/network/dns.pcap'
STARTUP_CAPTURE_FILE = '/runtime/device/startup.pcap'
MONITOR_CAPTURE_FILE = '/runtime/device/monitor.pcap'
Expand Down Expand Up @@ -52,60 +52,90 @@ def generate_module_report(self):
# Extract DNS data from the pcap file
dns_table_data = self.extract_dns_data()

html_content = '<h1>DNS Module</h1>'

# Set the summary variables
local_requests = sum(1 for row in dns_table_data
if row['Destination'] == self._dns_server and row['Type'] == 'Query')
if row['Destination'] ==
self._dns_server and row['Type'] == 'Query')
external_requests = sum(1 for row in dns_table_data
if row['Destination'] != self._dns_server and row['Type'] == 'Query')
if row['Destination'] !=
self._dns_server and row['Type'] == 'Query')

total_requests = sum(1 for row in dns_table_data
if row['Type'] == 'Query')

total_responses = sum(1 for row in dns_table_data
if row['Type'] == 'Response')

summary = '## Summary'
summary += f'''\n- Requests to local DNS server: {local_requests}'''
summary += f'''\n- Requests to external DNS servers: {external_requests}'''
summary += f'''\n- Total DNS requests: {total_requests}'''
summary += f'''\n- Total DNS responses: {total_responses}'''
# Add summary table
html_content += (f'''
<table class="module-summary">
<thead>
<tr>
<th>Requests to local DNS server</th>
<th>Requests to external DNS servers</th>
<th>Total DNS requests</th>
<th>Total DNS responses</th>
</tr>
</thead>
<tbody>
<tr>
<td>{local_requests}</td>
<td>{external_requests}</td>
<td>{total_requests}</td>
<td>{total_responses}</td>
</tr>
</table>
''')

if (total_requests + total_responses) > 0:

# Find the maximum length of 'Destination' values
max_data_length = max(len(row['Data']) for row in dns_table_data) if len(dns_table_data)>0 else 8
table_content = '''
<table class="module-data">
<thead>
<tr>
<th>Source</th>
<th>Destination</th>
<th>Type</th>
<th>URL</th>
</tr>
</thead>
<tbody>'''

table_content = ''
for row in dns_table_data:
table_content += (f'''| {row['Source']: ^12} '''
f'''| {row['Destination']: ^13} '''
f'''| {row['Type']: ^9} '''
f'''| {row['Data']: ^{max_data_length}} |\n''')
table_content += (f'''
<tr>
<td>{row['Source']}</td>
<td>{row['Destination']}</td>
<td>{row['Type']}</td>
<td>{row['Data']}</td>
</tr>''')

header = (f'''| {'Source': ^12} '''
f'''| {'Destination': ^{13}} '''
f'''| {'Type': ^{9}} '''
f'''| {'Data': ^{max_data_length}} |''')
header_line = (f'''|{'-' * 14}|{'-' * 15}|{'-' * 11}|'''
f'''{'-' * (max_data_length + 2)}''')
table_content += '''
</tbody>
</table>
'''

markdown_template = (f'''# DNS Module\n'''
f'''\n{header}\n{header_line}\n{table_content}\n{summary}''')
html_content += table_content

else:
markdown_template = (f'''# DNS Module\n'''
f'''\n- No DNS traffic detected\n'''
f'''\n{summary}''')
LOGGER.debug('Markdown Report:\n' + markdown_template)
html_content += ('''
<div class="callout-container info">
<div class="icon"></div>
No DNS traffic detected from the device
</div>''')

LOGGER.debug('Module report:\n' + html_content)

# 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
# Write the content to a file
with open(report_path, 'w', encoding='utf-8') as file:
file.write(markdown_template)
file.write(html_content)

LOGGER.info('Module report generated at: ' + str(report_path))
LOGGER.info('Module report generated at: ' + str(report_path))

return report_path

Expand Down
94 changes: 58 additions & 36 deletions modules/test/nmap/python/src/nmap_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import os

LOG_NAME = 'test_nmap'
MODULE_REPORT_FILE_NAME = 'nmap_report.md'
MODULE_REPORT_FILE_NAME = 'nmap_report.html'
NMAP_SCAN_RESULTS_SCAN_FILE = 'nmap_scan_results.json'
LOGGER = None

Expand Down Expand Up @@ -83,52 +83,74 @@ def generate_module_report(self):
else:
udp_open += 1

summary = '## Summary'
summary += f'''\n- TCP Ports Open: {tcp_open}'''
summary += f'''\n- UDP Ports Open: {udp_open}'''
summary += f'''\n- Total Ports Open: {tcp_open + udp_open}'''
html_content = '<h1>Services Module</h1>'

# Add summary table
html_content += (f'''
<table class="module-summary">
<thead>
<tr>
<th>TCP ports open</th>
<th>UDP ports open</th>
<th>Total ports open</th>
</tr>
</thead>
<tbody>
<tr>
<td>{tcp_open}</td>
<td>{udp_open}</td>
<td>{tcp_open + udp_open}</td>
</tr>
</table>
''')

if (tcp_open + udp_open) > 0:

table_content = '''
<table class="module-data">
<thead>
<tr>
<th>Port</th>
<th>State</th>
<th>Service</th>
<th>Version</th>
</tr>
</thead>
<tbody>'''

if (tcp_open + udp_open)>0:
for row in nmap_table_data:

# Find the maximum column lengths
max_service_length = max(
len(row['Service'])
for row in nmap_table_data) if len(nmap_table_data) > 0 else 15
max_version_length = max(
len(row['Version'])
for row in nmap_table_data) if len(nmap_table_data) > 0 else 15
table_content += (f'''
<tr>
<td>{row['Port']}/{row['Type']}</td>
<td>{row['State']}</td>
<td>{row['Service']}</td>
<td>{row['Version']}</td>
</tr>''')

table_content = ''
for row in nmap_table_data:
table_content += (f'''| {row['Port']: ^10} | {row['Type']: ^10} '''
f'''| {row['State']: ^10} '''
f'''| {row['Service']: ^{max_service_length}} '''
f'''| {row['Version']: ^{max_version_length}} |\n''')

# Dynamically adjust the header width based on the
# longest 'Destination' content
header = (f'''| {'Port': ^10} | {'Type': ^{10}} | {'State': ^{10}} '''
f'''| {'Service': ^{max_service_length}} '''
f'''| {'Version': ^{max_version_length}} |''')
header_line = (f'''|{'-' * 12}|{'-' * 12}|{'-' * 12}|'''
f'''{'-' * (max_service_length + 2)}'''
f'''|{'-' * (max_version_length + 2)}|''')

markdown_template = (f'''# NMAP Module\n'''
f'''\n{header}\n{header_line}\n{table_content}\n{summary}''')
table_content += '''
</tbody>
</table>
'''

html_content += table_content

else:
markdown_template = (f'''# NMAP Module\n'''
f'''\n- No ports detected open\n'''
f'''\n{summary}''')
LOGGER.debug('Markdown Report:\n' + markdown_template)

html_content += ('''
<div class="callout-container info">
<div class="icon"></div>
No open ports detected
</div>''')

LOGGER.debug('Module report:\n' + html_content)

# 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(markdown_template)
file.write(html_content)

LOGGER.info('Module report generated at: ' + str(report_path))

Expand Down
Loading