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): |