From 8ba0a59ebb78e420869424263a061211f868f255 Mon Sep 17 00:00:00 2001
From: sw <shadow.bfs@gmail.com>
Date: Sun, 8 Mar 2020 04:23:26 +0300
Subject: [PATCH] new report formats and arguments to use them

---
 Makefile                                  | 11 +++++++-
 README.md                                 |  9 ++++++-
 contrib/internal_types/flan_types.py      |  8 ++++++
 contrib/report_builders/__init__.py       |  2 ++
 contrib/report_builders/report_builder.py | 18 -------------
 kubernetes_templates/cron_job.yaml        |  2 ++
 kubernetes_templates/deployment.yaml      |  2 ++
 output_report.py                          | 31 +++++++++++++++--------
 requirements.txt                          |  1 +
 run.sh                                    | 20 +++++++++++----
 10 files changed, 68 insertions(+), 36 deletions(-)

diff --git a/Makefile b/Makefile
index d073f5b..0ed0bd0 100644
--- a/Makefile
+++ b/Makefile
@@ -3,4 +3,13 @@ build :
 
 container_name = flan_$(shell date +'%s')
 start : 
-	docker run --name $(container_name) -v "$(pwd)/shared:/shared:Z" flan_scan
+	docker run --name $(container_name) -v "$(shell pwd)/shared:/shared:Z" flan_scan
+
+md :
+	docker run --name $(container_name) -v "$(shell pwd)/shared:/shared:Z" -e format=md flan_scan
+
+html :
+	docker run --name $(container_name) -v "$(shell pwd)/shared:/shared:Z" -e format=html flan_scan
+
+json :
+	docker run --name $(container_name) -v "$(shell pwd)/shared:/shared:Z" -e format=json flan_scan
diff --git a/README.md b/README.md
index 260c26f..d144fb3 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,12 @@ $ make build
 $ make start
 ```
 
+6. To use another output format:
+```
+$ make html
+```
+Additional supported formats are *md* (markdown), *html* and *json*.
+
 When the scan finishes you will find a Latex report of the summarizing the scan in `shared/reports`. You can also see the raw XML output from Nmap in `shared/xml_files`.
 
 <div>
@@ -42,7 +48,7 @@ $ nmap -sV -oX /shared/xml_files -oN - -v1 $@ --script=vulners/vulners.nse <ip-a
 ```
 The `-oX` flag adds an XML version of the scan results to the `/shared/xml_files` directory and the `-oN -` flag outputs "normal" Nmap results to the console. The `-v1` flag increases the verbosity to 1 and the `-sV` flag runs a service detection scan (aside from Nmap's default port and SYN scans). The `--script=vulners/vulners.nse` is the script that matches the services detected with relevant CVEs.
 
-Nmap also allows you to run UDP scans and to scan IPv6 addresses. To add these and other flags to Scan Flan's Nmap command after running `make build` run the container and pass in you Nmap flags like so:
+Nmap also allows you to run UDP scans and to scan IPv6 addresses. To add these and other flags to Scan Flan's Nmap command after running `make build` run the container and pass in your Nmap flags like so:
 
 ```bash
 $ docker run -v $(pwd)/shared:/shared flan_scan <Nmap-flags>
@@ -57,6 +63,7 @@ $ docker run --name <container-name> \
              -v $(pwd)/shared:/shared \
              -e upload=<gcp or aws> \
              -e bucket=<bucket-name> \
+             -e format=<optional, one of: md, html or json> \
              flan_scan
 ```
 
diff --git a/contrib/internal_types/flan_types.py b/contrib/internal_types/flan_types.py
index f1c0da4..fb63476 100644
--- a/contrib/internal_types/flan_types.py
+++ b/contrib/internal_types/flan_types.py
@@ -22,6 +22,14 @@ class Vuln:
         self.vuln_type = vuln_type
         self.severity = severity
 
+    def to_dict(self):
+        return {
+            'name': self.name,
+            'type': self.vuln_type,
+            'severity': self.severity,
+            'severity_str': self.severity_str
+        }
+
     @staticmethod
     def convert_severity(severity: float) -> str:
         """
diff --git a/contrib/report_builders/__init__.py b/contrib/report_builders/__init__.py
index 5db2353..ca8cb01 100644
--- a/contrib/report_builders/__init__.py
+++ b/contrib/report_builders/__init__.py
@@ -1,3 +1,5 @@
 from .report_builder import ReportBuilder
 from .latex_report_builder import LatexReportBuilder
 from .markdown_report_builder import MarkdownReportBuilder
+from .json_report_builder import JsonReportBuilder
+from .html_report_builder import JinjaHtmlReportBuilder
diff --git a/contrib/report_builders/report_builder.py b/contrib/report_builders/report_builder.py
index 006b361..ed452cb 100644
--- a/contrib/report_builders/report_builder.py
+++ b/contrib/report_builders/report_builder.py
@@ -8,80 +8,62 @@ __all__ = ['ReportBuilder']
 
 
 class ReportBuilder(metaclass=abc.ABCMeta):
-    @abc.abstractmethod
     def init_report(self, start_date: str, nmap_command: str):
         """
         Creates document section with report overview
         """
         pass
 
-    @abc.abstractmethod
     def build(self) -> Any:
         """
         :return: Ready report in specific format
         """
         pass
 
-    @abc.abstractmethod
     def add_vulnerable_section(self):
         """
         Adds header for section with vulnerable services
         """
         pass
 
-    @abc.abstractmethod
     def add_non_vulnerable_section(self):
         """
         Adds header for section with services without detected vulnerabilities
         """
         pass
 
-    @abc.abstractmethod
     def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
         """
         Adds descriptions of vulnerable services
         """
         pass
 
-    @abc.abstractmethod
     def add_non_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
         """
         Adds descriptions of services without detected vulnerabilities
         """
         pass
 
-    @abc.abstractmethod
     def initialize_section(self):
         """
         Adds begin of report section
         """
         pass
 
-    @abc.abstractmethod
     def add_ips_section(self):
         """
         Adds section with list of scanned ip addresses
         """
         pass
 
-    @abc.abstractmethod
     def add_ip_address(self, ip: str):
         """
         Adds IP-address to scanned addresses section
         """
         pass
 
-    @abc.abstractmethod
     def finalize(self):
         """
         Adds report footer
         """
         pass
-
-    @property
-    @abc.abstractmethod
-    def header(self) -> Any:
-        """
-        :return: Common document header for format type (e.g. for latex report)
-        """
-        pass
diff --git a/kubernetes_templates/cron_job.yaml b/kubernetes_templates/cron_job.yaml
index 36f41f9..b920dda 100644
--- a/kubernetes_templates/cron_job.yaml
+++ b/kubernetes_templates/cron_job.yaml
@@ -33,3 +33,5 @@ spec:
                        value: <GCP_OR_AWS>
                      - name: bucket
                        value: <BUCKET_NAME>
+                     - name: format
+                       value: <REPORT_FORMAT>
diff --git a/kubernetes_templates/deployment.yaml b/kubernetes_templates/deployment.yaml
index e1fa560..d593f24 100644
--- a/kubernetes_templates/deployment.yaml
+++ b/kubernetes_templates/deployment.yaml
@@ -35,3 +35,5 @@ spec:
                value: <GCP_OR_AWS>
              - name: bucket
                value: <BUCKET_NAME>
+             - name: format
+               value: <REPORT_FORMAT>
diff --git a/output_report.py b/output_report.py
index 2f5b45e..42ecc54 100644
--- a/output_report.py
+++ b/output_report.py
@@ -4,9 +4,10 @@ from typing import IO
 
 from requests import Session
 
-from contrib.descriptions import CveProjectProvider
+from contrib.descriptions import CveProjectProvider, VulnDescriptionProvider
 from contrib.parsers import FlanXmlParser
-from contrib.report_builders import ReportBuilder, LatexReportBuilder, MarkdownReportBuilder
+from contrib.report_builders import ReportBuilder, LatexReportBuilder, MarkdownReportBuilder, JinjaHtmlReportBuilder, \
+    JsonReportBuilder
 
 
 def create_report(parser: FlanXmlParser, builder: ReportBuilder, nmap_command: str, start_date: str, output_writer: IO,
@@ -26,7 +27,7 @@ def create_report(parser: FlanXmlParser, builder: ReportBuilder, nmap_command: s
 
     builder.add_ips_section()
     for ip in ip_reader:
-        builder.add_ip_address(ip)
+        builder.add_ip_address(ip.strip())
 
     builder.finalize()
     output_writer.write(builder.build())
@@ -38,19 +39,26 @@ def parse_nmap_command(raw_command: str) -> str:
     return ' '.join(nmap_split)
 
 
-def create_default_provider():
+def create_default_provider() -> VulnDescriptionProvider:
     return CveProjectProvider(Session())
 
 
 def create_report_builder(report_type: str) -> ReportBuilder:
-    if report_type == 'latex':
-        return LatexReportBuilder(create_default_provider())
-    if report_type == 'md':
-        return MarkdownReportBuilder(create_default_provider())
-    raise NotImplementedError(report_type)
+    builder_map = {
+        'tex': lambda p: LatexReportBuilder(p),
+        'md': lambda p: MarkdownReportBuilder(p),
+        'html': lambda p: JinjaHtmlReportBuilder(p),
+        'json': lambda p: JsonReportBuilder(p)
+    }
 
+    if report_type not in builder_map:
+        raise NotImplementedError(report_type)
 
-def main(dirname: str, output_file: str, ip_file: str, report_type: str = 'latex'):
+    provider = create_default_provider()
+    return builder_map[report_type](provider)
+
+
+def main(dirname: str, output_file: str, ip_file: str, report_type: str = 'tex'):
     nmap_command = ''
     start_date = ''
     builder = create_report_builder(report_type)
@@ -69,4 +77,5 @@ def main(dirname: str, output_file: str, ip_file: str, report_type: str = 'latex
 
 
 if __name__ == '__main__':
-    main(*sys.argv[1:4], report_type='latex')
+    report_format = os.getenv('format', 'tex')
+    main(*sys.argv[1:4], report_type=report_format)
diff --git a/requirements.txt b/requirements.txt
index 82928b9..aced391 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
 xmltodict==0.12.0
 google-cloud-storage==1.23.0
 boto3==1.12.15
+Jinja2==2.10.3
\ No newline at end of file
diff --git a/run.sh b/run.sh
index 77ec6d0..dcbcdf2 100755
--- a/run.sh
+++ b/run.sh
@@ -10,8 +10,15 @@ else
     mkdir /reports
 fi
 
+report_extension="tex"
+
+if [[ ! -z $format ]]
+then
+    report_extension=$format
+fi
+
 xml_dir=xml_files/$current_time
-report_file=reports/report_$current_time.tex
+report_file=reports/report_$current_time.$report_extension
 
 function upload {
     if [[ -z $upload ]]
@@ -40,8 +47,11 @@ do
 done < /shared/ips.txt
 
 python /output_report.py $root_dir$xml_dir $root_dir$report_file /shared/ips.txt
-sed -i 's/_/\\_/g' $root_dir$report_file
-sed -i 's/\$/\\\$/g' $root_dir$report_file
-sed -i 's/#/\\#/g' $root_dir$report_file
-sed -i 's/%/\\%/g' $root_dir$report_file
+if [[ $report_extension = "tex" ]]
+then
+    sed -i 's/_/\\_/g' $root_dir$report_file
+    sed -i 's/\$/\\\$/g' $root_dir$report_file
+    sed -i 's/#/\\#/g' $root_dir$report_file
+    sed -i 's/%/\\%/g' $root_dir$report_file
+fi
 upload $report_file
-- 
GitLab