src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Security/SecurityNodeVisitor.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9272
06ed98a19b79
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
15 15
16 class SecurityNodeVisitor: 16 class SecurityNodeVisitor:
17 """ 17 """
18 Class implementing an AST node visitor for security checks. 18 Class implementing an AST node visitor for security checks.
19 """ 19 """
20
20 def __init__(self, checker, secCheckers, filename): 21 def __init__(self, checker, secCheckers, filename):
21 """ 22 """
22 Constructor 23 Constructor
23 24
24 @param checker reference to the main security checker object 25 @param checker reference to the main security checker object
25 @type SecurityChecker 26 @type SecurityChecker
26 @param secCheckers dictionary containing the available checker routines 27 @param secCheckers dictionary containing the available checker routines
27 @type dict 28 @type dict
28 @param filename name of the checked file 29 @param filename name of the checked file
29 @type str 30 @type str
30 """ 31 """
31 self.__checker = checker 32 self.__checker = checker
32 self.__securityCheckers = secCheckers 33 self.__securityCheckers = secCheckers
33 34
34 self.seen = 0 35 self.seen = 0
35 self.depth = 0 36 self.depth = 0
36 self.filename = filename 37 self.filename = filename
37 self.imports = set() 38 self.imports = set()
38 self.import_aliases = {} 39 self.import_aliases = {}
40 # in some cases we can't determine a qualified name 41 # in some cases we can't determine a qualified name
41 try: 42 try:
42 self.namespace = SecurityUtils.getModuleQualnameFromPath(filename) 43 self.namespace = SecurityUtils.getModuleQualnameFromPath(filename)
43 except SecurityUtils.InvalidModulePath: 44 except SecurityUtils.InvalidModulePath:
44 self.namespace = "" 45 self.namespace = ""
45 46
46 def __runChecks(self, checkType): 47 def __runChecks(self, checkType):
47 """ 48 """
48 Private method to run all enabled checks for a given check type. 49 Private method to run all enabled checks for a given check type.
49 50
50 @param checkType type of checks to be run 51 @param checkType type of checks to be run
51 @type str 52 @type str
52 """ 53 """
53 if checkType in self.__securityCheckers: 54 if checkType in self.__securityCheckers:
54 for check in self.__securityCheckers[checkType]: 55 for check in self.__securityCheckers[checkType]:
55 check(self.__checker.reportError, 56 check(
56 SecurityContext(self.__context), 57 self.__checker.reportError,
57 self.__checker.getConfig()) 58 SecurityContext(self.__context),
58 59 self.__checker.getConfig(),
60 )
61
59 def visit_ClassDef(self, node): 62 def visit_ClassDef(self, node):
60 """ 63 """
61 Public method defining a visitor for AST ClassDef nodes. 64 Public method defining a visitor for AST ClassDef nodes.
62 65
63 Add class name to current namespace for all descendants. 66 Add class name to current namespace for all descendants.
64 67
65 @param node reference to the node being inspected 68 @param node reference to the node being inspected
66 @type ast.ClassDef 69 @type ast.ClassDef
67 """ 70 """
68 # For all child nodes, add this class name to current namespace 71 # For all child nodes, add this class name to current namespace
69 self.namespace = SecurityUtils.namespacePathJoin( 72 self.namespace = SecurityUtils.namespacePathJoin(self.namespace, node.name)
70 self.namespace, node.name) 73
71
72 def visit_FunctionDef(self, node): 74 def visit_FunctionDef(self, node):
73 """ 75 """
74 Public method defining a visitor for AST FunctionDef nodes. 76 Public method defining a visitor for AST FunctionDef nodes.
75 77
76 @param node reference to the node being inspected 78 @param node reference to the node being inspected
77 @type ast.FunctionDef 79 @type ast.FunctionDef
78 """ 80 """
79 self.__visitFunctionDefinition(node) 81 self.__visitFunctionDefinition(node)
80 82
81 def visit_AsyncFunctionDef(self, node): 83 def visit_AsyncFunctionDef(self, node):
82 """ 84 """
83 Public method defining a visitor for AST AsyncFunctionDef nodes. 85 Public method defining a visitor for AST AsyncFunctionDef nodes.
84 86
85 @param node reference to the node being inspected 87 @param node reference to the node being inspected
86 @type ast.AsyncFunctionDef 88 @type ast.AsyncFunctionDef
87 """ 89 """
88 self.__visitFunctionDefinition(node) 90 self.__visitFunctionDefinition(node)
89 91
90 def __visitFunctionDefinition(self, node): 92 def __visitFunctionDefinition(self, node):
91 """ 93 """
92 Private method defining a visitor for AST FunctionDef and 94 Private method defining a visitor for AST FunctionDef and
93 AsyncFunctionDef nodes. 95 AsyncFunctionDef nodes.
94 96
95 Add relevant information about the node to the context for use in tests 97 Add relevant information about the node to the context for use in tests
96 which inspect function definitions. Add the function name to the 98 which inspect function definitions. Add the function name to the
97 current namespace for all descendants. 99 current namespace for all descendants.
98 100
99 @param node reference to the node being inspected 101 @param node reference to the node being inspected
100 @type ast.FunctionDef, ast.AsyncFunctionDef 102 @type ast.FunctionDef, ast.AsyncFunctionDef
101 """ 103 """
102 self.__context['function'] = node 104 self.__context["function"] = node
103 qualname = SecurityUtils.namespacePathJoin(self.namespace, node.name) 105 qualname = SecurityUtils.namespacePathJoin(self.namespace, node.name)
104 name = qualname.split('.')[-1] 106 name = qualname.split(".")[-1]
105 self.__context['qualname'] = qualname 107 self.__context["qualname"] = qualname
106 self.__context['name'] = name 108 self.__context["name"] = name
107 109
108 # For all child nodes and any tests run, add this function name to 110 # For all child nodes and any tests run, add this function name to
109 # current namespace 111 # current namespace
110 self.namespace = SecurityUtils.namespacePathJoin( 112 self.namespace = SecurityUtils.namespacePathJoin(self.namespace, node.name)
111 self.namespace, node.name) 113
112
113 self.__runChecks("FunctionDef") 114 self.__runChecks("FunctionDef")
114 115
115 def visit_Call(self, node): 116 def visit_Call(self, node):
116 """ 117 """
117 Public method defining a visitor for AST Call nodes. 118 Public method defining a visitor for AST Call nodes.
118 119
119 Add relevant information about the node to the context for use in tests 120 Add relevant information about the node to the context for use in tests
120 which inspect function calls. 121 which inspect function calls.
121 122
122 @param node reference to the node being inspected 123 @param node reference to the node being inspected
123 @type ast.Call 124 @type ast.Call
124 """ 125 """
125 self.__context['call'] = node 126 self.__context["call"] = node
126 qualname = SecurityUtils.getCallName(node, self.import_aliases) 127 qualname = SecurityUtils.getCallName(node, self.import_aliases)
127 name = qualname.split('.')[-1] 128 name = qualname.split(".")[-1]
128 self.__context['qualname'] = qualname 129 self.__context["qualname"] = qualname
129 self.__context['name'] = name 130 self.__context["name"] = name
130 self.__runChecks("Call") 131 self.__runChecks("Call")
131 132
132 def visit_Import(self, node): 133 def visit_Import(self, node):
133 """ 134 """
134 Public method defining a visitor for AST Import nodes. 135 Public method defining a visitor for AST Import nodes.
135 136
136 @param node reference to the node being inspected 137 @param node reference to the node being inspected
137 @type ast.Import 138 @type ast.Import
138 """ 139 """
139 for nodename in node.names: 140 for nodename in node.names:
140 if nodename.asname: 141 if nodename.asname:
141 self.import_aliases[nodename.asname] = nodename.name 142 self.import_aliases[nodename.asname] = nodename.name
142 self.imports.add(nodename.name) 143 self.imports.add(nodename.name)
143 self.__context['module'] = nodename.name 144 self.__context["module"] = nodename.name
144 self.__runChecks("Import") 145 self.__runChecks("Import")
145 146
146 def visit_ImportFrom(self, node): 147 def visit_ImportFrom(self, node):
147 """ 148 """
148 Public method defining a visitor for AST Import nodes. 149 Public method defining a visitor for AST Import nodes.
149 150
150 This adds relevant information about the node to 151 This adds relevant information about the node to
151 the context for use in tests which inspect imports. 152 the context for use in tests which inspect imports.
152 153
153 @param node reference to the node being inspected 154 @param node reference to the node being inspected
154 @type ast.ImportFrom 155 @type ast.ImportFrom
155 """ 156 """
156 module = node.module 157 module = node.module
157 if module is None: 158 if module is None:
158 self.visit_Import(node) 159 self.visit_Import(node)
159 return 160 return
160 161
161 for nodename in node.names: 162 for nodename in node.names:
162 if nodename.asname: 163 if nodename.asname:
163 self.import_aliases[nodename.asname] = ( 164 self.import_aliases[nodename.asname] = module + "." + nodename.name
164 module + "." + nodename.name
165 )
166 else: 165 else:
167 # Even if import is not aliased we need an entry that maps 166 # Even if import is not aliased we need an entry that maps
168 # name to module.name. For example, with 'from a import b' 167 # name to module.name. For example, with 'from a import b'
169 # b should be aliased to the qualified name a.b 168 # b should be aliased to the qualified name a.b
170 self.import_aliases[nodename.name] = ( 169 self.import_aliases[nodename.name] = module + "." + nodename.name
171 module + '.' + nodename.name)
172 self.imports.add(module + "." + nodename.name) 170 self.imports.add(module + "." + nodename.name)
173 self.__context['module'] = module 171 self.__context["module"] = module
174 self.__context['name'] = nodename.name 172 self.__context["name"] = nodename.name
175 self.__runChecks("ImportFrom") 173 self.__runChecks("ImportFrom")
176 174
177 def visit_Constant(self, node): 175 def visit_Constant(self, node):
178 """ 176 """
179 Public method defining a visitor for Constant nodes. 177 Public method defining a visitor for Constant nodes.
180 178
181 This calls the appropriate method for the node type. 179 This calls the appropriate method for the node type.
182 It maintains compatibility with <3.6 and 3.8+ 180 It maintains compatibility with <3.6 and 3.8+
183 181
184 @param node reference to the node being inspected 182 @param node reference to the node being inspected
185 @type ast.Constant 183 @type ast.Constant
186 """ 184 """
187 if isinstance(node.value, str): 185 if isinstance(node.value, str):
188 self.visit_Str(node) 186 self.visit_Str(node)
190 self.visit_Bytes(node) 188 self.visit_Bytes(node)
191 189
192 def visit_Str(self, node): 190 def visit_Str(self, node):
193 """ 191 """
194 Public method defining a visitor for String nodes. 192 Public method defining a visitor for String nodes.
195 193
196 This adds relevant information about node to 194 This adds relevant information about node to
197 the context for use in tests which inspect strings. 195 the context for use in tests which inspect strings.
198 196
199 @param node reference to the node being inspected 197 @param node reference to the node being inspected
200 @type ast.Str 198 @type ast.Str
201 """ 199 """
202 self.__context['str'] = node.s 200 self.__context["str"] = node.s
203 if not isinstance(node._securityParent, ast.Expr): # docstring 201 if not isinstance(node._securityParent, ast.Expr): # docstring
204 self.__context['linerange'] = SecurityUtils.linerange_fix( 202 self.__context["linerange"] = SecurityUtils.linerange_fix(
205 node._securityParent 203 node._securityParent
206 ) 204 )
207 self.__runChecks("Str") 205 self.__runChecks("Str")
208 206
209 def visit_Bytes(self, node): 207 def visit_Bytes(self, node):
210 """ 208 """
211 Public method defining a visitor for Bytes nodes. 209 Public method defining a visitor for Bytes nodes.
212 210
213 This adds relevant information about node to 211 This adds relevant information about node to
214 the context for use in tests which inspect strings. 212 the context for use in tests which inspect strings.
215 213
216 @param node reference to the node being inspected 214 @param node reference to the node being inspected
217 @type ast.Bytes 215 @type ast.Bytes
218 """ 216 """
219 self.__context['bytes'] = node.s 217 self.__context["bytes"] = node.s
220 if not isinstance(node._securityParent, ast.Expr): # docstring 218 if not isinstance(node._securityParent, ast.Expr): # docstring
221 self.__context['linerange'] = SecurityUtils.linerange_fix( 219 self.__context["linerange"] = SecurityUtils.linerange_fix(
222 node._securityParent 220 node._securityParent
223 ) 221 )
224 self.__runChecks("Bytes") 222 self.__runChecks("Bytes")
225 223
226 def __preVisit(self, node): 224 def __preVisit(self, node):
227 """ 225 """
228 Private method to set up a context for the visit method. 226 Private method to set up a context for the visit method.
229 227
230 @param node node to base the context on 228 @param node node to base the context on
231 @type ast.AST 229 @type ast.AST
232 @return flag indicating to visit the node 230 @return flag indicating to visit the node
233 @rtype bool 231 @rtype bool
234 """ 232 """
235 self.__context = {} 233 self.__context = {}
236 self.__context['imports'] = self.imports 234 self.__context["imports"] = self.imports
237 self.__context['import_aliases'] = self.import_aliases 235 self.__context["import_aliases"] = self.import_aliases
238 236
239 if hasattr(node, 'lineno'): 237 if hasattr(node, "lineno"):
240 self.__context['lineno'] = node.lineno 238 self.__context["lineno"] = node.lineno
241 239
242 self.__context['node'] = node 240 self.__context["node"] = node
243 self.__context['linerange'] = SecurityUtils.linerange_fix(node) 241 self.__context["linerange"] = SecurityUtils.linerange_fix(node)
244 self.__context['filename'] = self.filename 242 self.__context["filename"] = self.filename
245 243
246 self.seen += 1 244 self.seen += 1
247 self.depth += 1 245 self.depth += 1
248 246
249 return True 247 return True
250 248
251 def visit(self, node): 249 def visit(self, node):
252 """ 250 """
253 Public method to inspected an AST node. 251 Public method to inspected an AST node.
254 252
255 @param node AST node to be inspected 253 @param node AST node to be inspected
256 @type ast.AST 254 @type ast.AST
257 """ 255 """
258 name = node.__class__.__name__ 256 name = node.__class__.__name__
259 method = 'visit_' + name 257 method = "visit_" + name
260 visitor = getattr(self, method, None) 258 visitor = getattr(self, method, None)
261 if visitor is not None: 259 if visitor is not None:
262 visitor(node) 260 visitor(node)
263 else: 261 else:
264 self.__runChecks(name) 262 self.__runChecks(name)
265 263
266 def __postVisit(self, node): 264 def __postVisit(self, node):
267 """ 265 """
268 Private method to clean up after a node was visited. 266 Private method to clean up after a node was visited.
269 267
270 @param node AST node that was visited 268 @param node AST node that was visited
271 @type ast.AST 269 @type ast.AST
272 """ 270 """
273 self.depth -= 1 271 self.depth -= 1
274 # Clean up post-recursion stuff that gets setup in the visit methods 272 # Clean up post-recursion stuff that gets setup in the visit methods
275 # for these node types. 273 # for these node types.
276 if isinstance(node, (ast.FunctionDef, ast.ClassDef)): 274 if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
277 self.namespace = SecurityUtils.namespacePathSplit( 275 self.namespace = SecurityUtils.namespacePathSplit(self.namespace)[0]
278 self.namespace)[0] 276
279
280 def generic_visit(self, node): 277 def generic_visit(self, node):
281 """ 278 """
282 Public method to drive the node visitor. 279 Public method to drive the node visitor.
283 280
284 @param node node to be inspected 281 @param node node to be inspected
285 @type ast.AST 282 @type ast.AST
286 """ 283 """
287 for _, value in ast.iter_fields(node): 284 for _, value in ast.iter_fields(node):
288 if isinstance(value, list): 285 if isinstance(value, list):

eric ide

mercurial