eric6/Plugins/CheckerPlugins/CodeStyleChecker/Security/SecurityNodeVisitor.py

changeset 7612
ca1ce1e0fcff
child 7613
382f89c11e27
equal deleted inserted replaced
7611:d546c4e72f52 7612:ca1ce1e0fcff
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an AST node visitor for security checks.
8 """
9
10 import ast
11
12 from . import SecurityUtils
13 from .SecurityContext import SecurityContext
14
15
16 class SecurityNodeVisitor(object):
17 """
18 Class implementing an AST node visitor for security checks.
19 """
20 def __init__(self, checker, secCheckers, filename):
21 self.__checker = checker
22 self.__securityCheckers = secCheckers
23
24 self.seen = 0
25 self.depth = 0
26 self.filename = filename
27 self.imports = set()
28 self.import_aliases = {}
29
30 # in some cases we can't determine a qualified name
31 try:
32 self.namespace = SecurityUtils.getModuleQualnameFromPath(filename)
33 except SecurityUtils.InvalidModulePath:
34 self.namespace = ""
35
36 def __runChecks(self, checkType):
37 """
38 Private method to run all enabled checks for a given check type.
39 """
40 if checkType in self.__securityCheckers:
41 for check in self.__securityCheckers[checkType]:
42 check(self.__checker.reportError,
43 SecurityContext(self.__context),
44 self.__checker.getConfig())
45
46 def visit_ClassDef(self, node):
47 """
48 Public method defining a visitor for AST ClassDef nodes.
49
50 Add class name to current namespace for all descendants.
51
52 @param node reference to the node being inspected
53 @type ast.ClassDef
54 """
55 # For all child nodes, add this class name to current namespace
56 self.namespace = SecurityUtils.namespacePathJoin(
57 self.namespace, node.name)
58
59 def visit_FunctionDef(self, node):
60 """
61 Public method defining a visitor for AST FunctionDef nodes.
62
63 Add relevant information about the node to the context for use in tests
64 which inspect function definitions. Add the function name to the
65 current namespace for all descendants.
66
67 @param node reference to the node being inspected
68 @type ast.FunctionDef
69 """
70 self.__context['function'] = node
71 qualname = SecurityUtils.namespacePathJoin(self.namespace, node.name)
72 name = qualname.split('.')[-1]
73 self.__context['qualname'] = qualname
74 self.__context['name'] = name
75
76 # For all child nodes and any tests run, add this function name to
77 # current namespace
78 self.namespace = SecurityUtils.namespacePathJoin(
79 self.namespace, node.name)
80
81 self.__runChecks("FunctionDef")
82
83 def visit_Call(self, node):
84 """
85 Public method defining a visitor for AST Call nodes.
86
87 Add relevant information about the node to the context for use in tests
88 which inspect function calls.
89
90 @param node reference to the node being inspected
91 @type ast.Call
92 """
93 self.__context['call'] = node
94 qualname = SecurityUtils.getCallName(node, self.import_aliases)
95 name = qualname.split('.')[-1]
96 self.__context['qualname'] = qualname
97 self.__context['name'] = name
98 self.__runChecks("Call")
99
100 def __preVisit(self, node):
101 """
102 Private method to set up a context for the visit method.
103
104 @param node node to base the context on
105 @type ast.AST
106 """
107 self.__context = {}
108 self.__context['imports'] = self.imports
109 self.__context['import_aliases'] = self.import_aliases
110
111 if hasattr(node, 'lineno'):
112 self.__context['lineno'] = node.lineno
113 ##
114 ## if node.lineno in self.nosec_lines:
115 ## LOG.debug("skipped, nosec")
116 ## self.metrics.note_nosec()
117 ## return False
118
119 self.__context['node'] = node
120 self.__context['linerange'] = SecurityUtils.linerange_fix(node)
121 self.__context['filename'] = self.filename
122
123 self.seen += 1
124 self.depth += 1
125
126 return True
127
128 def visit(self, node):
129 """
130 Public method to inspected an AST node.
131
132 @param node AST node to be inspected
133 @type ast.AST
134 """
135 name = node.__class__.__name__
136 method = 'visit_' + name
137 visitor = getattr(self, method, None)
138 if visitor is not None:
139 visitor(node)
140 else:
141 self.__runChecks(name)
142
143 def __postVisit(self, node):
144 """
145 Private method to clean up after a node was visited.
146
147 @param node AST node that was visited
148 @type ast.AST
149 """
150 self.depth -= 1
151 # Clean up post-recursion stuff that gets setup in the visit methods
152 # for these node types.
153 if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
154 self.namespace = SecurityUtils.namespacePathSplit(
155 self.namespace)[0]
156
157 def generic_visit(self, node):
158 """
159 Public method to drive the node visitor.
160
161 @param node node to be inspected
162 @type ast.AST
163 """
164 for _, value in ast.iter_fields(node):
165 if isinstance(value, list):
166 maxIndex = len(value) - 1
167 for index, item in enumerate(value):
168 if isinstance(item, ast.AST):
169 if index < maxIndex:
170 item._securitySibling = value[index + 1]
171 else:
172 item._securitySibling = None
173 item._securityParent = node
174
175 if self.__preVisit(item):
176 self.visit(item)
177 self.generic_visit(item)
178 self.__postVisit(item)
179
180 elif isinstance(value, ast.AST):
181 value._securitySibling = None
182 value._securityParent = node
183 if self.__preVisit(value):
184 self.visit(value)
185 self.generic_visit(value)
186 self.__postVisit(value)

eric ide

mercurial