0
# Context Analysis
1
2
AST node analysis and import tracking during security test execution. The Context system provides detailed information about function calls, imports, and code patterns essential for accurate vulnerability detection in Python source code.
3
4
## Capabilities
5
6
### Context
7
8
Provides contextual information during AST node analysis for security tests. Context objects give access to function call details, string literals, import patterns, and other code analysis data.
9
10
```python { .api }
11
class Context:
12
def __init__(self, context_object=None):
13
"""
14
Initialize context with optional context object.
15
16
Parameters:
17
- context_object: optional context data for initialization
18
"""
19
20
@property
21
def call_function_name(self):
22
"""
23
Get function name being called (not fully qualified).
24
25
Returns:
26
str: Function name or None if not a function call
27
"""
28
29
@property
30
def call_function_name_qual(self):
31
"""
32
Get fully qualified function name including module path.
33
34
Returns:
35
str: Qualified function name (e.g., 'os.system') or None
36
"""
37
38
@property
39
def call_args(self):
40
"""
41
Get list of function call arguments.
42
43
Returns:
44
list: Function call arguments as AST nodes
45
"""
46
47
@property
48
def call_args_count(self):
49
"""
50
Get number of function call arguments.
51
52
Returns:
53
int: Number of arguments in function call
54
"""
55
56
@property
57
def call_keywords(self):
58
"""
59
Get dictionary of keyword parameters.
60
61
Returns:
62
dict: Keyword arguments mapped to values
63
"""
64
65
@property
66
def string_literal_value(self):
67
"""
68
Get value of string literal node.
69
70
Returns:
71
str: String literal value or None if not a string literal
72
"""
73
74
@property
75
def node(self):
76
"""
77
Get the current AST node being analyzed.
78
79
Returns:
80
ast.Node: Current AST node
81
"""
82
83
@property
84
def string_val(self):
85
"""
86
Get string literal value (alternative to string_literal_value).
87
88
Returns:
89
str: String value or None
90
"""
91
92
@property
93
def bytes_val(self):
94
"""
95
Get bytes literal value.
96
97
Returns:
98
bytes: Bytes value or None
99
"""
100
101
def is_module_being_imported(self, module):
102
"""
103
Check if specified module is being imported.
104
105
Parameters:
106
- module: str, module name to check
107
108
Returns:
109
bool: True if module is being imported
110
"""
111
112
def is_module_imported_like(self, module):
113
"""
114
Check if module is imported with pattern matching.
115
116
Parameters:
117
- module: str, module pattern to match
118
119
Returns:
120
bool: True if module matches import pattern
121
"""
122
123
def is_module_imported_exact(self, module):
124
"""
125
Check if exact module name is imported.
126
127
Parameters:
128
- module: str, exact module name
129
130
Returns:
131
bool: True if exact module is imported
132
"""
133
134
def check_call_arg_value(self, argument_name, argument_values):
135
"""
136
Check if function call argument matches specific values.
137
138
Parameters:
139
- argument_name: str, name of argument to check
140
- argument_values: list, acceptable values for the argument
141
142
Returns:
143
bool: True if argument matches any of the values
144
"""
145
146
def get_lineno_for_call_arg(self, argument_name):
147
"""
148
Get line number for specific function call argument.
149
150
Parameters:
151
- argument_name: str, name of argument
152
153
Returns:
154
int: Line number or None if not found
155
"""
156
```
157
158
## Usage Examples
159
160
### Function Call Analysis
161
162
```python
163
from bandit.core import test_properties as test
164
import bandit
165
166
@test.checks('Call')
167
@test.test_id('B999')
168
def check_dangerous_calls(context):
169
"""Example security test using context analysis."""
170
171
# Check function name
172
if context.call_function_name == 'eval':
173
return bandit.Issue(
174
severity=bandit.HIGH,
175
confidence=bandit.HIGH,
176
text="Use of eval() detected - potential code injection",
177
cwe=94
178
)
179
180
# Check qualified function name
181
if context.call_function_name_qual == 'os.system':
182
# Analyze arguments
183
if context.call_args_count > 0:
184
first_arg = context.call_args[0]
185
if hasattr(first_arg, 's'): # String literal
186
command = first_arg.s
187
if any(char in command for char in ['&', '|', ';']):
188
return bandit.Issue(
189
severity=bandit.HIGH,
190
confidence=bandit.MEDIUM,
191
text="Shell command with potential injection vectors",
192
cwe=78
193
)
194
```
195
196
### Import Pattern Detection
197
198
```python
199
@test.checks('Import', 'ImportFrom')
200
@test.test_id('B900')
201
def check_dangerous_imports(context):
202
"""Check for dangerous module imports."""
203
204
# Check for specific dangerous imports
205
dangerous_modules = ['pickle', 'cPickle', 'dill', 'shelve']
206
207
for module in dangerous_modules:
208
if context.is_module_being_imported(module):
209
return bandit.Issue(
210
severity=bandit.MEDIUM,
211
confidence=bandit.HIGH,
212
text=f"Import of {module} module detected - potential deserialization risk",
213
cwe=502
214
)
215
216
# Check for wildcard imports from security-sensitive modules
217
if context.is_module_imported_like('subprocess.*'):
218
return bandit.Issue(
219
severity=bandit.LOW,
220
confidence=bandit.MEDIUM,
221
text="Wildcard import from subprocess module",
222
cwe=20
223
)
224
```
225
226
### String Literal Analysis
227
228
```python
229
@test.checks('Str')
230
@test.test_id('B901')
231
def check_hardcoded_secrets(context):
232
"""Check for hardcoded secrets in string literals."""
233
234
if context.string_literal_value:
235
value = context.string_literal_value.lower()
236
237
# Check for common secret patterns
238
secret_patterns = [
239
'password=',
240
'secret_key=',
241
'api_key=',
242
'access_token=',
243
'private_key='
244
]
245
246
for pattern in secret_patterns:
247
if pattern in value and len(value) > 20:
248
return bandit.Issue(
249
severity=bandit.HIGH,
250
confidence=bandit.MEDIUM,
251
text="Potential hardcoded secret detected",
252
cwe=259
253
)
254
```
255
256
### Keyword Argument Analysis
257
258
```python
259
@test.checks('Call')
260
@test.test_id('B902')
261
def check_ssl_context(context):
262
"""Check SSL context configuration."""
263
264
if context.call_function_name_qual in ['ssl.create_default_context', 'ssl.SSLContext']:
265
keywords = context.call_keywords
266
267
# Check for disabled certificate verification
268
if 'check_hostname' in keywords:
269
if hasattr(keywords['check_hostname'], 'value') and not keywords['check_hostname'].value:
270
return bandit.Issue(
271
severity=bandit.HIGH,
272
confidence=bandit.HIGH,
273
text="SSL hostname verification disabled",
274
cwe=295
275
)
276
277
# Check for weak SSL versions
278
if 'protocol' in keywords:
279
protocol_node = keywords['protocol']
280
if hasattr(protocol_node, 'attr') and 'SSL' in protocol_node.attr:
281
if any(weak in protocol_node.attr for weak in ['SSLv2', 'SSLv3', 'TLSv1']):
282
return bandit.Issue(
283
severity=bandit.HIGH,
284
confidence=bandit.HIGH,
285
text="Weak SSL/TLS protocol version",
286
cwe=326
287
)
288
```
289
290
### Complex Context Analysis
291
292
```python
293
@test.checks('Call')
294
@test.test_id('B903')
295
def check_subprocess_shell(context):
296
"""Advanced subprocess call analysis."""
297
298
if context.call_function_name_qual in ['subprocess.call', 'subprocess.run', 'subprocess.Popen']:
299
keywords = context.call_keywords
300
args = context.call_args
301
302
# Check if shell=True is used
303
shell_enabled = False
304
if 'shell' in keywords:
305
shell_node = keywords['shell']
306
if hasattr(shell_node, 'value') and shell_node.value:
307
shell_enabled = True
308
309
if shell_enabled and args:
310
# Analyze first argument for user input patterns
311
first_arg = args[0]
312
if hasattr(first_arg, 's'): # String literal
313
command = first_arg.s
314
# Check for common injection patterns
315
if any(pattern in command for pattern in ['%s', '{}', '+', 'format(']):
316
return bandit.Issue(
317
severity=bandit.HIGH,
318
confidence=bandit.MEDIUM,
319
text="subprocess call with shell=True and potential injection",
320
cwe=78
321
)
322
```