■ ■ ■ ■ ■ ■
contrib/report_builders/markdown_report_builder.py
| 1 | + | from datetime import datetime |
| 2 | + | from typing import Any, Dict, List |
| 3 | + | |
| 4 | + | from contrib.descriptions import VulnDescriptionProvider |
| 5 | + | from contrib.internal_types import ScanResult |
| 6 | + | from contrib.report_builders import ReportBuilder |
| 7 | + | |
| 8 | + | __all__ = ['MarkdownReportBuilder'] |
| 9 | + | |
| 10 | + | |
| 11 | + | class MarkdownReportBuilder(ReportBuilder): |
| 12 | + | def __init__(self, description_provider: VulnDescriptionProvider): |
| 13 | + | self.description_provider = description_provider |
| 14 | + | self._buffer = '' |
| 15 | + | |
| 16 | + | def init_report(self, start_date: str, nmap_command: str): |
| 17 | + | self._append_line(self.header) |
| 18 | + | self._append_line('## {date:%B %d, %Y}'.format(date=datetime.utcnow())) |
| 19 | + | self._append_line('### **Summary**') |
| 20 | + | self._append_line('Flan Scan ran a network vulnerability scan with the following Nmap command on {date}' |
| 21 | + | .format(date=start_date)) |
| 22 | + | self._append_line('`{command}`'.format(command=nmap_command)) |
| 23 | + | |
| 24 | + | def build(self) -> Any: |
| 25 | + | return self._buffer |
| 26 | + | |
| 27 | + | def add_vulnerable_section(self): |
| 28 | + | self._append_line('### Services with vulnerabilities') |
| 29 | + | |
| 30 | + | def add_non_vulnerable_section(self): |
| 31 | + | self._append_line('### Services with no *known* vulnerabilities') |
| 32 | + | |
| 33 | + | def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]): |
| 34 | + | for i, pair in enumerate(scan_results.items(), start=1): |
| 35 | + | app_name, report = pair # type: str, ScanResult |
| 36 | + | self._append_service(i, app_name) |
| 37 | + | num_vulns = len(report.vulns) |
| 38 | + | |
| 39 | + | for v in report.vulns: |
| 40 | + | description = self.description_provider.get_description(v.name, v.vuln_type) |
| 41 | + | self._append_line('- [**{name}** {severity} ({severity_num})]({link} "{title}")' |
| 42 | + | .format(name=v.name, severity=v.severity_str, severity_num=v.severity, |
| 43 | + | link=description.url, title=v.name), spaces=4) |
| 44 | + | self._append_line('```text', separators=1, spaces=6) |
| 45 | + | self._append_line(description.text, separators=1, spaces=6) |
| 46 | + | self._append_line('```', spaces=6) |
| 47 | + | |
| 48 | + | self._append_line('The above {num} vulnerabilities apply to these network locations'.format(num=num_vulns), |
| 49 | + | spaces=4) |
| 50 | + | self._append_line('```text', separators=1, spaces=4) |
| 51 | + | for addr, ports in report.locations.items(): |
| 52 | + | self._append_location(addr, ports, spaces=4) |
| 53 | + | self._append_line('```', spaces=4) |
| 54 | + | |
| 55 | + | def add_non_vulnerable_services(self, scan_results: Dict[str, ScanResult]): |
| 56 | + | for i, pair in enumerate(scan_results.items(), start=1): |
| 57 | + | app_name, report = pair # type: str, ScanResult |
| 58 | + | self._append_service(i, app_name) |
| 59 | + | |
| 60 | + | for addr, ports in report.locations.items(): |
| 61 | + | self._append_location(addr, ports, spaces=4) |
| 62 | + | self._append('\n') |
| 63 | + | |
| 64 | + | def initialize_section(self): |
| 65 | + | pass |
| 66 | + | |
| 67 | + | def add_ips_section(self): |
| 68 | + | self._append_line('### List of IPs Scanned') |
| 69 | + | |
| 70 | + | def add_ip_address(self, ip: str): |
| 71 | + | self._append_line('- {ip}'.format(ip=ip), separators=1) |
| 72 | + | |
| 73 | + | def finalize(self): |
| 74 | + | pass |
| 75 | + | |
| 76 | + | @property |
| 77 | + | def header(self) -> Any: |
| 78 | + | return '# Flan scan report' |
| 79 | + | |
| 80 | + | def _append(self, text: str, spaces: int = 0): |
| 81 | + | if spaces: |
| 82 | + | self._buffer += ' ' * spaces |
| 83 | + | self._buffer += text |
| 84 | + | |
| 85 | + | def _append_line(self, text: str, separators: int = 2, spaces: int = 0): |
| 86 | + | self._append(text, spaces) |
| 87 | + | self._append('\n' * separators) |
| 88 | + | |
| 89 | + | def _append_service(self, index: int, name: str, spaces: int = 0): |
| 90 | + | self._append_line('{index}. **{service}**'.format(index=index, service=name.strip()), spaces=spaces, |
| 91 | + | separators=1) |
| 92 | + | |
| 93 | + | def _append_location(self, address: str, ports: List[str], spaces: int): |
| 94 | + | self._append_line('- {address} Ports: {ports}'.format(address=address, ports=', '.join(ports)), spaces=spaces, |
| 95 | + | separators=1) |
| 96 | + | |