Skip to content

Commit

Permalink
BanditBear: Add bandit_select_tests option
Browse files Browse the repository at this point in the history
This adds an additional feature to run the bear
using a list of selected tests IDs.

Closes coala#2386
  • Loading branch information
sangamcse committed Apr 1, 2018
1 parent be62fca commit 9ee0013
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 19 deletions.
45 changes: 38 additions & 7 deletions bears/python/BanditBear.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import re

from coalib.bearlib.abstractions.Linter import linter
from dependency_management.requirements.PipRequirement import PipRequirement
Expand All @@ -7,7 +8,8 @@
from coalib.settings.Setting import typed_list


@linter(executable='bandit')
@linter(executable='bandit',
use_stderr=True)
class BanditBear:
"""
Performs security analysis on Python source code, utilizing the ``ast``
Expand All @@ -24,18 +26,24 @@ class BanditBear:
def create_arguments(filename, file, config_file,
bandit_skipped_tests: typed_list(str)=
('B105', 'B106', 'B107', 'B404', 'B603', 'B606',
'B607')):
'B607'),
bandit_selected_tests: typed_list(str)=()):
"""
:param bandit_skipped_tests:
The IDs of the tests ``bandit`` shall not perform. You can get
information about the available builtin codes at
https://github.com/openstack/bandit#usage.
:param bandit_skipped_tests: The IDs of the tests ``bandit`` shall
not perform.
:param bandit_selected_tests: The IDs of the tests ``bandit`` shall
perform.
You can get information about the available builtin codes at
https://github.com/openstack/bandit#usage.
"""
args = (filename, '-f', 'json')

if bandit_skipped_tests:
args += ('-s', ','.join(bandit_skipped_tests))

if bandit_selected_tests:
args += ('-t', ','.join(bandit_selected_tests))

return args

severity_map = {'HIGH': RESULT_SEVERITY.MAJOR,
Expand All @@ -47,7 +55,30 @@ def create_arguments(filename, file, config_file,
'LOW': 50}

def process_output(self, output, filename, file):
output = json.loads(output)
def warn_issue(message):
self.warn('While running {0}, some issues were found:'
.format(self.__class__.__name__))
self.warn(message)

# Taking output from stderr in case bandit shows errors
# such as selected test ID and skipped test ID are same.
err_pattern = re.compile(r'ERROR.*')
match = err_pattern.search(output[1])
if match:
error_output = (match.group())
warn_issue(error_output)

if not file or not output[0]:
return

# Handling program errors
# such as error in selecting test IDs.
# that are not in json format.
try:
output = json.loads(output[0])
except ValueError:
warn_issue(output[0])
return

for error in output['errors']:
yield Result.from_values(
Expand Down
72 changes: 60 additions & 12 deletions tests/python/BanditBearTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os.path

from coalib.settings.Section import Section
from coalib.settings.Setting import Setting
from coalib.results.Result import Result
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
from coalib.testing.LocalBearTestHelper import (
Expand All @@ -24,31 +25,37 @@ def load_testfile(name, splitlines=False):
return contents


def test(testfilename, expected_results):
def test_function(self):
bear = BanditBear(Section(''), Queue())
self.check_results(bear, load_testfile(testfilename, True),
expected_results, get_testfile_path(testfilename),
create_tempfile=False)
return test_function


class BanditBearTest(LocalBearTestHelper):
test_assert = test(

def setUp(self):
self.section = Section('bandit')
self.queue = Queue()
self.uut = BanditBear(self.section, self.queue)

def execute_test(testfilename, expected_results):
def test_function(self):
self.check_results(self.uut,
load_testfile(testfilename, True),
expected_results,
get_testfile_path(testfilename),
create_tempfile=False)
return test_function

test_assert = execute_test(
'assert.py',
[Result.from_values('B101', 'Use of assert detected. The enclosed '
'code will be removed when compiling to optimised '
'byte code.', get_testfile_path('assert.py'), 1,
end_line=1, severity=RESULT_SEVERITY.INFO,
confidence=90)])

test_exec_py2_py = test(
test_exec_py2_py = execute_test(
'exec-py2.py',
[Result.from_values('BanditBear', 'syntax error while parsing AST '
'from file', get_testfile_path('exec-py2.py'),
severity=RESULT_SEVERITY.MAJOR)])

test_jinja2_templating = test(
test_jinja2_templating = execute_test(
'jinja2_templating.py',
[Result.from_values('B701', 'Using jinja2 templates with '
'autoescape=False is dangerous and can lead to '
Expand Down Expand Up @@ -78,6 +85,47 @@ class BanditBearTest(LocalBearTestHelper):
end_line=16, severity=RESULT_SEVERITY.MAJOR,
confidence=90)])

def test_bandit_selected_tests1(self):
test_file_content = load_testfile('httpoxy_cgihandler_assert.py', True)
test_file = get_testfile_path('httpoxy_cgihandler_assert.py')
self.section.append(Setting('bandit_selected_tests', 'B101, B412'))
result = [Result.from_values('B101', 'Use of assert detected. The '
'enclosed code will be removed when '
'compiling to optimised byte code.',
test_file, 8, end_line=8,
severity=RESULT_SEVERITY.INFO,
confidence=90),
Result.from_values('B412', 'Consider possible security '
'implications associated with wsgiref.'
'handlers.CGIHandler module.',
test_file, 12, end_line=12,
severity=RESULT_SEVERITY.MAJOR,
confidence=90)]
self.check_results(self.uut, test_file_content, result, test_file,
create_tempfile=False)

def test_bandit_selected_tests2(self):
test_file_content = load_testfile('httpoxy_cgihandler_assert.py', True)
test_file = get_testfile_path('httpoxy_cgihandler_assert.py')
self.section.append(Setting('bandit_selected_tests', 'B101'))
result = [Result.from_values('B101', 'Use of assert detected. The '
'enclosed code will be removed when '
'compiling to optimised byte code.',
test_file, 8, end_line=8,
severity=RESULT_SEVERITY.INFO,
confidence=90)]
self.check_results(self.uut, test_file_content, result, test_file,
create_tempfile=False)

def test_bandit_select_ignore_same_tests(self):
test_file_content = load_testfile('httpoxy_cgihandler_assert.py', True)
test_file = get_testfile_path('httpoxy_cgihandler_assert.py')
self.section.append(Setting('bandit_skipped_tests', 'B101'))
self.section.append(Setting('bandit_selected_tests', 'B101'))
result = []
self.check_results(self.uut, test_file_content, result, test_file,
create_tempfile=False)

# The following test will ignore some error codes, so "good" and "bad" doesn't
# reflect the actual code quality.

Expand Down
12 changes: 12 additions & 0 deletions tests/python/bandit_test_files/httpoxy_cgihandler_assert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import requests
import wsgiref.handlers


def application(environ, start_response):
r = requests.get('https://192.168.0.42/private/api/foobar')
start_response('200 OK', [('Content-Type', 'text/plain')])
assert True
return [r.content]

if __name__ == '__main__':
wsgiref.handlers.CGIHandler().run(application)

0 comments on commit 9ee0013

Please sign in to comment.