65 |
65 |
66 |
66 |
67 def hasShell(context): |
67 def hasShell(context): |
68 """ |
68 """ |
69 Function to check, if the node of the context contains the shell keyword. |
69 Function to check, if the node of the context contains the shell keyword. |
70 |
70 |
71 @param context context to be inspected |
71 @param context context to be inspected |
72 @type SecurityContext |
72 @type SecurityContext |
73 @return tuple containing a flag indicating the presence of the 'shell' |
73 @return tuple containing a flag indicating the presence of the 'shell' |
74 argument and flag indicating the value of the 'shell' argument |
74 argument and flag indicating the value of the 'shell' argument |
75 @rtype tuple of (bool, bool) |
75 @rtype tuple of (bool, bool) |
76 """ |
76 """ |
77 keywords = context.node.keywords |
77 keywords = context.node.keywords |
78 result = False |
78 result = False |
79 shell = False |
79 shell = False |
80 if 'shell' in context.callKeywords: |
80 if "shell" in context.callKeywords: |
81 shell = True |
81 shell = True |
82 for key in keywords: |
82 for key in keywords: |
83 if key.arg == 'shell': |
83 if key.arg == "shell": |
84 val = key.value |
84 val = key.value |
85 if AstUtilities.isNumber(val): |
85 if AstUtilities.isNumber(val): |
86 result = bool(val.n) |
86 result = bool(val.n) |
87 elif isinstance(val, ast.List): |
87 elif isinstance(val, ast.List): |
88 result = bool(val.elts) |
88 result = bool(val.elts) |
89 elif isinstance(val, ast.Dict): |
89 elif isinstance(val, ast.Dict): |
90 result = bool(val.keys) |
90 result = bool(val.keys) |
91 elif isinstance(val, ast.Name) and val.id in ['False', 'None']: |
91 elif isinstance(val, ast.Name) and val.id in ["False", "None"]: |
92 result = False |
92 result = False |
93 elif AstUtilities.isNameConstant(val): |
93 elif AstUtilities.isNameConstant(val): |
94 result = val.value |
94 result = val.value |
95 else: |
95 else: |
96 result = True |
96 result = True |
97 |
97 |
98 return shell, result |
98 return shell, result |
99 |
99 |
100 |
100 |
101 def checkSubprocessPopenWithShell(reportError, context, config): |
101 def checkSubprocessPopenWithShell(reportError, context, config): |
102 """ |
102 """ |
103 Function to check for use of popen with shell equals true. |
103 Function to check for use of popen with shell equals true. |
104 |
104 |
105 @param reportError function to be used to report errors |
105 @param reportError function to be used to report errors |
106 @type func |
106 @type func |
107 @param context security context object |
107 @param context security context object |
108 @type SecurityContext |
108 @type SecurityContext |
109 @param config dictionary with configuration data |
109 @param config dictionary with configuration data |
110 @type dict |
110 @type dict |
111 """ |
111 """ |
112 functionNames = ( |
112 functionNames = ( |
113 config["shell_injection_subprocess"] |
113 config["shell_injection_subprocess"] |
114 if config and "shell_injection_subprocess" in config else |
114 if config and "shell_injection_subprocess" in config |
115 SecurityDefaults["shell_injection_subprocess"] |
115 else SecurityDefaults["shell_injection_subprocess"] |
116 ) |
116 ) |
117 |
117 |
118 if context.callFunctionNameQual in functionNames: |
118 if context.callFunctionNameQual in functionNames: |
119 shell, shellValue = hasShell(context) |
119 shell, shellValue = hasShell(context) |
120 if shell and shellValue and len(context.callArgs) > 0: |
120 if shell and shellValue and len(context.callArgs) > 0: |
121 sev = _evaluateShellCall(context) |
121 sev = _evaluateShellCall(context) |
122 if sev == "L": |
122 if sev == "L": |
123 reportError( |
123 reportError( |
124 context.getLinenoForCallArg('shell') - 1, |
124 context.getLinenoForCallArg("shell") - 1, |
125 context.getOffsetForCallArg('shell'), |
125 context.getOffsetForCallArg("shell"), |
126 "S602.L", |
126 "S602.L", |
127 sev, |
127 sev, |
128 "H", |
128 "H", |
129 ) |
129 ) |
130 else: |
130 else: |
131 reportError( |
131 reportError( |
132 context.getLinenoForCallArg('shell') - 1, |
132 context.getLinenoForCallArg("shell") - 1, |
133 context.getOffsetForCallArg('shell'), |
133 context.getOffsetForCallArg("shell"), |
134 "S602.H", |
134 "S602.H", |
135 sev, |
135 sev, |
136 "H", |
136 "H", |
137 ) |
137 ) |
138 |
138 |
139 |
139 |
140 def checkSubprocessPopenWithoutShell(reportError, context, config): |
140 def checkSubprocessPopenWithoutShell(reportError, context, config): |
141 """ |
141 """ |
142 Function to check for use of popen without shell equals true. |
142 Function to check for use of popen without shell equals true. |
143 |
143 |
144 @param reportError function to be used to report errors |
144 @param reportError function to be used to report errors |
145 @type func |
145 @type func |
146 @param context security context object |
146 @param context security context object |
147 @type SecurityContext |
147 @type SecurityContext |
148 @param config dictionary with configuration data |
148 @param config dictionary with configuration data |
149 @type dict |
149 @type dict |
150 """ |
150 """ |
151 functionNames = ( |
151 functionNames = ( |
152 config["shell_injection_subprocess"] |
152 config["shell_injection_subprocess"] |
153 if config and "shell_injection_subprocess" in config else |
153 if config and "shell_injection_subprocess" in config |
154 SecurityDefaults["shell_injection_subprocess"] |
154 else SecurityDefaults["shell_injection_subprocess"] |
155 ) |
155 ) |
156 |
156 |
157 if ( |
157 if context.callFunctionNameQual in functionNames and not hasShell(context)[0]: |
158 context.callFunctionNameQual in functionNames and |
|
159 not hasShell(context)[0] |
|
160 ): |
|
161 reportError( |
158 reportError( |
162 context.node.lineno - 1, |
159 context.node.lineno - 1, |
163 context.node.col_offset, |
160 context.node.col_offset, |
164 "S603", |
161 "S603", |
165 "L", |
162 "L", |
168 |
165 |
169 |
166 |
170 def checkOtherFunctionWithShell(reportError, context, config): |
167 def checkOtherFunctionWithShell(reportError, context, config): |
171 """ |
168 """ |
172 Function to check for any function with shell equals true. |
169 Function to check for any function with shell equals true. |
173 |
170 |
174 @param reportError function to be used to report errors |
171 @param reportError function to be used to report errors |
175 @type func |
172 @type func |
176 @param context security context object |
173 @param context security context object |
177 @type SecurityContext |
174 @type SecurityContext |
178 @param config dictionary with configuration data |
175 @param config dictionary with configuration data |
179 @type dict |
176 @type dict |
180 """ |
177 """ |
181 functionNames = ( |
178 functionNames = ( |
182 config["shell_injection_subprocess"] |
179 config["shell_injection_subprocess"] |
183 if config and "shell_injection_subprocess" in config else |
180 if config and "shell_injection_subprocess" in config |
184 SecurityDefaults["shell_injection_subprocess"] |
181 else SecurityDefaults["shell_injection_subprocess"] |
185 ) |
182 ) |
186 |
183 |
187 if context.callFunctionNameQual not in functionNames: |
184 if context.callFunctionNameQual not in functionNames: |
188 shell, shellValue = hasShell(context) |
185 shell, shellValue = hasShell(context) |
189 if shell and shellValue: |
186 if shell and shellValue: |
190 reportError( |
187 reportError( |
191 context.getLinenoForCallArg('shell') - 1, |
188 context.getLinenoForCallArg("shell") - 1, |
192 context.getOffsetForCallArg('shell'), |
189 context.getOffsetForCallArg("shell"), |
193 "S604", |
190 "S604", |
194 "M", |
191 "M", |
195 "L", |
192 "L", |
196 ) |
193 ) |
197 |
194 |
198 |
195 |
199 def checkStartProcessWithShell(reportError, context, config): |
196 def checkStartProcessWithShell(reportError, context, config): |
200 """ |
197 """ |
201 Function to check for starting a process with a shell. |
198 Function to check for starting a process with a shell. |
202 |
199 |
203 @param reportError function to be used to report errors |
200 @param reportError function to be used to report errors |
204 @type func |
201 @type func |
205 @param context security context object |
202 @param context security context object |
206 @type SecurityContext |
203 @type SecurityContext |
207 @param config dictionary with configuration data |
204 @param config dictionary with configuration data |
208 @type dict |
205 @type dict |
209 """ |
206 """ |
210 functionNames = ( |
207 functionNames = ( |
211 config["shell_injection_shell"] |
208 config["shell_injection_shell"] |
212 if config and "shell_injection_shell" in config else |
209 if config and "shell_injection_shell" in config |
213 SecurityDefaults["shell_injection_shell"] |
210 else SecurityDefaults["shell_injection_shell"] |
214 ) |
211 ) |
215 |
212 |
216 if ( |
213 if context.callFunctionNameQual in functionNames and len(context.callArgs) > 0: |
217 context.callFunctionNameQual in functionNames and |
|
218 len(context.callArgs) > 0 |
|
219 ): |
|
220 sev = _evaluateShellCall(context) |
214 sev = _evaluateShellCall(context) |
221 if sev == "L": |
215 if sev == "L": |
222 reportError( |
216 reportError( |
223 context.node.lineno - 1, |
217 context.node.lineno - 1, |
224 context.node.col_offset, |
218 context.node.col_offset, |
237 |
231 |
238 |
232 |
239 def checkStartProcessWithNoShell(reportError, context, config): |
233 def checkStartProcessWithNoShell(reportError, context, config): |
240 """ |
234 """ |
241 Function to check for starting a process with no shell. |
235 Function to check for starting a process with no shell. |
242 |
236 |
243 @param reportError function to be used to report errors |
237 @param reportError function to be used to report errors |
244 @type func |
238 @type func |
245 @param context security context object |
239 @param context security context object |
246 @type SecurityContext |
240 @type SecurityContext |
247 @param config dictionary with configuration data |
241 @param config dictionary with configuration data |
248 @type dict |
242 @type dict |
249 """ |
243 """ |
250 functionNames = ( |
244 functionNames = ( |
251 config["shell_injection_noshell"] |
245 config["shell_injection_noshell"] |
252 if config and "shell_injection_noshell" in config else |
246 if config and "shell_injection_noshell" in config |
253 SecurityDefaults["shell_injection_noshell"] |
247 else SecurityDefaults["shell_injection_noshell"] |
254 ) |
248 ) |
255 |
249 |
256 if context.callFunctionNameQual in functionNames: |
250 if context.callFunctionNameQual in functionNames: |
257 reportError( |
251 reportError( |
258 context.node.lineno - 1, |
252 context.node.lineno - 1, |
259 context.node.col_offset, |
253 context.node.col_offset, |
260 "S606", |
254 "S606", |
264 |
258 |
265 |
259 |
266 def checkStartProcessWithPartialPath(reportError, context, config): |
260 def checkStartProcessWithPartialPath(reportError, context, config): |
267 """ |
261 """ |
268 Function to check for starting a process with no shell. |
262 Function to check for starting a process with no shell. |
269 |
263 |
270 @param reportError function to be used to report errors |
264 @param reportError function to be used to report errors |
271 @type func |
265 @type func |
272 @param context security context object |
266 @param context security context object |
273 @type SecurityContext |
267 @type SecurityContext |
274 @param config dictionary with configuration data |
268 @param config dictionary with configuration data |
275 @type dict |
269 @type dict |
276 """ |
270 """ |
277 functionNames = ( |
271 functionNames = ( |
278 config["shell_injection_subprocess"] |
272 config["shell_injection_subprocess"] |
279 if config and "shell_injection_subprocess" in config else |
273 if config and "shell_injection_subprocess" in config |
280 SecurityDefaults["shell_injection_subprocess"] |
274 else SecurityDefaults["shell_injection_subprocess"] |
281 ) |
275 ) |
282 |
276 |
283 if config and "shell_injection_shell" in config: |
277 if config and "shell_injection_shell" in config: |
284 functionNames += config["shell_injection_shell"] |
278 functionNames += config["shell_injection_shell"] |
285 else: |
279 else: |
286 functionNames += SecurityDefaults["shell_injection_shell"] |
280 functionNames += SecurityDefaults["shell_injection_shell"] |
287 |
281 |
288 if config and "shell_injection_noshell" in config: |
282 if config and "shell_injection_noshell" in config: |
289 functionNames += config["shell_injection_noshell"] |
283 functionNames += config["shell_injection_noshell"] |
290 else: |
284 else: |
291 functionNames += SecurityDefaults["shell_injection_noshell"] |
285 functionNames += SecurityDefaults["shell_injection_noshell"] |
292 |
286 |
293 if ( |
287 if len(context.callArgs) and context.callFunctionNameQual in functionNames: |
294 len(context.callArgs) and |
|
295 context.callFunctionNameQual in functionNames |
|
296 ): |
|
297 node = context.node.args[0] |
288 node = context.node.args[0] |
298 |
289 |
299 # some calls take an arg list, check the first part |
290 # some calls take an arg list, check the first part |
300 if isinstance(node, ast.List): |
291 if isinstance(node, ast.List): |
301 node = node.elts[0] |
292 node = node.elts[0] |
302 |
293 |
303 # make sure the param is a string literal and not a var name |
294 # make sure the param is a string literal and not a var name |
304 if ( |
295 if AstUtilities.isString(node) and not fullPathMatchRe.match(node.s): |
305 AstUtilities.isString(node) and |
|
306 not fullPathMatchRe.match(node.s) |
|
307 ): |
|
308 reportError( |
296 reportError( |
309 context.node.lineno - 1, |
297 context.node.lineno - 1, |
310 context.node.col_offset, |
298 context.node.col_offset, |
311 "S607", |
299 "S607", |
312 "L", |
300 "L", |