ansible-later/env_27/lib/python2.7/site-packages/bandit/core/context.py
Robert Kaussow 10aaa8e7e3 fix pytest
2019-04-11 15:56:20 +02:00

340 lines
11 KiB
Python

# -*- coding:utf-8 -*-
#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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 _ast
import six
from bandit.core import utils
class Context(object):
def __init__(self, context_object=None):
'''Initialize the class with a context, empty dict otherwise
:param context_object: The context object to create class from
:return: -
'''
if context_object is not None:
self._context = context_object
else:
self._context = dict()
def __repr__(self):
'''Generate representation of object for printing / interactive use
Most likely only interested in non-default properties, so we return
the string version of _context.
Example string returned:
<Context {'node': <_ast.Call object at 0x110252510>, 'function': None,
'name': 'socket', 'imports': set(['socket']), 'module': None,
'filename': 'examples/binding.py',
'call': <_ast.Call object at 0x110252510>, 'lineno': 3,
'import_aliases': {}, 'qualname': 'socket.socket'}>
:return: A string representation of the object
'''
return "<Context %s>" % self._context
@property
def call_args(self):
'''Get a list of function args
:return: A list of function args
'''
args = []
for arg in self._context['call'].args:
if hasattr(arg, 'attr'):
args.append(arg.attr)
else:
args.append(self._get_literal_value(arg))
return args
@property
def call_args_count(self):
'''Get the number of args a function call has
:return: The number of args a function call has
'''
if 'call' in self._context and hasattr(self._context['call'], 'args'):
return len(self._context['call'].args)
else:
return None
@property
def call_function_name(self):
'''Get the name (not FQ) of a function call
:return: The name (not FQ) of a function call
'''
if 'name' in self._context:
return self._context['name']
else:
return None
@property
def call_function_name_qual(self):
'''Get the FQ name of a function call
:return: The FQ name of a function call
'''
if 'qualname' in self._context:
return self._context['qualname']
else:
return None
@property
def call_keywords(self):
'''Get a dictionary of keyword parameters
:return: A dictionary of keyword parameters for a call as strings
'''
if ('call' in self._context and
hasattr(self._context['call'], 'keywords')):
return_dict = {}
for li in self._context['call'].keywords:
if hasattr(li.value, 'attr'):
return_dict[li.arg] = li.value.attr
else:
return_dict[li.arg] = self._get_literal_value(li.value)
return return_dict
else:
return None
@property
def node(self):
'''Get the raw AST node associated with the context
:return: The raw AST node associated with the context
'''
if 'node' in self._context:
return self._context['node']
else:
return None
@property
def string_val(self):
'''Get the value of a standalone unicode or string object
:return: value of a standalone unicode or string object
'''
if 'str' in self._context:
return self._context['str']
else:
return None
@property
def bytes_val(self):
'''Get the value of a standalone bytes object (py3 only)
:return: value of a standalone bytes object
'''
return self._context.get('bytes')
@property
def string_val_as_escaped_bytes(self):
'''Get escaped value of the object.
Turn the value of a string or bytes object into byte sequence with
unknown, control, and \\ characters escaped.
This function should be used when looking for a known sequence in a
potentially badly encoded string in the code.
:return: sequence of printable ascii bytes representing original string
'''
val = self.string_val
if val is not None:
# it's any of str or unicode in py2, or str in py3
return val.encode('unicode_escape')
val = self.bytes_val
if val is not None:
return utils.escaped_bytes_representation(val)
return None
@property
def statement(self):
'''Get the raw AST for the current statement
:return: The raw AST for the current statement
'''
if 'statement' in self._context:
return self._context['statement']
else:
return None
@property
def function_def_defaults_qual(self):
'''Get a list of fully qualified default values in a function def
:return: List of defaults
'''
defaults = []
if 'node' in self._context:
for default in self._context['node'].args.defaults:
defaults.append(utils.get_qual_attr(
default,
self._context['import_aliases']))
return defaults
def _get_literal_value(self, literal):
'''Utility function to turn AST literals into native Python types
:param literal: The AST literal to convert
:return: The value of the AST literal
'''
if isinstance(literal, _ast.Num):
literal_value = literal.n
elif isinstance(literal, _ast.Str):
literal_value = literal.s
elif isinstance(literal, _ast.List):
return_list = list()
for li in literal.elts:
return_list.append(self._get_literal_value(li))
literal_value = return_list
elif isinstance(literal, _ast.Tuple):
return_tuple = tuple()
for ti in literal.elts:
return_tuple = return_tuple + (self._get_literal_value(ti),)
literal_value = return_tuple
elif isinstance(literal, _ast.Set):
return_set = set()
for si in literal.elts:
return_set.add(self._get_literal_value(si))
literal_value = return_set
elif isinstance(literal, _ast.Dict):
literal_value = dict(zip(literal.keys, literal.values))
elif isinstance(literal, _ast.Ellipsis):
# what do we want to do with this?
literal_value = None
elif isinstance(literal, _ast.Name):
literal_value = literal.id
# NOTE(sigmavirus24): NameConstants are only part of the AST in Python
# 3. NameConstants tend to refer to things like True and False. This
# prevents them from being re-assigned in Python 3.
elif six.PY3 and isinstance(literal, _ast.NameConstant):
literal_value = str(literal.value)
# NOTE(sigmavirus24): Bytes are only part of the AST in Python 3
elif six.PY3 and isinstance(literal, _ast.Bytes):
literal_value = literal.s
else:
literal_value = None
return literal_value
def get_call_arg_value(self, argument_name):
'''Gets the value of a named argument in a function call.
:return: named argument value
'''
kwd_values = self.call_keywords
if kwd_values is not None and argument_name in kwd_values:
return kwd_values[argument_name]
def check_call_arg_value(self, argument_name, argument_values=None):
'''Checks for a value of a named argument in a function call.
Returns none if the specified argument is not found.
:param argument_name: A string - name of the argument to look for
:param argument_values: the value, or list of values to test against
:return: Boolean True if argument found and matched, False if
found and not matched, None if argument not found at all
'''
arg_value = self.get_call_arg_value(argument_name)
if arg_value is not None:
if not isinstance(argument_values, list):
# if passed a single value, or a tuple, convert to a list
argument_values = list((argument_values,))
for val in argument_values:
if arg_value == val:
return True
return False
else:
# argument name not found, return None to allow testing for this
# eventuality
return None
def get_lineno_for_call_arg(self, argument_name):
'''Get the line number for a specific named argument
In case the call is split over multiple lines, get the correct one for
the argument.
:param argument_name: A string - name of the argument to look for
:return: Integer - the line number of the found argument, or -1
'''
for key in self.node.keywords:
if key.arg == argument_name:
return key.value.lineno
def get_call_arg_at_position(self, position_num):
'''Returns positional argument at the specified position (if it exists)
:param position_num: The index of the argument to return the value for
:return: Value of the argument at the specified position if it exists
'''
if ('call' in self._context and
hasattr(self._context['call'], 'args') and
position_num < len(self._context['call'].args)):
return self._get_literal_value(
self._context['call'].args[position_num]
)
else:
return None
def is_module_being_imported(self, module):
'''Check for the specified module is currently being imported
:param module: The module name to look for
:return: True if the module is found, False otherwise
'''
return 'module' in self._context and self._context['module'] == module
def is_module_imported_exact(self, module):
'''Check if a specified module has been imported; only exact matches.
:param module: The module name to look for
:return: True if the module is found, False otherwise
'''
return ('imports' in self._context and
module in self._context['imports'])
def is_module_imported_like(self, module):
'''Check if a specified module has been imported
Check if a specified module has been imported; specified module exists
as part of any import statement.
:param module: The module name to look for
:return: True if the module is found, False otherwise
'''
if 'imports' in self._context:
for imp in self._context['imports']:
if module in imp:
return True
return False