Yet Another Formatter (yaml)
This patch adds a yaml formatter to the output options of bandit. Change-Id: Ibbe0cff062ce2c11138b746f95109f31de10f5b1
This commit is contained in:
parent
32b4714562
commit
fbd4e83efe
@ -87,8 +87,8 @@ Usage::
|
||||
$ bandit -h
|
||||
usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE]
|
||||
[-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i]
|
||||
[-f {csv,html,json,screen,txt,xml}] [-o [OUTPUT_FILE]] [-v] [-d]
|
||||
[--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]
|
||||
[-f {csv,html,json,screen,txt,xml,yaml}] [-o [OUTPUT_FILE]] [-v]
|
||||
[-d] [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]
|
||||
[--ini INI_PATH] [--version]
|
||||
targets [targets ...]
|
||||
|
||||
@ -118,7 +118,7 @@ Usage::
|
||||
(-l for LOW, -ll for MEDIUM, -lll for HIGH)
|
||||
-i, --confidence report only issues of a given confidence level or
|
||||
higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)
|
||||
-f {csv,html,json,screen,txt,xml}, --format {csv,html,json,screen,txt,xml}
|
||||
-f {csv,html,json,screen,txt,xml,yaml}, --format {csv,html,json,screen,txt,xml,yaml}
|
||||
specify output format
|
||||
-o [OUTPUT_FILE], --output [OUTPUT_FILE]
|
||||
write report to filename
|
||||
|
92
bandit/formatters/yaml.py
Normal file
92
bandit/formatters/yaml.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Copyright (c) 2017 VMware, Inc.
|
||||
#
|
||||
# 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"""
|
||||
==============
|
||||
YAML Formatter
|
||||
==============
|
||||
|
||||
This formatter outputs the issues in a yaml format.
|
||||
|
||||
:Example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
filename,test_name,test_id,issue_severity,issue_confidence,issue_text,
|
||||
line_number,line_range
|
||||
examples/yaml_load.py,blacklist_calls,B301,MEDIUM,HIGH,"Use of unsafe yaml
|
||||
load. Allows instantiation of arbitrary objects. Consider yaml.safe_load().
|
||||
",5,[5]
|
||||
|
||||
.. versionadded:: 1.4.1
|
||||
|
||||
"""
|
||||
# Necessary for this formatter to work when imported on Python 2. Importing
|
||||
# the standard library's yaml module conflicts with the name of this module.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import operator
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def report(manager, fileobj, sev_level, conf_level, lines=-1):
|
||||
'''Prints issues in YAML 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
|
||||
'''
|
||||
|
||||
machine_output = {'results': [], 'errors': []}
|
||||
for (fname, reason) in manager.get_skipped():
|
||||
machine_output['errors'].append({'filename': fname, 'reason': reason})
|
||||
|
||||
results = manager.get_issue_list(sev_level=sev_level,
|
||||
conf_level=conf_level)
|
||||
|
||||
collector = [r.as_dict() for r in results]
|
||||
|
||||
itemgetter = operator.itemgetter
|
||||
if manager.agg_type == 'vuln':
|
||||
machine_output['results'] = sorted(collector,
|
||||
key=itemgetter('test_name'))
|
||||
else:
|
||||
machine_output['results'] = sorted(collector,
|
||||
key=itemgetter('filename'))
|
||||
|
||||
machine_output['metrics'] = manager.metrics.data
|
||||
|
||||
for result in machine_output['results']:
|
||||
if 'code' in result:
|
||||
code = result['code'].replace('\n', '\\n')
|
||||
result['code'] = code
|
||||
|
||||
# timezone agnostic format
|
||||
TS_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
time_string = datetime.datetime.utcnow().strftime(TS_FORMAT)
|
||||
machine_output['generated_at'] = time_string
|
||||
|
||||
yaml.safe_dump(machine_output, fileobj, default_flow_style=False)
|
||||
|
||||
if fileobj.name != sys.stdout.name:
|
||||
LOG.info("YAML output written to file: %s", fileobj.name)
|
5
doc/source/formatters/yaml.rst
Normal file
5
doc/source/formatters/yaml.rst
Normal file
@ -0,0 +1,5 @@
|
||||
----
|
||||
yaml
|
||||
----
|
||||
|
||||
.. automodule:: bandit.formatters.yaml
|
@ -7,8 +7,8 @@ SYNOPSIS
|
||||
|
||||
bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE]
|
||||
[-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i]
|
||||
[-f {csv,html,json,screen,txt,xml}] [-o OUTPUT_FILE] [-v] [-d]
|
||||
[--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]
|
||||
[-f {csv,html,json,screen,txt,xml,yaml}] [-o OUTPUT_FILE] [-v]
|
||||
[-d] [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]
|
||||
[--ini INI_PATH] [--version]
|
||||
targets [targets ...]
|
||||
|
||||
@ -43,7 +43,7 @@ OPTIONS
|
||||
(-l for LOW, -ll for MEDIUM, -lll for HIGH)
|
||||
-i, --confidence report only issues of a given confidence level or
|
||||
higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)
|
||||
-f {csv,html,json,screen,txt,xml}, --format {csv,html,json,screen,txt,xml}
|
||||
-f {csv,html,json,screen,txt,xml,yaml}, --format {csv,html,json,screen,txt,xml,yaml}
|
||||
specify output format
|
||||
-o OUTPUT_FILE, --output OUTPUT_FILE
|
||||
write report to filename
|
||||
|
@ -36,6 +36,7 @@ bandit.formatters =
|
||||
xml = bandit.formatters.xml:report
|
||||
html = bandit.formatters.html:report
|
||||
screen = bandit.formatters.screen:report
|
||||
yaml = bandit.formatters.yaml:report
|
||||
bandit.plugins =
|
||||
# bandit/plugins/app_debug.py
|
||||
flask_debug_true = bandit.plugins.app_debug:flask_debug_true
|
||||
|
96
tests/unit/formatters/test_yaml.py
Normal file
96
tests/unit/formatters/test_yaml.py
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright (c) 2017 VMware, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import collections
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
import yaml
|
||||
|
||||
import bandit
|
||||
from bandit.core import config
|
||||
from bandit.core import constants
|
||||
from bandit.core import issue
|
||||
from bandit.core import manager
|
||||
from bandit.core import metrics
|
||||
from bandit.formatters import json as b_json
|
||||
|
||||
|
||||
class JsonFormatterTests(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(JsonFormatterTests, self).setUp()
|
||||
conf = config.BanditConfig()
|
||||
self.manager = manager.BanditManager(conf, 'file')
|
||||
(tmp_fd, self.tmp_fname) = tempfile.mkstemp()
|
||||
self.context = {'filename': self.tmp_fname,
|
||||
'lineno': 4,
|
||||
'linerange': [4]}
|
||||
self.check_name = 'hardcoded_bind_all_interfaces'
|
||||
self.issue = issue.Issue(bandit.MEDIUM, bandit.MEDIUM,
|
||||
'Possible binding to all interfaces.')
|
||||
|
||||
self.candidates = [issue.Issue(bandit.LOW, bandit.LOW, 'Candidate A',
|
||||
lineno=1),
|
||||
issue.Issue(bandit.HIGH, bandit.HIGH, 'Candiate B',
|
||||
lineno=2)]
|
||||
|
||||
self.manager.out_file = self.tmp_fname
|
||||
|
||||
self.issue.fname = self.context['filename']
|
||||
self.issue.lineno = self.context['lineno']
|
||||
self.issue.linerange = self.context['linerange']
|
||||
self.issue.test = self.check_name
|
||||
|
||||
self.manager.results.append(self.issue)
|
||||
self.manager.metrics = metrics.Metrics()
|
||||
|
||||
# mock up the metrics
|
||||
for key in ['_totals', 'binding.py']:
|
||||
self.manager.metrics.data[key] = {'loc': 4, 'nosec': 2}
|
||||
for (criteria, default) in constants.CRITERIA:
|
||||
for rank in constants.RANKING:
|
||||
self.manager.metrics.data[key]['{0}.{1}'.format(
|
||||
criteria, rank
|
||||
)] = 0
|
||||
|
||||
@mock.patch('bandit.core.manager.BanditManager.get_issue_list')
|
||||
def test_report(self, get_issue_list):
|
||||
self.manager.files_list = ['binding.py']
|
||||
self.manager.scores = [{'SEVERITY': [0] * len(constants.RANKING),
|
||||
'CONFIDENCE': [0] * len(constants.RANKING)}]
|
||||
|
||||
get_issue_list.return_value = collections.OrderedDict(
|
||||
[(self.issue, self.candidates)])
|
||||
|
||||
tmp_file = open(self.tmp_fname, 'w')
|
||||
b_json.report(self.manager, tmp_file, self.issue.severity,
|
||||
self.issue.confidence)
|
||||
|
||||
with open(self.tmp_fname) as f:
|
||||
data = yaml.load(f.read())
|
||||
self.assertIsNotNone(data['generated_at'])
|
||||
self.assertEqual(self.tmp_fname, data['results'][0]['filename'])
|
||||
self.assertEqual(self.issue.severity,
|
||||
data['results'][0]['issue_severity'])
|
||||
self.assertEqual(self.issue.confidence,
|
||||
data['results'][0]['issue_confidence'])
|
||||
self.assertEqual(self.issue.text, data['results'][0]['issue_text'])
|
||||
self.assertEqual(self.context['lineno'],
|
||||
data['results'][0]['line_number'])
|
||||
self.assertEqual(self.context['linerange'],
|
||||
data['results'][0]['line_range'])
|
||||
self.assertEqual(self.check_name, data['results'][0]['test_name'])
|
||||
self.assertIn('candidates', data['results'][0])
|
Loading…
x
Reference in New Issue
Block a user