src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Security/Checks/djangoXssVulnerability.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9325
8157eb19aba5
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
21 21
22 22
23 def getChecks(): 23 def getChecks():
24 """ 24 """
25 Public method to get a dictionary with checks handled by this module. 25 Public method to get a dictionary with checks handled by this module.
26 26
27 @return dictionary containing checker lists containing checker function and 27 @return dictionary containing checker lists containing checker function and
28 list of codes 28 list of codes
29 @rtype dict 29 @rtype dict
30 """ 30 """
31 return { 31 return {
36 36
37 37
38 def checkDjangoXssVulnerability(reportError, context, config): 38 def checkDjangoXssVulnerability(reportError, context, config):
39 """ 39 """
40 Function to check for potential XSS vulnerability. 40 Function to check for potential XSS vulnerability.
41 41
42 @param reportError function to be used to report errors 42 @param reportError function to be used to report errors
43 @type func 43 @type func
44 @param context security context object 44 @param context security context object
45 @type SecurityContext 45 @type SecurityContext
46 @param config dictionary with configuration data 46 @param config dictionary with configuration data
47 @type dict 47 @type dict
48 """ 48 """
49 if context.isModuleImportedLike('django.utils.safestring'): 49 if context.isModuleImportedLike("django.utils.safestring"):
50 affectedFunctions = [ 50 affectedFunctions = [
51 'mark_safe', 51 "mark_safe",
52 'SafeText', 52 "SafeText",
53 'SafeUnicode', 53 "SafeUnicode",
54 'SafeString', 54 "SafeString",
55 'SafeBytes' 55 "SafeBytes",
56 ] 56 ]
57 if context.callFunctionName in affectedFunctions: 57 if context.callFunctionName in affectedFunctions:
58 xss = context.node.args[0] 58 xss = context.node.args[0]
59 if not AstUtilities.isString(xss): 59 if not AstUtilities.isString(xss):
60 checkPotentialRisk(reportError, context.node) 60 checkPotentialRisk(reportError, context.node)
61 61
62 62
63 def checkPotentialRisk(reportError, node): 63 def checkPotentialRisk(reportError, node):
64 """ 64 """
65 Function to check a given node for a potential XSS vulnerability. 65 Function to check a given node for a potential XSS vulnerability.
66 66
67 @param reportError function to be used to report errors 67 @param reportError function to be used to report errors
68 @type func 68 @type func
69 @param node node to be checked 69 @param node node to be checked
70 @type ast.Call 70 @type ast.Call
71 """ 71 """
72 xssVar = node.args[0] 72 xssVar = node.args[0]
73 73
74 secure = False 74 secure = False
75 75
76 if isinstance(xssVar, ast.Name): 76 if isinstance(xssVar, ast.Name):
77 # Check if the var are secure 77 # Check if the var are secure
78 parent = node._securityParent 78 parent = node._securityParent
79 while not isinstance(parent, (ast.Module, ast.FunctionDef)): 79 while not isinstance(parent, (ast.Module, ast.FunctionDef)):
80 parent = parent._securityParent 80 parent = parent._securityParent
81 81
82 isParam = False 82 isParam = False
83 if isinstance(parent, ast.FunctionDef): 83 if isinstance(parent, ast.FunctionDef):
84 for name in parent.args.args: 84 for name in parent.args.args:
85 if name.arg == xssVar.id: 85 if name.arg == xssVar.id:
86 isParam = True 86 isParam = True
87 break 87 break
88 88
89 if not isParam: 89 if not isParam:
90 secure = evaluateVar(xssVar, parent, node.lineno) 90 secure = evaluateVar(xssVar, parent, node.lineno)
91 elif isinstance(xssVar, ast.Call): 91 elif isinstance(xssVar, ast.Call):
92 parent = node._securityParent 92 parent = node._securityParent
93 while not isinstance(parent, (ast.Module, ast.FunctionDef)): 93 while not isinstance(parent, (ast.Module, ast.FunctionDef)):
100 parent = node._securityParent 100 parent = node._securityParent
101 while not isinstance(parent, (ast.Module, ast.FunctionDef)): 101 while not isinstance(parent, (ast.Module, ast.FunctionDef)):
102 parent = parent._securityParent 102 parent = parent._securityParent
103 newCall = transform2call(xssVar) 103 newCall = transform2call(xssVar)
104 secure = evaluateCall(newCall, parent) 104 secure = evaluateCall(newCall, parent)
105 105
106 if not secure: 106 if not secure:
107 reportError( 107 reportError(node.lineno - 1, node.col_offset, "S703", "M", "H")
108 node.lineno - 1,
109 node.col_offset,
110 "S703",
111 "M",
112 "H"
113 )
114 108
115 109
116 class DeepAssignation: 110 class DeepAssignation:
117 """ 111 """
118 Class to perform a deep analysis of an assign. 112 Class to perform a deep analysis of an assign.
119 """ 113 """
114
120 def __init__(self, varName, ignoreNodes=None): 115 def __init__(self, varName, ignoreNodes=None):
121 """ 116 """
122 Constructor 117 Constructor
123 118
124 @param varName name of the variable 119 @param varName name of the variable
125 @type str 120 @type str
126 @param ignoreNodes list of nodes to ignore 121 @param ignoreNodes list of nodes to ignore
127 @type list of ast.AST 122 @type list of ast.AST
128 """ 123 """
129 self.__varName = varName 124 self.__varName = varName
130 self.__ignoreNodes = ignoreNodes 125 self.__ignoreNodes = ignoreNodes
131 126
132 def isAssignedIn(self, items): 127 def isAssignedIn(self, items):
133 """ 128 """
134 Public method to check, if the variable is assigned to. 129 Public method to check, if the variable is assigned to.
135 130
136 @param items list of nodes to check against 131 @param items list of nodes to check against
137 @type list of ast.AST 132 @type list of ast.AST
138 @return list of nodes assigned 133 @return list of nodes assigned
139 @rtype list of ast.AST 134 @rtype list of ast.AST
140 """ 135 """
144 if newAssigned: 139 if newAssigned:
145 if isinstance(newAssigned, (list, tuple)): 140 if isinstance(newAssigned, (list, tuple)):
146 assigned.extend(newAssigned) 141 assigned.extend(newAssigned)
147 else: 142 else:
148 assigned.append(newAssigned) 143 assigned.append(newAssigned)
149 144
150 return assigned 145 return assigned
151 146
152 def isAssigned(self, node): 147 def isAssigned(self, node):
153 """ 148 """
154 Public method to check assignment against a given node. 149 Public method to check assignment against a given node.
155 150
156 @param node node to check against 151 @param node node to check against
157 @type ast.AST 152 @type ast.AST
158 @return flag indicating an assignement 153 @return flag indicating an assignement
159 @rtype bool 154 @rtype bool
160 """ 155 """
161 assigned = False 156 assigned = False
162 if ( 157 if (
163 self.__ignoreNodes and 158 self.__ignoreNodes
164 isinstance(self.__ignoreNodes, (list, tuple, object)) and 159 and isinstance(self.__ignoreNodes, (list, tuple, object))
165 isinstance(node, self.__ignoreNodes) 160 and isinstance(node, self.__ignoreNodes)
166 ): 161 ):
167 return assigned 162 return assigned
168 163
169 if isinstance(node, ast.Expr): 164 if isinstance(node, ast.Expr):
170 assigned = self.isAssigned(node.value) 165 assigned = self.isAssigned(node.value)
171 elif isinstance(node, ast.FunctionDef): 166 elif isinstance(node, ast.FunctionDef):
172 for name in node.args.args: 167 for name in node.args.args:
173 if ( 168 if isinstance(name, ast.Name) and name.id == self.var_name.id:
174 isinstance(name, ast.Name) and
175 name.id == self.var_name.id
176 ):
177 # If is param the assignations are not affected 169 # If is param the assignations are not affected
178 return assigned 170 return assigned
179 171
180 assigned = self.isAssignedIn(node.body) 172 assigned = self.isAssignedIn(node.body)
181 elif isinstance(node, ast.With): 173 elif isinstance(node, ast.With):
182 for withitem in node.items: 174 for withitem in node.items:
183 varId = getattr(withitem.optional_vars, 'id', None) 175 varId = getattr(withitem.optional_vars, "id", None)
184 assigned = ( 176 assigned = (
185 node 177 node if varId == self.__varName.id else self.isAssignedIn(node.body)
186 if varId == self.__varName.id else
187 self.isAssignedIn(node.body)
188 ) 178 )
189 elif isinstance(node, ast.Try): 179 elif isinstance(node, ast.Try):
190 assigned = [] 180 assigned = []
191 assigned.extend(self.isAssignedIn(node.body)) 181 assigned.extend(self.isAssignedIn(node.body))
192 assigned.extend(self.isAssignedIn(node.handlers)) 182 assigned.extend(self.isAssignedIn(node.handlers))
198 elif isinstance(node, (ast.If, ast.For, ast.While)): 188 elif isinstance(node, (ast.If, ast.For, ast.While)):
199 assigned = [] 189 assigned = []
200 assigned.extend(self.isAssignedIn(node.body)) 190 assigned.extend(self.isAssignedIn(node.body))
201 assigned.extend(self.isAssignedIn(node.orelse)) 191 assigned.extend(self.isAssignedIn(node.orelse))
202 elif ( 192 elif (
203 isinstance(node, ast.AugAssign) and 193 isinstance(node, ast.AugAssign)
204 isinstance(node.target, ast.Name) and 194 and isinstance(node.target, ast.Name)
205 node.target.id == self.__varName.id 195 and node.target.id == self.__varName.id
206 ): 196 ):
207 assigned = node.value 197 assigned = node.value
208 elif isinstance(node, ast.Assign) and node.targets: 198 elif isinstance(node, ast.Assign) and node.targets:
209 target = node.targets[0] 199 target = node.targets[0]
210 if isinstance(target, ast.Name): 200 if isinstance(target, ast.Name):
213 elif isinstance(target, ast.Tuple): 203 elif isinstance(target, ast.Tuple):
214 for pos, name in enumerate(target.elts): 204 for pos, name in enumerate(target.elts):
215 if name.id == self.__varName.id: 205 if name.id == self.__varName.id:
216 assigned = node.value.elts[pos] 206 assigned = node.value.elts[pos]
217 break 207 break
218 208
219 return assigned 209 return assigned
220 210
221 211
222 def evaluateVar(xssVar, parent, until, ignoreNodes=None): 212 def evaluateVar(xssVar, parent, until, ignoreNodes=None):
223 """ 213 """
224 Function to evaluate a variable node for potential XSS vulnerability. 214 Function to evaluate a variable node for potential XSS vulnerability.
225 215
226 @param xssVar variable node to be checked 216 @param xssVar variable node to be checked
227 @type ast.Name 217 @type ast.Name
228 @param parent parent node 218 @param parent parent node
229 @type ast.AST 219 @type ast.AST
230 @param until end line number to evaluate variable against 220 @param until end line number to evaluate variable against
234 @return flag indicating a secure evaluation 224 @return flag indicating a secure evaluation
235 @rtype bool 225 @rtype bool
236 """ 226 """
237 secure = False 227 secure = False
238 if isinstance(xssVar, ast.Name): 228 if isinstance(xssVar, ast.Name):
239 if ( 229 if isinstance(parent, ast.FunctionDef) and any(
240 isinstance(parent, ast.FunctionDef) and 230 name.arg == xssVar.id for name in parent.args.args
241 any(name.arg == xssVar.id for name in parent.args.args)
242 ): 231 ):
243 return False # Params are not secure 232 return False # Params are not secure
244 233
245 analyser = DeepAssignation(xssVar, ignoreNodes) 234 analyser = DeepAssignation(xssVar, ignoreNodes)
246 for node in parent.body: 235 for node in parent.body:
247 if node.lineno >= until: 236 if node.lineno >= until:
248 break 237 break
249 to = analyser.isAssigned(node) 238 to = analyser.isAssigned(node)
250 if to: 239 if to:
251 if AstUtilities.isString(to): 240 if AstUtilities.isString(to):
252 secure = True 241 secure = True
253 elif isinstance(to, ast.Name): 242 elif isinstance(to, ast.Name):
254 secure = evaluateVar( 243 secure = evaluateVar(to, parent, to.lineno, ignoreNodes)
255 to, parent, to.lineno, ignoreNodes)
256 elif isinstance(to, ast.Call): 244 elif isinstance(to, ast.Call):
257 secure = evaluateCall(to, parent, ignoreNodes) 245 secure = evaluateCall(to, parent, ignoreNodes)
258 elif isinstance(to, (list, tuple)): 246 elif isinstance(to, (list, tuple)):
259 numSecure = 0 247 numSecure = 0
260 for someTo in to: 248 for someTo in to:
261 if AstUtilities.isString(someTo): 249 if AstUtilities.isString(someTo):
262 numSecure += 1 250 numSecure += 1
263 elif isinstance(someTo, ast.Name): 251 elif isinstance(someTo, ast.Name):
264 if evaluateVar(someTo, parent, 252 if evaluateVar(someTo, parent, node.lineno, ignoreNodes):
265 node.lineno, ignoreNodes):
266 numSecure += 1 253 numSecure += 1
267 else: 254 else:
268 break 255 break
269 else: 256 else:
270 break 257 break
274 secure = False 261 secure = False
275 break 262 break
276 else: 263 else:
277 secure = False 264 secure = False
278 break 265 break
279 266
280 return secure 267 return secure
281 268
282 269
283 def evaluateCall(call, parent, ignoreNodes=None): 270 def evaluateCall(call, parent, ignoreNodes=None):
284 """ 271 """
285 Function to evaluate a call node for potential XSS vulnerability. 272 Function to evaluate a call node for potential XSS vulnerability.
286 273
287 @param call call node to be checked 274 @param call call node to be checked
288 @type ast.Call 275 @type ast.Call
289 @param parent parent node 276 @param parent parent node
290 @type ast.AST 277 @type ast.AST
291 @param ignoreNodes list of nodes to ignore 278 @param ignoreNodes list of nodes to ignore
293 @return flag indicating a secure evaluation 280 @return flag indicating a secure evaluation
294 @rtype bool 281 @rtype bool
295 """ 282 """
296 secure = False 283 secure = False
297 evaluate = False 284 evaluate = False
298 285
299 if ( 286 if (
300 isinstance(call, ast.Call) and 287 isinstance(call, ast.Call)
301 isinstance(call.func, ast.Attribute) and 288 and isinstance(call.func, ast.Attribute)
302 AstUtilities.isString(call.func.value) and 289 and AstUtilities.isString(call.func.value)
303 call.func.attr == 'format' 290 and call.func.attr == "format"
304 ): 291 ):
305 evaluate = True 292 evaluate = True
306 if call.keywords: 293 if call.keywords:
307 evaluate = False 294 evaluate = False
308 295
309 if evaluate: 296 if evaluate:
310 args = list(call.args) 297 args = list(call.args)
311 298
312 numSecure = 0 299 numSecure = 0
313 for arg in args: 300 for arg in args:
314 if AstUtilities.isString(arg): 301 if AstUtilities.isString(arg):
315 numSecure += 1 302 numSecure += 1
316 elif isinstance(arg, ast.Name): 303 elif isinstance(arg, ast.Name):
321 elif isinstance(arg, ast.Call): 308 elif isinstance(arg, ast.Call):
322 if evaluateCall(arg, parent, ignoreNodes): 309 if evaluateCall(arg, parent, ignoreNodes):
323 numSecure += 1 310 numSecure += 1
324 else: 311 else:
325 break 312 break
326 elif ( 313 elif isinstance(arg, ast.Starred) and isinstance(
327 isinstance(arg, ast.Starred) and 314 arg.value, (ast.List, ast.Tuple)
328 isinstance(arg.value, (ast.List, ast.Tuple))
329 ): 315 ):
330 args.extend(arg.value.elts) 316 args.extend(arg.value.elts)
331 numSecure += 1 317 numSecure += 1
332 else: 318 else:
333 break 319 break
334 secure = numSecure == len(args) 320 secure = numSecure == len(args)
335 321
336 return secure 322 return secure
337 323
338 324
339 def transform2call(var): 325 def transform2call(var):
340 """ 326 """
341 Function to transform a variable node to a call node. 327 Function to transform a variable node to a call node.
342 328
343 @param var variable node 329 @param var variable node
344 @type ast.BinOp 330 @type ast.BinOp
345 @return call node 331 @return call node
346 @rtype ast.Call 332 @rtype ast.Call
347 """ 333 """
354 newCall.args = [] 340 newCall.args = []
355 newCall.keywords = None 341 newCall.keywords = None
356 newCall.lineno = var.lineno 342 newCall.lineno = var.lineno
357 newCall.func = ast.Attribute() 343 newCall.func = ast.Attribute()
358 newCall.func.value = var.left 344 newCall.func.value = var.left
359 newCall.func.attr = 'format' 345 newCall.func.attr = "format"
360 if isinstance(var.right, ast.Tuple): 346 if isinstance(var.right, ast.Tuple):
361 newCall.args = var.right.elts 347 newCall.args = var.right.elts
362 else: 348 else:
363 newCall.args = [var.right] 349 newCall.args = [var.right]
364 350
365 return newCall 351 return newCall
366 352
367 return None 353 return None

eric ide

mercurial