Mon, 08 Jun 2020 08:17:14 +0200
Code Style Checker: started to implement checker for security related issues.
# -*- coding: utf-8 -*- # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing an AST node visitor for security checks. """ import ast from . import SecurityUtils from .SecurityContext import SecurityContext class SecurityNodeVisitor(object): """ Class implementing an AST node visitor for security checks. """ def __init__(self, checker, secCheckers, filename): self.__checker = checker self.__securityCheckers = secCheckers self.seen = 0 self.depth = 0 self.filename = filename self.imports = set() self.import_aliases = {} # in some cases we can't determine a qualified name try: self.namespace = SecurityUtils.getModuleQualnameFromPath(filename) except SecurityUtils.InvalidModulePath: self.namespace = "" def __runChecks(self, checkType): """ Private method to run all enabled checks for a given check type. """ if checkType in self.__securityCheckers: for check in self.__securityCheckers[checkType]: check(self.__checker.reportError, SecurityContext(self.__context), self.__checker.getConfig()) def visit_ClassDef(self, node): """ Public method defining a visitor for AST ClassDef nodes. Add class name to current namespace for all descendants. @param node reference to the node being inspected @type ast.ClassDef """ # For all child nodes, add this class name to current namespace self.namespace = SecurityUtils.namespacePathJoin( self.namespace, node.name) def visit_FunctionDef(self, node): """ Public method defining a visitor for AST FunctionDef nodes. Add relevant information about the node to the context for use in tests which inspect function definitions. Add the function name to the current namespace for all descendants. @param node reference to the node being inspected @type ast.FunctionDef """ self.__context['function'] = node qualname = SecurityUtils.namespacePathJoin(self.namespace, node.name) name = qualname.split('.')[-1] self.__context['qualname'] = qualname self.__context['name'] = name # For all child nodes and any tests run, add this function name to # current namespace self.namespace = SecurityUtils.namespacePathJoin( self.namespace, node.name) self.__runChecks("FunctionDef") def visit_Call(self, node): """ Public method defining a visitor for AST Call nodes. Add relevant information about the node to the context for use in tests which inspect function calls. @param node reference to the node being inspected @type ast.Call """ self.__context['call'] = node qualname = SecurityUtils.getCallName(node, self.import_aliases) name = qualname.split('.')[-1] self.__context['qualname'] = qualname self.__context['name'] = name self.__runChecks("Call") def __preVisit(self, node): """ Private method to set up a context for the visit method. @param node node to base the context on @type ast.AST """ self.__context = {} self.__context['imports'] = self.imports self.__context['import_aliases'] = self.import_aliases if hasattr(node, 'lineno'): self.__context['lineno'] = node.lineno ## ## if node.lineno in self.nosec_lines: ## LOG.debug("skipped, nosec") ## self.metrics.note_nosec() ## return False self.__context['node'] = node self.__context['linerange'] = SecurityUtils.linerange_fix(node) self.__context['filename'] = self.filename self.seen += 1 self.depth += 1 return True def visit(self, node): """ Public method to inspected an AST node. @param node AST node to be inspected @type ast.AST """ name = node.__class__.__name__ method = 'visit_' + name visitor = getattr(self, method, None) if visitor is not None: visitor(node) else: self.__runChecks(name) def __postVisit(self, node): """ Private method to clean up after a node was visited. @param node AST node that was visited @type ast.AST """ self.depth -= 1 # Clean up post-recursion stuff that gets setup in the visit methods # for these node types. if isinstance(node, (ast.FunctionDef, ast.ClassDef)): self.namespace = SecurityUtils.namespacePathSplit( self.namespace)[0] def generic_visit(self, node): """ Public method to drive the node visitor. @param node node to be inspected @type ast.AST """ for _, value in ast.iter_fields(node): if isinstance(value, list): maxIndex = len(value) - 1 for index, item in enumerate(value): if isinstance(item, ast.AST): if index < maxIndex: item._securitySibling = value[index + 1] else: item._securitySibling = None item._securityParent = node if self.__preVisit(item): self.visit(item) self.generic_visit(item) self.__postVisit(item) elif isinstance(value, ast.AST): value._securitySibling = None value._securityParent = node if self.__preVisit(value): self.visit(value) self.generic_visit(value) self.__postVisit(value)