~ubuntu-branches/ubuntu/wily/bandit/wily-proposed

« back to all changes in this revision

Viewing changes to bandit/core/context.py

  • Committer: Package Import Robot
  • Author(s): Dave Walker (Daviey)
  • Date: 2015-07-22 09:01:39 UTC
  • Revision ID: package-import@ubuntu.com-20150722090139-fl0nluy0x8m9ctx4
Tags: upstream-0.12.0
ImportĀ upstreamĀ versionĀ 0.12.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding:utf-8 -*-
 
2
#
 
3
# Copyright 2014 Hewlett-Packard Development Company, L.P.
 
4
#
 
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
 
8
#
 
9
#      http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
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
 
15
# under the License.
 
16
 
 
17
import _ast
 
18
 
 
19
import six
 
20
 
 
21
from bandit.core import utils
 
22
 
 
23
 
 
24
class Context():
 
25
    def __init__(self, context_object=None):
 
26
        '''Initialize the class with a context, empty dict otherwise
 
27
 
 
28
        :param context_object: The context object to create class from
 
29
        :return: -
 
30
        '''
 
31
        if context_object is not None:
 
32
            self._context = context_object
 
33
        else:
 
34
            self._context = dict()
 
35
 
 
36
    def __repr__(self):
 
37
        '''Generate representation of object for printing / interactive use
 
38
 
 
39
        Most likely only interested in non-default properties, so we return
 
40
        the string version of _context.
 
41
 
 
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'}>
 
48
 
 
49
        :return: A string representation of the object
 
50
        '''
 
51
        return "<Context %s>" % self._context
 
52
 
 
53
    @property
 
54
    def call_args(self):
 
55
        '''Get a list of function args
 
56
 
 
57
        :return: A list of function args
 
58
        '''
 
59
        args = []
 
60
        for arg in self._context['call'].args:
 
61
            if hasattr(arg, 'attr'):
 
62
                args.append(arg.attr)
 
63
            else:
 
64
                args.append(self._get_literal_value(arg))
 
65
        return args
 
66
 
 
67
    @property
 
68
    def call_args_count(self):
 
69
        '''Get the number of args a function call has
 
70
 
 
71
        :return: The number of args a function call has
 
72
        '''
 
73
        if hasattr(self._context['call'], 'args'):
 
74
            return len(self._context['call'].args)
 
75
        else:
 
76
            return None
 
77
 
 
78
    @property
 
79
    def call_args_string(self):
 
80
        '''Get a string representation of the call arguments
 
81
 
 
82
        :return: Returns a string representation of the call arguments
 
83
        '''
 
84
        if 'call' in self._context and hasattr(self._context, 'args'):
 
85
            return utils.ast_args_to_str(self._context['call'].args)
 
86
        else:
 
87
            return ''
 
88
 
 
89
    @property
 
90
    def call_function_name(self):
 
91
        '''Get the name (not FQ) of a function call
 
92
 
 
93
        :return: The name (not FQ) of a function call
 
94
        '''
 
95
        if 'name' in self._context:
 
96
            return self._context['name']
 
97
        else:
 
98
            return None
 
99
 
 
100
    @property
 
101
    def call_function_name_qual(self):
 
102
        '''Get the FQ name of a function call
 
103
 
 
104
        :return: The FQ name of a function call
 
105
        '''
 
106
        if 'qualname' in self._context:
 
107
            return self._context['qualname']
 
108
        else:
 
109
            return None
 
110
 
 
111
    @property
 
112
    def call_keywords(self):
 
113
        '''Get a dictionary of keyword parameters
 
114
 
 
115
        :return: A dictionary of keyword parameters for a call as strings
 
116
        '''
 
117
        if (
 
118
            'call' in self._context and
 
119
            hasattr(self._context['call'], 'keywords')
 
120
        ):
 
121
            return_dict = {}
 
122
            for li in self._context['call'].keywords:
 
123
                if hasattr(li.value, 'attr'):
 
124
                    return_dict[li.arg] = li.value.attr
 
125
                else:
 
126
                    return_dict[li.arg] = self._get_literal_value(li.value)
 
127
            return return_dict
 
128
        else:
 
129
            return None
 
130
 
 
131
    @property
 
132
    def node(self):
 
133
        '''Get the raw AST node associated with the context
 
134
 
 
135
        :return: The raw AST node associated with the context
 
136
        '''
 
137
        if 'node' in self._context:
 
138
            return self._context['node']
 
139
        else:
 
140
            return None
 
141
 
 
142
    @property
 
143
    def string_val(self):
 
144
        '''Get a string value of a standalone string
 
145
 
 
146
        :return: String value of a standalone string
 
147
        '''
 
148
        if 'str' in self._context:
 
149
            return utils.safe_str(self._context['str'])
 
150
        else:
 
151
            return None
 
152
 
 
153
    @property
 
154
    def statement(self):
 
155
        '''Get the raw AST for the current statement
 
156
 
 
157
        :return: The raw AST for the current statement
 
158
        '''
 
159
        if 'statement' in self._context:
 
160
            return self._context['statement']
 
161
        else:
 
162
            return None
 
163
 
 
164
    @property
 
165
    def function_def_defaults_qual(self):
 
166
        '''Get a list of fully qualified default values in a function def
 
167
 
 
168
        :return: List of defaults
 
169
        '''
 
170
        defaults = []
 
171
        for default in self._context['node'].args.defaults:
 
172
            defaults.append(utils.get_qual_attr(
 
173
                default,
 
174
                self._context['import_aliases']))
 
175
        return defaults
 
176
 
 
177
    def _get_literal_value(self, literal):
 
178
        '''Utility function to turn AST literals into native Python types
 
179
 
 
180
        :param literal: The AST literal to convert
 
181
        :return: The value of the AST literal
 
182
        '''
 
183
        if isinstance(literal, _ast.Num):
 
184
            return literal.n
 
185
 
 
186
        elif isinstance(literal, _ast.Str):
 
187
            return literal.s
 
188
 
 
189
        elif isinstance(literal, _ast.List):
 
190
            return_list = list()
 
191
            for li in literal.elts:
 
192
                return_list.append(self._get_literal_value(li))
 
193
            return return_list
 
194
 
 
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),)
 
199
            return return_tuple
 
200
 
 
201
        elif isinstance(literal, _ast.Set):
 
202
            return_set = set()
 
203
            for si in literal.elts:
 
204
                return_set.add(self._get_literal_value(si))
 
205
            return return_set
 
206
 
 
207
        elif isinstance(literal, _ast.Dict):
 
208
            return dict(zip(literal.keys, literal.values))
 
209
 
 
210
        elif isinstance(literal, _ast.Ellipsis):
 
211
            # what do we want to do with this?
 
212
            pass
 
213
 
 
214
        elif isinstance(literal, _ast.Name):
 
215
            return literal.id
 
216
 
 
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)
 
222
 
 
223
        # NOTE(sigmavirus24): Bytes are only part of the AST in Python 3
 
224
        elif six.PY3 and isinstance(literal, _ast.Bytes):
 
225
            return literal.s
 
226
 
 
227
        else:
 
228
            return None
 
229
 
 
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.
 
232
 
 
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
 
238
        '''
 
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)
 
249
                    return True
 
250
            return False
 
251
        else:
 
252
            # argument name not found, return None to allow testing for this
 
253
            # eventuality
 
254
            return None
 
255
 
 
256
    def set_lineno_for_call_arg(self, argument_name):
 
257
        '''Updates the line number for a specific named argument
 
258
 
 
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
 
266
        '''
 
267
        for key in self.node.keywords:
 
268
            if key.arg is argument_name:
 
269
                self._context['lineno'] = key.value.lineno
 
270
 
 
271
    def get_call_arg_at_position(self, position_num):
 
272
        '''Returns positional argument at the specified position (if it exists)
 
273
 
 
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
 
276
        '''
 
277
        if (
 
278
            hasattr(self._context['call'], 'args') and
 
279
            position_num < len(self._context['call'].args)
 
280
        ):
 
281
            return self._get_literal_value(
 
282
                self._context['call'].args[position_num]
 
283
            )
 
284
        else:
 
285
            return None
 
286
 
 
287
    def is_module_being_imported(self, module):
 
288
        '''Check for the specified module is currently being imported
 
289
 
 
290
        :param module: The module name to look for
 
291
        :return: True if the module is found, False otherwise
 
292
        '''
 
293
        if 'module' in self._context and self._context['module'] == module:
 
294
            return True
 
295
        else:
 
296
            return False
 
297
 
 
298
    def is_module_imported_exact(self, module):
 
299
        '''Check if a specified module has been imported; only exact matches.
 
300
 
 
301
        :param module: The module name to look for
 
302
        :return: True if the module is found, False otherwise
 
303
        '''
 
304
        if 'imports' in self._context and module in self._context['imports']:
 
305
            return True
 
306
        else:
 
307
            return False
 
308
 
 
309
    def is_module_imported_like(self, module):
 
310
        '''Check if a specified module has been imported
 
311
 
 
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
 
316
        '''
 
317
        if 'imports' in self._context:
 
318
            for imp in self._context['imports']:
 
319
                if module in imp:
 
320
                    return True
 
321
        return False