--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Security/Checks/djangoXssVulnerability.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Security/Checks/djangoXssVulnerability.py Wed Jul 13 14:55:47 2022 +0200 @@ -23,7 +23,7 @@ def getChecks(): """ Public method to get a dictionary with checks handled by this module. - + @return dictionary containing checker lists containing checker function and list of codes @rtype dict @@ -38,7 +38,7 @@ def checkDjangoXssVulnerability(reportError, context, config): """ Function to check for potential XSS vulnerability. - + @param reportError function to be used to report errors @type func @param context security context object @@ -46,13 +46,13 @@ @param config dictionary with configuration data @type dict """ - if context.isModuleImportedLike('django.utils.safestring'): + if context.isModuleImportedLike("django.utils.safestring"): affectedFunctions = [ - 'mark_safe', - 'SafeText', - 'SafeUnicode', - 'SafeString', - 'SafeBytes' + "mark_safe", + "SafeText", + "SafeUnicode", + "SafeString", + "SafeBytes", ] if context.callFunctionName in affectedFunctions: xss = context.node.args[0] @@ -63,29 +63,29 @@ def checkPotentialRisk(reportError, node): """ Function to check a given node for a potential XSS vulnerability. - + @param reportError function to be used to report errors @type func @param node node to be checked @type ast.Call """ xssVar = node.args[0] - + secure = False - + if isinstance(xssVar, ast.Name): # Check if the var are secure parent = node._securityParent while not isinstance(parent, (ast.Module, ast.FunctionDef)): parent = parent._securityParent - + isParam = False if isinstance(parent, ast.FunctionDef): for name in parent.args.args: if name.arg == xssVar.id: isParam = True break - + if not isParam: secure = evaluateVar(xssVar, parent, node.lineno) elif isinstance(xssVar, ast.Call): @@ -102,25 +102,20 @@ parent = parent._securityParent newCall = transform2call(xssVar) secure = evaluateCall(newCall, parent) - + if not secure: - reportError( - node.lineno - 1, - node.col_offset, - "S703", - "M", - "H" - ) + reportError(node.lineno - 1, node.col_offset, "S703", "M", "H") class DeepAssignation: """ Class to perform a deep analysis of an assign. """ + def __init__(self, varName, ignoreNodes=None): """ Constructor - + @param varName name of the variable @type str @param ignoreNodes list of nodes to ignore @@ -128,11 +123,11 @@ """ self.__varName = varName self.__ignoreNodes = ignoreNodes - + def isAssignedIn(self, items): """ Public method to check, if the variable is assigned to. - + @param items list of nodes to check against @type list of ast.AST @return list of nodes assigned @@ -146,13 +141,13 @@ assigned.extend(newAssigned) else: assigned.append(newAssigned) - + return assigned - + def isAssigned(self, node): """ Public method to check assignment against a given node. - + @param node node to check against @type ast.AST @return flag indicating an assignement @@ -160,31 +155,26 @@ """ assigned = False if ( - self.__ignoreNodes and - isinstance(self.__ignoreNodes, (list, tuple, object)) and - isinstance(node, self.__ignoreNodes) + self.__ignoreNodes + and isinstance(self.__ignoreNodes, (list, tuple, object)) + and isinstance(node, self.__ignoreNodes) ): return assigned - + if isinstance(node, ast.Expr): assigned = self.isAssigned(node.value) elif isinstance(node, ast.FunctionDef): for name in node.args.args: - if ( - isinstance(name, ast.Name) and - name.id == self.var_name.id - ): + if isinstance(name, ast.Name) and name.id == self.var_name.id: # If is param the assignations are not affected return assigned - + assigned = self.isAssignedIn(node.body) elif isinstance(node, ast.With): for withitem in node.items: - varId = getattr(withitem.optional_vars, 'id', None) + varId = getattr(withitem.optional_vars, "id", None) assigned = ( - node - if varId == self.__varName.id else - self.isAssignedIn(node.body) + node if varId == self.__varName.id else self.isAssignedIn(node.body) ) elif isinstance(node, ast.Try): assigned = [] @@ -200,9 +190,9 @@ assigned.extend(self.isAssignedIn(node.body)) assigned.extend(self.isAssignedIn(node.orelse)) elif ( - isinstance(node, ast.AugAssign) and - isinstance(node.target, ast.Name) and - node.target.id == self.__varName.id + isinstance(node, ast.AugAssign) + and isinstance(node.target, ast.Name) + and node.target.id == self.__varName.id ): assigned = node.value elif isinstance(node, ast.Assign) and node.targets: @@ -215,14 +205,14 @@ if name.id == self.__varName.id: assigned = node.value.elts[pos] break - + return assigned def evaluateVar(xssVar, parent, until, ignoreNodes=None): """ Function to evaluate a variable node for potential XSS vulnerability. - + @param xssVar variable node to be checked @type ast.Name @param parent parent node @@ -236,12 +226,11 @@ """ secure = False if isinstance(xssVar, ast.Name): - if ( - isinstance(parent, ast.FunctionDef) and - any(name.arg == xssVar.id for name in parent.args.args) + if isinstance(parent, ast.FunctionDef) and any( + name.arg == xssVar.id for name in parent.args.args ): return False # Params are not secure - + analyser = DeepAssignation(xssVar, ignoreNodes) for node in parent.body: if node.lineno >= until: @@ -251,8 +240,7 @@ if AstUtilities.isString(to): secure = True elif isinstance(to, ast.Name): - secure = evaluateVar( - to, parent, to.lineno, ignoreNodes) + secure = evaluateVar(to, parent, to.lineno, ignoreNodes) elif isinstance(to, ast.Call): secure = evaluateCall(to, parent, ignoreNodes) elif isinstance(to, (list, tuple)): @@ -261,8 +249,7 @@ if AstUtilities.isString(someTo): numSecure += 1 elif isinstance(someTo, ast.Name): - if evaluateVar(someTo, parent, - node.lineno, ignoreNodes): + if evaluateVar(someTo, parent, node.lineno, ignoreNodes): numSecure += 1 else: break @@ -276,14 +263,14 @@ else: secure = False break - + return secure def evaluateCall(call, parent, ignoreNodes=None): """ Function to evaluate a call node for potential XSS vulnerability. - + @param call call node to be checked @type ast.Call @param parent parent node @@ -295,20 +282,20 @@ """ secure = False evaluate = False - + if ( - isinstance(call, ast.Call) and - isinstance(call.func, ast.Attribute) and - AstUtilities.isString(call.func.value) and - call.func.attr == 'format' + isinstance(call, ast.Call) + and isinstance(call.func, ast.Attribute) + and AstUtilities.isString(call.func.value) + and call.func.attr == "format" ): evaluate = True if call.keywords: evaluate = False - + if evaluate: args = list(call.args) - + numSecure = 0 for arg in args: if AstUtilities.isString(arg): @@ -323,23 +310,22 @@ numSecure += 1 else: break - elif ( - isinstance(arg, ast.Starred) and - isinstance(arg.value, (ast.List, ast.Tuple)) + elif isinstance(arg, ast.Starred) and isinstance( + arg.value, (ast.List, ast.Tuple) ): args.extend(arg.value.elts) numSecure += 1 else: break secure = numSecure == len(args) - + return secure def transform2call(var): """ Function to transform a variable node to a call node. - + @param var variable node @type ast.BinOp @return call node @@ -356,12 +342,12 @@ newCall.lineno = var.lineno newCall.func = ast.Attribute() newCall.func.value = var.left - newCall.func.attr = 'format' + newCall.func.attr = "format" if isinstance(var.right, ast.Tuple): newCall.args = var.right.elts else: newCall.args = [var.right] - + return newCall - + return None