3
# Copyright 2014 Hewlett-Packard Development Company, L.P.
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
# not use this file except in compliance with the License. You may obtain
7
# a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
# License for the specific language governing permissions and limitations
21
from bandit.core import utils
25
def __init__(self, context_object=None):
26
'''Initialize the class with a context, empty dict otherwise
28
:param context_object: The context object to create class from
31
if context_object is not None:
32
self._context = context_object
34
self._context = dict()
37
'''Generate representation of object for printing / interactive use
39
Most likely only interested in non-default properties, so we return
40
the string version of _context.
42
Example string returned:
43
<Context {'node': <_ast.Call object at 0x110252510>, 'function': None,
44
'name': 'socket', 'imports': set(['socket']), 'module': None,
45
'filename': 'examples/binding.py',
46
'call': <_ast.Call object at 0x110252510>, 'lineno': 3,
47
'import_aliases': {}, 'qualname': 'socket.socket'}>
49
:return: A string representation of the object
51
return "<Context %s>" % self._context
55
'''Get a list of function args
57
:return: A list of function args
60
for arg in self._context['call'].args:
61
if hasattr(arg, 'attr'):
64
args.append(self._get_literal_value(arg))
68
def call_args_count(self):
69
'''Get the number of args a function call has
71
:return: The number of args a function call has
73
if hasattr(self._context['call'], 'args'):
74
return len(self._context['call'].args)
79
def call_args_string(self):
80
'''Get a string representation of the call arguments
82
:return: Returns a string representation of the call arguments
84
if 'call' in self._context and hasattr(self._context, 'args'):
85
return utils.ast_args_to_str(self._context['call'].args)
90
def call_function_name(self):
91
'''Get the name (not FQ) of a function call
93
:return: The name (not FQ) of a function call
95
if 'name' in self._context:
96
return self._context['name']
101
def call_function_name_qual(self):
102
'''Get the FQ name of a function call
104
:return: The FQ name of a function call
106
if 'qualname' in self._context:
107
return self._context['qualname']
112
def call_keywords(self):
113
'''Get a dictionary of keyword parameters
115
:return: A dictionary of keyword parameters for a call as strings
118
'call' in self._context and
119
hasattr(self._context['call'], 'keywords')
122
for li in self._context['call'].keywords:
123
if hasattr(li.value, 'attr'):
124
return_dict[li.arg] = li.value.attr
126
return_dict[li.arg] = self._get_literal_value(li.value)
133
'''Get the raw AST node associated with the context
135
:return: The raw AST node associated with the context
137
if 'node' in self._context:
138
return self._context['node']
143
def string_val(self):
144
'''Get a string value of a standalone string
146
:return: String value of a standalone string
148
if 'str' in self._context:
149
return utils.safe_str(self._context['str'])
155
'''Get the raw AST for the current statement
157
:return: The raw AST for the current statement
159
if 'statement' in self._context:
160
return self._context['statement']
165
def function_def_defaults_qual(self):
166
'''Get a list of fully qualified default values in a function def
168
:return: List of defaults
171
for default in self._context['node'].args.defaults:
172
defaults.append(utils.get_qual_attr(
174
self._context['import_aliases']))
177
def _get_literal_value(self, literal):
178
'''Utility function to turn AST literals into native Python types
180
:param literal: The AST literal to convert
181
:return: The value of the AST literal
183
if isinstance(literal, _ast.Num):
186
elif isinstance(literal, _ast.Str):
189
elif isinstance(literal, _ast.List):
191
for li in literal.elts:
192
return_list.append(self._get_literal_value(li))
195
elif isinstance(literal, _ast.Tuple):
196
return_tuple = tuple()
197
for ti in literal.elts:
198
return_tuple = return_tuple + (self._get_literal_value(ti),)
201
elif isinstance(literal, _ast.Set):
203
for si in literal.elts:
204
return_set.add(self._get_literal_value(si))
207
elif isinstance(literal, _ast.Dict):
208
return dict(zip(literal.keys, literal.values))
210
elif isinstance(literal, _ast.Ellipsis):
211
# what do we want to do with this?
214
elif isinstance(literal, _ast.Name):
217
# NOTE(sigmavirus24): NameConstants are only part of the AST in Python
218
# 3. NameConstants tend to refer to things like True and False. This
219
# prevents them from being re-assigned in Python 3.
220
elif six.PY3 and isinstance(literal, _ast.NameConstant):
221
return str(literal.value)
223
# NOTE(sigmavirus24): Bytes are only part of the AST in Python 3
224
elif six.PY3 and isinstance(literal, _ast.Bytes):
230
def check_call_arg_value(self, argument_name, argument_values=None):
231
'''Checks for a value of a named argument in a function call.
233
Returns none if the specified argument is not found.
234
:param argument_name: A string - name of the argument to look for
235
:param argument_values: the value, or list of values to test against
236
:return: Boolean True if argument found and matched, False if
237
found and not matched, None if argument not found at all
239
kwd_values = self.call_keywords
240
if (kwd_values is not None and
241
argument_name in kwd_values):
242
if not isinstance(argument_values, list):
243
# if passed a single value, or a tuple, convert to a list
244
argument_values = list((argument_values,))
245
for val in argument_values:
246
if kwd_values[argument_name] == val:
247
# if matched, fix up the context lineno for reporting
248
self.set_lineno_for_call_arg(argument_name)
252
# argument name not found, return None to allow testing for this
256
def set_lineno_for_call_arg(self, argument_name):
257
'''Updates the line number for a specific named argument
259
If a call is split over multiple lines, when a keyword arg is found
260
the issue will be reported with the line number of the start of the
261
call. This function updates the line number in the current context
262
copy to match the actual line where the match occurs.
263
:call_node: the call to find the argument in
264
:param argument_name: A string - name of the argument to look for
265
:return: Integer - the line number of the found argument
267
for key in self.node.keywords:
268
if key.arg is argument_name:
269
self._context['lineno'] = key.value.lineno
271
def get_call_arg_at_position(self, position_num):
272
'''Returns positional argument at the specified position (if it exists)
274
:param position_num: The index of the argument to return the value for
275
:return: Value of the argument at the specified position if it exists
278
hasattr(self._context['call'], 'args') and
279
position_num < len(self._context['call'].args)
281
return self._get_literal_value(
282
self._context['call'].args[position_num]
287
def is_module_being_imported(self, module):
288
'''Check for the specified module is currently being imported
290
:param module: The module name to look for
291
:return: True if the module is found, False otherwise
293
if 'module' in self._context and self._context['module'] == module:
298
def is_module_imported_exact(self, module):
299
'''Check if a specified module has been imported; only exact matches.
301
:param module: The module name to look for
302
:return: True if the module is found, False otherwise
304
if 'imports' in self._context and module in self._context['imports']:
309
def is_module_imported_like(self, module):
310
'''Check if a specified module has been imported
312
Check if a specified module has been imported; specified module exists
313
as part of any import statement.
314
:param module: The module name to look for
315
:return: True if the module is found, False otherwise
317
if 'imports' in self._context:
318
for imp in self._context['imports']: