Skip to content
Snippets Groups Projects
Commit 827aa432 authored by shadow's avatar shadow
Browse files

vulnerability descriptions with urls and markdown report generator

parent 520e7bb3
Branches
Tags
No related merge requests found
from .description_provider import VulnDescriptionProvider from .description_provider import VulnDescription, VulnDescriptionProvider
from .cveproject import CveProjectProvider from .cveproject import CveProjectProvider
from requests import Session, HTTPError from requests import Session, HTTPError
from contrib.descriptions import VulnDescriptionProvider from contrib.descriptions import VulnDescriptionProvider, VulnDescription
__all__ = ['CveProjectProvider'] __all__ = ['CveProjectProvider']
...@@ -16,7 +15,7 @@ class CveProjectProvider(VulnDescriptionProvider): ...@@ -16,7 +15,7 @@ class CveProjectProvider(VulnDescriptionProvider):
self.sess = session self.sess = session
self.cache = {} self.cache = {}
def get_description(self, vuln: str, vuln_type: str) -> str: def get_description(self, vuln: str, vuln_type: str) -> VulnDescription:
if vuln in self.cache: if vuln in self.cache:
return self.cache[vuln] return self.cache[vuln]
...@@ -30,8 +29,8 @@ class CveProjectProvider(VulnDescriptionProvider): ...@@ -30,8 +29,8 @@ class CveProjectProvider(VulnDescriptionProvider):
cve_json = response.json() cve_json = response.json()
description = cve_json['description']['description_data'][0]['value'] description = cve_json['description']['description_data'][0]['value']
self.cache[vuln] = description self.cache[vuln] = description
return description return VulnDescription(description, url)
except HTTPError as he: except HTTPError as he:
return 'Description fetching error: ' + str(he) return VulnDescription('', 'Description fetching error: ' + str(he))
return '' return VulnDescription('', '')
import abc import abc
from typing import Optional
__all__ = ['VulnDescriptionProvider'] __all__ = ['VulnDescriptionProvider', 'VulnDescription']
class VulnDescription:
def __init__(self, text: str, url: Optional[str] = None):
self.text = text
self.url = url
class VulnDescriptionProvider(metaclass=abc.ABCMeta): class VulnDescriptionProvider(metaclass=abc.ABCMeta):
...@@ -8,5 +15,5 @@ class VulnDescriptionProvider(metaclass=abc.ABCMeta): ...@@ -8,5 +15,5 @@ class VulnDescriptionProvider(metaclass=abc.ABCMeta):
Provides extended vulnerability description by vulnerablity identifier and type Provides extended vulnerability description by vulnerablity identifier and type
""" """
@abc.abstractmethod @abc.abstractmethod
def get_description(self, vuln: str, vuln_type: str) -> str: def get_description(self, vuln: str, vuln_type: str) -> VulnDescription:
pass pass
from collections import defaultdict from collections import defaultdict
from typing import List, Dict
__all__ = ['SeverityLevels', 'Vuln', 'ScanResult'] __all__ = ['SeverityLevels', 'Vuln', 'ScanResult']
...@@ -46,5 +46,5 @@ class ScanResult: ...@@ -46,5 +46,5 @@ class ScanResult:
Scan result representation Scan result representation
""" """
def __init__(self): def __init__(self):
self.locations = defaultdict(list) self.locations = defaultdict(list) # type: Dict[str, List[str]]
self.vulns = [] self.vulns = [] # type: List[Vuln]
from .report_builder import ReportBuilder from .report_builder import ReportBuilder
from .latex_report_builder import LatexReportBuilder from .latex_report_builder import LatexReportBuilder
from .markdown_report_builder import MarkdownReportBuilder
...@@ -42,7 +42,8 @@ class LatexReportBuilder(ReportBuilder): ...@@ -42,7 +42,8 @@ class LatexReportBuilder(ReportBuilder):
locations = report.locations locations = report.locations
num_vulns = len(vulns) num_vulns = len(vulns)
for i, v in enumerate(vulns): for v in vulns:
description = self.description_provider.get_description(v.name, v.vuln_type)
severity_name = v.severity_str severity_name = v.severity_str
self._append('\\begin{figure}[h!]\n') self._append('\\begin{figure}[h!]\n')
self._append('\\begin{tabular}{|p{16cm}|}\\rowcolor[HTML]{' self._append('\\begin{tabular}{|p{16cm}|}\\rowcolor[HTML]{'
...@@ -50,10 +51,10 @@ class LatexReportBuilder(ReportBuilder): ...@@ -50,10 +51,10 @@ class LatexReportBuilder(ReportBuilder):
+ '} \\begin{tabular}{@{}p{15cm}>{\\raggedleft\\arraybackslash} p{0.5cm}@{}}\\textbf{' + '} \\begin{tabular}{@{}p{15cm}>{\\raggedleft\\arraybackslash} p{0.5cm}@{}}\\textbf{'
+ v.name + ' ' + severity_name + ' (' + v.name + ' ' + severity_name + ' ('
+ str(v.severity) + str(v.severity)
+ ')} & \href{https://nvd.nist.gov/vuln/detail/' + ')} & \href{' + description.url
+ v.name + '}{\large \\faicon{link}}' + '}{\large \\faicon{link}}'
+ '\end{tabular}\\\\\n Summary:' + '\end{tabular}\\\\\n Summary:'
+ self.description_provider.get_description(v.name, v.vuln_type) + description.text
+ '\\\\ \hline \end{tabular} ') + '\\\\ \hline \end{tabular} ')
self._append('\end{figure}\n') self._append('\end{figure}\n')
......
from datetime import datetime
from typing import Any, Dict, List
from contrib.descriptions import VulnDescriptionProvider
from contrib.internal_types import ScanResult
from contrib.report_builders import ReportBuilder
__all__ = ['MarkdownReportBuilder']
class MarkdownReportBuilder(ReportBuilder):
def __init__(self, description_provider: VulnDescriptionProvider):
self.description_provider = description_provider
self._buffer = ''
def init_report(self, start_date: str, nmap_command: str):
self._append_line(self.header)
self._append_line('## {date:%B %d, %Y}'.format(date=datetime.utcnow()))
self._append_line('### **Summary**')
self._append_line('Flan Scan ran a network vulnerability scan with the following Nmap command on {date}'
.format(date=start_date))
self._append_line('`{command}`'.format(command=nmap_command))
def build(self) -> Any:
return self._buffer
def add_vulnerable_section(self):
self._append_line('### Services with vulnerabilities')
def add_non_vulnerable_section(self):
self._append_line('### Services with no *known* vulnerabilities')
def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
for i, pair in enumerate(scan_results.items(), start=1):
app_name, report = pair # type: str, ScanResult
self._append_service(i, app_name)
num_vulns = len(report.vulns)
for v in report.vulns:
description = self.description_provider.get_description(v.name, v.vuln_type)
self._append_line('- [**{name}** {severity} ({severity_num})]({link} "{title}")'
.format(name=v.name, severity=v.severity_str, severity_num=v.severity,
link=description.url, title=v.name), spaces=4)
self._append_line('```text', separators=1, spaces=6)
self._append_line(description.text, separators=1, spaces=6)
self._append_line('```', spaces=6)
self._append_line('The above {num} vulnerabilities apply to these network locations'.format(num=num_vulns),
spaces=4)
self._append_line('```text', separators=1, spaces=4)
for addr, ports in report.locations.items():
self._append_location(addr, ports, spaces=4)
self._append_line('```', spaces=4)
def add_non_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
for i, pair in enumerate(scan_results.items(), start=1):
app_name, report = pair # type: str, ScanResult
self._append_service(i, app_name)
for addr, ports in report.locations.items():
self._append_location(addr, ports, spaces=4)
self._append('\n')
def initialize_section(self):
pass
def add_ips_section(self):
self._append_line('### List of IPs Scanned')
def add_ip_address(self, ip: str):
self._append_line('- {ip}'.format(ip=ip), separators=1)
def finalize(self):
pass
@property
def header(self) -> Any:
return '# Flan scan report'
def _append(self, text: str, spaces: int = 0):
if spaces:
self._buffer += ' ' * spaces
self._buffer += text
def _append_line(self, text: str, separators: int = 2, spaces: int = 0):
self._append(text, spaces)
self._append('\n' * separators)
def _append_service(self, index: int, name: str, spaces: int = 0):
self._append_line('{index}. **{service}**'.format(index=index, service=name.strip()), spaces=spaces,
separators=1)
def _append_location(self, address: str, ports: List[str], spaces: int):
self._append_line('- {address} Ports: {ports}'.format(address=address, ports=', '.join(ports)), spaces=spaces,
separators=1)
...@@ -6,7 +6,7 @@ from requests import Session ...@@ -6,7 +6,7 @@ from requests import Session
from contrib.descriptions import CveProjectProvider from contrib.descriptions import CveProjectProvider
from contrib.parsers import FlanXmlParser from contrib.parsers import FlanXmlParser
from contrib.report_builders import ReportBuilder, LatexReportBuilder from contrib.report_builders import ReportBuilder, LatexReportBuilder, MarkdownReportBuilder
def create_report(parser: FlanXmlParser, builder: ReportBuilder, nmap_command: str, start_date: str, output_writer: IO, def create_report(parser: FlanXmlParser, builder: ReportBuilder, nmap_command: str, start_date: str, output_writer: IO,
...@@ -38,12 +38,15 @@ def parse_nmap_command(raw_command: str) -> str: ...@@ -38,12 +38,15 @@ def parse_nmap_command(raw_command: str) -> str:
return ' '.join(nmap_split) return ' '.join(nmap_split)
def create_default_provider():
return CveProjectProvider(Session())
def create_report_builder(report_type: str) -> ReportBuilder: def create_report_builder(report_type: str) -> ReportBuilder:
if report_type == 'latex': if report_type == 'latex':
session = Session() return LatexReportBuilder(create_default_provider())
description_provider = CveProjectProvider(session) if report_type == 'md':
report_bilder = LatexReportBuilder(description_provider) return MarkdownReportBuilder(create_default_provider())
return report_bilder
raise NotImplementedError(report_type) raise NotImplementedError(report_type)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment