Files
xiaoxia-saas/.venv-ci-root/Lib/site-packages/bandit/core/context.py
T

317 lines
10 KiB
Python

#
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# SPDX-License-Identifier: Apache-2.0
import ast
from bandit.core import utils
class Context:
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 f"<Context {self._context}>"
@property
def call_args(self):
"""Get a list of function args
:return: A list of function args
"""
args = []
if "call" in self._context and hasattr(self._context["call"], "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 or None
"""
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
"""
return self._context.get("name")
@property
def call_function_name_qual(self):
"""Get the FQ name of a function call
:return: The FQ name of a function call
"""
return self._context.get("qualname")
@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
"""
return self._context.get("node")
@property
def string_val(self):
"""Get the value of a standalone unicode or string object
:return: value of a standalone unicode or string object
"""
return self._context.get("str")
@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
"""
return self._context.get("statement")
@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
and hasattr(self._context["node"], "args")
and hasattr(self._context["node"].args, "defaults")
):
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.Constant):
if isinstance(literal.value, bool):
literal_value = str(literal.value)
elif literal.value is None:
literal_value = str(literal.value)
else:
literal_value = literal.value
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 += (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.Name):
literal_value = literal.id
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
"""
if hasattr(self.node, "keywords"):
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
"""
max_args = self.call_args_count
if max_args and position_num < max_args:
arg = self._context["call"].args[position_num]
return getattr(arg, "attr", None) or self._get_literal_value(arg)
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 self._context.get("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 module in self._context.get("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
@property
def filename(self):
return self._context.get("filename")
@property
def file_data(self):
return self._context.get("file_data")
@property
def import_aliases(self):
return self._context.get("import_aliases")