dusty/reporters/junit/reporter.py (74 lines of code) (raw):
#!/usr/bin/python3
# coding=utf-8
# pylint: disable=I0011,E0401
# Copyright 2019 getcarrier.io
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Reporter: junit
"""
from junit_xml import TestSuite, TestCase
from dusty.tools import log, markdown
from dusty.models.module import DependentModuleModel
from dusty.models.reporter import ReporterModel
from dusty.models.finding import DastFinding, SastFinding
from dusty.constants import SEVERITIES
from . import constants
class Reporter(DependentModuleModel, ReporterModel):
""" Report findings from scanners """
def __init__(self, context):
""" Initialize reporter instance """
super().__init__()
self.context = context
self.config = \
self.context.config["reporters"][__name__.split(".")[-2]]
def report(self):
""" Report """
file = self.config.get("file", constants.DEFAULT_REPORT_FILE)
if self.config.get("format_file_name", True):
file = file.format(**self.context.meta)
log.info("Creating XML report %s", file)
# Prepare test cases
test_name = \
f"{self.context.get_meta('project_name', 'UnnamedProject')}-" \
f"{self.context.get_meta('environment_name', 'unknown')}-" \
f"{self.context.get_meta('testing_type', 'AST')}"
test_cases = list()
# Summary
summary_case = TestCase(
f"Security tests has been COMPLETED",
classname="Carrier Dusty",
stdout=\
f"Total findings (with false positives and info): {len(self.context.findings)}. " \
f"Total scan errors: {len(self.context.errors)}."
)
test_cases.append(summary_case)
# Findings
for item in self.context.findings:
if item.get_meta("information_finding", False) or \
item.get_meta("false_positive_finding", False) or \
item.get_meta("excluded_finding", False):
continue
if isinstance(item, DastFinding):
test_case = TestCase(item.title, classname=item.get_meta("tool", ""))
test_case.add_error_info(
message=markdown.markdown_to_text(item.description) if \
self.config.get("plain_text", False) else \
markdown.markdown_unescape(item.description),
error_type=item.get_meta("severity", SEVERITIES[-1])
)
test_cases.append(test_case)
if isinstance(item, SastFinding):
test_case = TestCase(item.title, classname=item.get_meta("tool", ""))
test_case.add_error_info(
message=markdown.markdown_to_text("\n\n".join(item.description)) if \
self.config.get("plain_text", False) else \
markdown.markdown_unescape("\n\n".join(item.description)),
error_type=item.get_meta("severity", SEVERITIES[-1])
)
test_cases.append(test_case)
# Save to file
with open(file, "w") as report:
TestSuite.to_file(report, [TestSuite(test_name, test_cases)], prettyprint=False)
self.set_meta("report_file", file)
@staticmethod
def fill_config(data_obj):
""" Make sample config """
data_obj.insert(len(data_obj), "file", "/path/to/report.xml", comment="XML report path")
data_obj.insert(
len(data_obj), "format_file_name", True,
comment="(optional) Allow to use {variables} inside file path"
)
data_obj.insert(
len(data_obj), "plain_text", False,
comment="(optional) Convert markdown to plain text"
)
@staticmethod
def get_name():
""" Reporter name """
return "JUnit"
@staticmethod
def get_description():
""" Reporter description """
return "JUnit XML reporter"