Add Python 3.4 compatibility to bandit

This includes a number of changes to make this happen:

- We handle the fact that in Python 3.3 and later, ast.TryExcept and
  ast.TryFinally were replaced by ast.Try

- We handle the fact that ast.NameConstant is now the node type for
  True/False

- We handle the cases where map and range need to return lists

- We remove a property from the result store to prevent errors assigning
  to the underlying attribute

- We check for exec conditionally based on the version of Python

- We use proper octal notation, e.g., 0o755

Change-Id: I71c0bb61c9ee0bf1b751a719a4eb95bf7a0b4943
This commit is contained in:
Ian Cordasco 2015-05-27 18:00:38 -05:00
parent e7bf93f742
commit ddf75663ce
10 changed files with 39 additions and 29 deletions

View File

@ -54,4 +54,4 @@ CONFIDENCE_DEFAULT = 'UNDEFINED'
# These are only useful when we have a constant in code. If we
# have a variable we cannot determine if False.
# See https://docs.python.org/2/library/stdtypes.html#truth-value-testing
FALSE_VALUES = [None, False, 'False', 0, 0L, 0.0, 0j, '', (), [], {}]
FALSE_VALUES = [None, False, 'False', 0, 0.0, 0j, '', (), [], {}]

View File

@ -220,6 +220,9 @@ class Context():
elif isinstance(literal, _ast.Name):
return literal.id
elif hasattr(literal, 'value'):
return str(literal.value)
else:
return None

View File

@ -23,6 +23,12 @@ from bandit.core import utils as b_utils
from bandit.core.utils import InvalidModulePath
if hasattr(ast, 'TryExcept'):
ast_Try = (ast.TryExcept, ast.TryFinally)
else: # Python 3.3+
ast_Try = ast.Try
class StatementBuffer():
'''Buffer for code statements
@ -82,20 +88,15 @@ class StatementBuffer():
tmp_buf = stmt.body
stmt.body = []
stmt.orelse = []
elif (isinstance(stmt, ast.TryExcept)):
for handler in stmt.handlers:
elif isinstance(stmt, ast_Try):
for handler in getattr(stmt, 'handlers', []):
stmt.body.extend(handler.body)
stmt.body.extend(stmt.orelse)
stmt.body.extend(getattr(stmt, 'orelse', []))
stmt.body.extend(tmp_buf)
tmp_buf = stmt.body
stmt.body = []
stmt.orelse = []
stmt.handlers = []
elif (isinstance(stmt, ast.TryFinally)):
stmt.body.extend(stmt.finalbody)
stmt.body.extend(tmp_buf)
tmp_buf = stmt.body
stmt.body = []
stmt.finalbody = []
# once we are sure it's either a single statement or that
@ -386,9 +387,9 @@ class BanditNodeVisitor(ast.NodeVisitor):
def add(x, y):
return x + y
for score_type in self.scores:
self.scores[score_type] = map(
self.scores[score_type] = list(map(
add, self.scores[score_type], scores[score_type]
)
))
def process(self, fdata):
'''Main process loop

View File

@ -47,14 +47,6 @@ class BanditResultStore():
self.format = 'txt'
self.out_file = None
@property
def count(self):
'''Count property - used to get the current number of test results
:return: The current count of test results
'''
return self.count
def skip(self, filename, reason):
'''Indicates that the specified file was skipped and why

View File

@ -13,15 +13,26 @@
# 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 six
import bandit
from bandit.core.test_properties import *
@checks('Exec')
def exec_used(context):
def exec_issue():
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.HIGH,
text="Use of exec detected."
)
if six.PY2:
@checks('Exec')
def exec_used(context):
return exec_issue()
else:
@checks('Call')
def exec_used(context):
if context.call_function_name_qual == 'exec':
return exec_issue()

View File

@ -31,7 +31,8 @@ def jinja2_autoescape_false(context):
if isinstance(node, ast.keyword):
# definite autoescape = False
if (getattr(node, 'arg', None) == 'autoescape' and
getattr(node.value, 'id', None) == 'False'):
(getattr(node.value, 'id', None) == 'False' or
getattr(node.value, 'value', None) is False)):
return bandit.Issue(
severity=bandit.HIGH,
confidence=bandit.HIGH,
@ -42,7 +43,8 @@ def jinja2_autoescape_false(context):
)
# found autoescape
if getattr(node, 'arg', None) == 'autoescape':
if(getattr(node.value, 'id', None) == 'True'):
if (getattr(node.value, 'id', None) == 'True' or
getattr(node.value, 'value', None) is True):
return
else:
return bandit.Issue(

View File

@ -17,6 +17,7 @@ classifier =
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3.4
Topic :: Security
[entry_points]

View File

@ -73,4 +73,4 @@ class StatementBufferTests(unittest.TestCase):
self.assertEqual(3, len(stmt['linerange']))
# the range should be the correct line numbers
self.assertEqual([11, 12, 13], stmt['linerange'])
self.assertEqual([11, 12, 13], list(stmt['linerange']))

View File

@ -56,7 +56,7 @@ class UtilTests(unittest.TestCase):
# good/a/b/c/test_typical.py
os.makedirs(os.path.join(
self.tempdir, 'good', 'a', 'b', 'c'), 0755)
self.tempdir, 'good', 'a', 'b', 'c'), 0o755)
_touch(os.path.join(self.tempdir, 'good', '__init__.py'))
_touch(os.path.join(self.tempdir, 'good', 'a', '__init__.py'))
_touch(os.path.join(
@ -68,7 +68,7 @@ class UtilTests(unittest.TestCase):
# missingmid/a/b/c/test_missingmid.py
os.makedirs(os.path.join(
self.tempdir, 'missingmid', 'a', 'b', 'c'), 0755)
self.tempdir, 'missingmid', 'a', 'b', 'c'), 0o755)
_touch(os.path.join(self.tempdir, 'missingmid', '__init__.py'))
# no missingmid/a/__init__.py
_touch(os.path.join(
@ -80,7 +80,7 @@ class UtilTests(unittest.TestCase):
# missingend/a/b/c/test_missingend.py
os.makedirs(os.path.join(
self.tempdir, 'missingend', 'a', 'b', 'c'), 0755)
self.tempdir, 'missingend', 'a', 'b', 'c'), 0o755)
_touch(os.path.join(
self.tempdir, 'missingend', '__init__.py'))
_touch(os.path.join(
@ -90,7 +90,7 @@ class UtilTests(unittest.TestCase):
self.tempdir, 'missingend', 'a', 'b', 'c', 'test_missingend.py'))
# syms/a/bsym/c/test_typical.py
os.makedirs(os.path.join(self.tempdir, 'syms', 'a'), 0755)
os.makedirs(os.path.join(self.tempdir, 'syms', 'a'), 0o755)
_touch(os.path.join(self.tempdir, 'syms', '__init__.py'))
_touch(os.path.join(self.tempdir, 'syms', 'a', '__init__.py'))
os.symlink(os.path.join(self.tempdir, 'good', 'a', 'b'),

View File

@ -1,6 +1,6 @@
[tox]
minversion = 1.6
envlist = py27,py33,pypy,pep8
envlist = py27,py34,pypy,pep8
skipsdist = True
[testenv]