diff --git a/Makefile b/Makefile index d073f5b22d779660b7bf655dbdebeb1a3d5d0a13..0ed0bd04a5e0066679e279a7eef66bf2c133714d 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 260c26feadd001a34de6daf3427088c54ddb3ed2..d144fb39545d6bae32a5677e05f054af71b053fa 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 f1c0da4c425162ed8090338e1a59ed025366e8b2..fb634766eb836a6dabaec29f10cb3b75b681e934 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 5db235371c69a132b59d71227a0b2844a03f40fd..ca8cb01a572d2050f72204fbb3bd1ee5653e56e5 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 006b3617f4bfef4e8aee0f1f3384e72b4eff097e..ed452cbf56994e1ddab12d5528766d9a09583a8f 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 36f41f99f0b0edddbed45027b771d6c1d5e539cf..b920ddaa56efe41719fa76eb41823a09ac7cf015 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 e1fa56022f8e68059035688e1fea84d5d89fc0ef..d593f2439f6f3c29226ae9bfc7451c75a6cf44ef 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 2f5b45e751f1d4d3e3edfddafba97a6a8703f6b6..42ecc54f3bcd1aaaba17325556000ff6d79f417b 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 82928b9bf0572e09d1567f4bcf795058b541c349..aced3912ac9026e03d26ac9aea092b350d87199f 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 77ec6d07cd6c924e4775778f087e4986d573ed4c..dcbcdf2526d62ffc7373da62d53ed29330a717c0 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