# Copyright (c) 2015 Rackspace, Inc.
# Copyright (c) 2015 Hewlett Packard Enterprise
#
# 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.
r"""
==============
HTML formatter
==============
This formatter outputs the issues as HTML.
:Example:
.. code-block:: html
Bandit Report
Metrics:
Total lines of code:
9
Total lines skipped (#nosec):
0
.. versionadded:: 0.14.0
"""
from __future__ import absolute_import
import logging
import sys
import six
from bandit.core import docs_utils
from bandit.core import test_properties
from bandit.formatters import utils
if not six.PY2:
from html import escape as html_escape
else:
from cgi import escape as html_escape
LOG = logging.getLogger(__name__)
@test_properties.accepts_baseline
def report(manager, fileobj, sev_level, conf_level, lines=-1):
"""Writes issues to 'fileobj' in HTML format
:param manager: the bandit manager object
:param fileobj: The output file object, which may be sys.stdout
:param sev_level: Filtering severity level
:param conf_level: Filtering confidence level
:param lines: Number of lines to report, -1 for all
"""
header_block = u"""
Bandit Report
"""
report_block = u"""
{metrics}
{skipped}
{results}
"""
issue_block = u"""
{test_name}: {test_text}
Test ID: {test_id}
Severity: {severity}
Confidence: {confidence}
File: {path}
More info: {url}
{code}
{candidates}
"""
code_block = u"""
"""
candidate_block = u"""
Candidates:
{candidate_list}
"""
candidate_issue = u"""
"""
skipped_block = u"""
Skipped files:
{files_list}
"""
metrics_block = u"""
Metrics:
Total lines of code:
{loc}
Total lines skipped (#nosec):
{nosec}
"""
issues = manager.get_issue_list(sev_level=sev_level, conf_level=conf_level)
baseline = not isinstance(issues, list)
# build the skipped string to insert in the report
skipped_str = ''.join('%s reason: %s
' % (fname, reason)
for fname, reason in manager.get_skipped())
if skipped_str:
skipped_text = skipped_block.format(files_list=skipped_str)
else:
skipped_text = ''
# build the results string to insert in the report
results_str = ''
for index, issue in enumerate(issues):
if not baseline or len(issues[issue]) == 1:
candidates = ''
safe_code = html_escape(issue.get_code(lines, True).
strip('\n').lstrip(' '))
code = code_block.format(code=safe_code)
else:
candidates_str = ''
code = ''
for candidate in issues[issue]:
candidate_code = html_escape(candidate.get_code(lines, True).
strip('\n').lstrip(' '))
candidates_str += candidate_issue.format(code=candidate_code)
candidates = candidate_block.format(candidate_list=candidates_str)
url = docs_utils.get_url(issue.test_id)
results_str += issue_block.format(issue_no=index,
issue_class='issue-sev-{}'.
format(issue.severity.lower()),
test_name=issue.test,
test_id=issue.test_id,
test_text=issue.text,
severity=issue.severity,
confidence=issue.confidence,
path=issue.fname, code=code,
candidates=candidates,
url=url)
# build the metrics string to insert in the report
metrics_summary = metrics_block.format(
loc=manager.metrics.data['_totals']['loc'],
nosec=manager.metrics.data['_totals']['nosec'])
# build the report and output it
report_contents = report_block.format(metrics=metrics_summary,
skipped=skipped_text,
results=results_str)
with fileobj:
wrapped_file = utils.wrap_file_object(fileobj)
wrapped_file.write(utils.convert_file_contents(header_block))
wrapped_file.write(utils.convert_file_contents(report_contents))
if fileobj.name != sys.stdout.name:
LOG.info("HTML output written to file: %s", fileobj.name)