def visit(self, node, state): currscope = self.locate_scope(node, state) varnode = node.node var_name = varnode.name # Store object properties (vars, class variables) as $this->property # otherwise $a->var and $b->var overwrite each other (both stored as $var). if type(node.node) is phpast.ObjectProperty: var_name = node.node.node.name + "->" + varnode.name # Create var newobj = VariableDef(var_name, varnode.lineno, currscope, ast_node=node.expr) node._obj = newobj currscope.add_var(newobj) # New object property? Also add var to parent scope (if not exist) if type(node.node) is phpast.ObjectProperty: root_scope = currscope.get_root_scope()._parent_scope if not root_scope.get_var(var_name): # create object var property = VariableDef(var_name, varnode.lineno, currscope, ast_node=node.expr) property.parents = [newobj] root_scope.add_var(property) # Overwrite object property if type(node.node) is phpast.ObjectProperty: # link this var to object property root_scope = currscope.get_root_scope() if type(root_scope._ast_node) is phpast.Method: # link this var to property root_scope._parent_scope.get_var(var_name).parents = [newobj] # Object creation if type(node.expr) is phpast.New and node.expr.name in state.classes: # Start ast travel class Node class_node = state.classes[node.expr.name] class_node._object = True class_node._object_var = newobj # pass this var to object class_node.accept(self._main_visitor_method) return newobj, False
def visit(self, node, state): stoponthis = False newobj = None # global parent scope parentscope = self.locate_scope(node, state) if not getattr(node, '_object', False): # Start traveling node when instance is created node._parent_scope = parentscope state.classes[node.name] = node stoponthis = True else: # new instance has been created # Create new Scope and push it onto the stack newscope = Scope(node, parent_scope=parentscope, is_root=True) # add builtins to scope newscope._builtins = dict( ((uv, VariableDef(uv, -1, newscope)) for uv in VariableDef.USER_VARS)) state.scopes.append(newscope) newobj = Obj(node.name, node.lineno, newscope, node._object_var, ast_node=node) # create $this var for internal method calling this_var = VariableDef('$this', node.lineno, newscope) this_var._obj_def = newobj newscope.add_var(this_var) # add ObjDef to VarDef, this way we can trace method call back to the correct instance node._object_var._obj_def = newobj node._scope = newscope state.objects[node._object_var.name] = node._object_var return newobj, stoponthis
def _parse_me(self, node, scope): ''' TODO: add method call Traverse this AST subtree until either a Variable or FunctionCall node is found... ''' for node in NodeRep.parse(node): if type(node) is phpast.BinaryOp: # only parse direct nodes for item in NodeRep.parse(node, 0, 0, 1): self._parse_me(item, scope) break if type(node) is phpast.Variable: # object properties are stored as $this->property varname = node.name if type(node._parent_node) is phpast.ObjectProperty: varname = node._parent_node.node.name + '->' + node._parent_node.name vardef = VariableDef(varname + '__$temp_anon_var$_', node.lineno, scope) vardef.var_nodes = [node] # anon var is not stored in scope vardef._anon_var = True # get and set parent scopevar = scope.get_var(varname) vardef.add_parent(scopevar) # add Param to VarDef # TODO: not really necessary? vardef._parent_obj = self # add var to current scope scope.add_var(vardef) self.vars.append(vardef) break elif type(node) is phpast.FunctionCall: # TEST call functioncal visitor from core.visitors.base_visitor import BaseVisitor from core.visitors.function_call_visitor import FunctionCallVisitor visitor = FunctionCallVisitor(BaseVisitor); fc, stoponthis = visitor.visit(node, scope.get_state()) vardef = VariableDef(node.name + '_funvar', node.lineno, scope) # from core.nodes.function_call import FuncCall # from core.nodes.function_call import FuncCall # fc = FuncCall(node.name, node.lineno, node, scope, self) #TODO: Can we do this in a more extensible way? Why different ways for handling FILE_DISC / XSS? # Add vulntrace vulntype = fc.is_vulnerable_for() if vulntype and 'FILE_DISCLOSURE' in vulntype and \ self._parent_obj.name in SENSITIVE_FUNCTIONS['XSS']: # Add vulntrace to parent call with pending trace fc.add_vulntrace(vulntype) self._parent_obj._pending_trace = fc.get_vulntraces()[-1] vardef._safe_for.append('XSS') vardef.set_clean() # elif vulntype: # fc.add_vulntrace(vulntype) # # Keep track of thin funccal # self.get_root_obj()._functions.append(fc) # vardef.set_clean() # return values (custom function)? called_obj = fc.get_called_obj(); if called_obj: # Set function scope as active code called_obj._scope._dead_code = False for var in called_obj._return_vars: vardef.add_parent(var) # else: for param in fc.params: for var in param.vars: vardef.add_parent(var) # Securing function? vulnty = get_vulnty_for_sec(fc.name) if vulnty: vardef._safe_for.append(vulnty) self.vars.append(vardef) break