def _define_obj(traverser, node): "Creates a local context object" var = JSObject() for prop in node["properties"]: if prop["type"] == "PrototypeMutation": var_name = "prototype" else: key = prop["key"] if key["type"] == "Literal": var_name = key["value"] elif isinstance(key["name"], basestring): var_name = key["name"] else: if "property" in key["name"]: name = key["name"] else: name = {"property": key["name"]} var_name = _get_member_exp_property(traverser, name) var_value = traverser._traverse_node(prop["value"]) var.set(var_name, var_value, traverser) # TODO: Observe "kind" if not isinstance(var, JSWrapper): return JSWrapper(var, lazy=True, traverser=traverser) var.lazy = True return var
def wrap(): traverser.function_collection.append([]) traverser._debug("THIS_PUSH") # Allow references to "this" traverser.this_stack.append(JSObject(traverser=traverser)) # inherit contexts from the current scope traverser.contexts = current_contexts params = {} for param in node["params"]: if param["type"] == "Identifier": params[param["name"]] = lambda: JSObject(traverser=traverser) else: # TODO: Support array and object destructuring. pass context = JSContext(data=params, traverser=traverser) traverser.contexts.append(context) traverser.traverse_node(node["body"]) # Call all of the function collection's members to traverse all of the # child functions. func_coll = traverser.function_collection.pop() for func in func_coll: func() traverser.contexts.pop() # Since we need to manually manage the "this" stack, pop off that # context. traverser._debug("THIS_POP") traverser.this_stack.pop()
def _define_obj(traverser, node): 'Creates a local context object' var = JSObject() for prop in node['properties']: if prop['type'] == 'PrototypeMutation': var_name = 'prototype' else: key = prop['key'] if key['type'] == 'Literal': var_name = key['value'] elif isinstance(key['name'], basestring): var_name = key['name'] else: if 'property' in key['name']: name = key['name'] else: name = {'property': key['name']} var_name = _get_member_exp_property(traverser, name) var_value = traverser._traverse_node(prop['value']) var.set(var_name, var_value, traverser) # TODO: Observe "kind" if not isinstance(var, JSWrapper): return JSWrapper(var, lazy=True, traverser=traverser) var.lazy = True return var
def ObjectExpression(traverser, node): var = JSObject(traverser=traverser) for prop in node["properties"]: key = prop["key"] var.set(key["value" if key["type"] == "Literal" else "name"], traverser.traverse_node(prop["value"]), traverser=traverser, ignore_setters=True) # TODO: Observe "kind" return var
def _function(traverser, node): """ A helper function that traverses and instantiates function declarations and function expressions. """ current_contexts = traverser.contexts[:] def wrap(): traverser.function_collection.append([]) traverser._debug("THIS_PUSH") # Allow references to "this" traverser.this_stack.append(JSObject(traverser=traverser)) # inherit contexts from the current scope traverser.contexts = current_contexts params = {} for param in node["params"]: if param["type"] == "Identifier": params[param["name"]] = lambda: JSObject(traverser=traverser) else: # TODO: Support array and object destructuring. pass context = JSContext(data=params, traverser=traverser) traverser.contexts.append(context) traverser.traverse_node(node["body"]) # Call all of the function collection's members to traverse all of the # child functions. func_coll = traverser.function_collection.pop() for func in func_coll: func() traverser.contexts.pop() # Since we need to manually manage the "this" stack, pop off that # context. traverser._debug("THIS_POP") traverser.this_stack.pop() # Put the function off for traversal at the end of the current block scope. if traverser.function_collection: traverser.function_collection[-1].append(wrap) else: wrap(traverser, node) output = JSObject(traverser=traverser, callable_=True) output.TYPEOF = "function" return output
def MemberExpression(traverser, node, instantiate=False): "Traces a MemberExpression and returns the appropriate object" traverser._debug("TESTING>>%s" % node["type"]) if node["type"] == "MemberExpression": # x.y or x[y] # x = base base = MemberExpression(traverser, node["object"], instantiate) identifier = _get_member_exp_property(traverser, node) traverser._debug("MEMBER_EXP>>PROPERTY (%s)" % identifier) return base.get(traverser, identifier, instantiate=instantiate) elif node["type"] == "Identifier": traverser._debug("MEMBER_EXP>>ROOT:IDENTIFIER (%s)" % node["name"]) # If we're supposed to instantiate the object and it doesn't already # exist, instantitate the object. if instantiate and not traverser._is_defined(node["name"]): output = JSObject(traverser=traverser) traverser.contexts[0].set(node["name"], output) else: output = traverser._seek_variable(node["name"]) return output else: traverser._debug("MEMBER_EXP>>ROOT:EXPRESSION") # It's an expression, so just try your damndest. return traverser.traverse_node(node)
def CallExpression(traverser, node): args = [traverser.traverse_node(a) for a in node["arguments"]] member = traverser.traverse_node(node["callee"]) if (node["callee"]["type"] == "MemberExpression" and node["callee"]["property"]["type"] == "Identifier"): # If we can identify the function being called on any member of any # instance, we can use that to either generate an output value or test # for additional conditions. identifier_name = node["callee"]["property"]["name"] if identifier_name in instanceactions.INSTANCE_DEFINITIONS: traverser._debug('Calling instance action...') result = instanceactions.INSTANCE_DEFINITIONS[identifier_name]( args, traverser, traverser.traverse_node(node["callee"]["object"])) if result is not None: return result if isinstance(member, JSGlobal) and "return" in member.global_data: traverser._debug("EVALUATING RETURN...") output = member.global_data["return"](wrapper=member, arguments=args, traverser=traverser) if output is not None: return output return JSObject(traverser=traverser)
def set_lvalue(value): node_left = node["left"] traverser._debug("ASSIGNING:DIRECT(%s)" % node_left["type"]) global_overwrite = False readonly_value = True if node_left["type"] == "Identifier": # Identifiers just need the ID name and a value to push. # Raise a global overwrite issue if the identifier is global. global_overwrite = traverser._is_global(node_left["name"]) # Get the readonly attribute and store its value if is_global if global_overwrite: from predefinedentities import GLOBAL_ENTITIES global_dict = GLOBAL_ENTITIES[node_left["name"]] readonly_value = global_dict.get("readonly", False) traverser._declare_variable(node_left["name"], value, type_="glob") elif node_left["type"] == "MemberExpression": member_object = MemberExpression(traverser, node_left["object"], instantiate=True) member_property = _get_member_exp_property(traverser, node_left) traverser._debug("ASSIGNMENT:MEMBER_PROPERTY(%s)" % member_property) if member_object is None: member_object = JSObject() member_object.set(member_property, value, traverser)
def wrap(traverser, node): me = JSObject() traverser.function_collection.append([]) # Replace the current context with a prototypeable JS object. traverser._pop_context() me.type_ = "default" # Treat the function as a normal object. traverser._push_context(me) traverser._debug("THIS_PUSH") traverser.this_stack.append(me) # Allow references to "this" # Declare parameters in the local scope params = [] for param in node["params"]: if param["type"] == "Identifier": params.append(param["name"]) elif param["type"] == "ArrayPattern": for element in param["elements"]: # Array destructuring in function prototypes? LOL! if element is None or element["type"] != "Identifier": continue params.append(element["name"]) local_context = traverser._peek_context(1) for param in params: var = JSWrapper(lazy=True, traverser=traverser) # We can assume that the params are static because we don't care # about what calls the function. We want to know whether the # function solely returns static values. If so, it is a static # function. local_context.set(param, var) traverser._traverse_node(node["body"]) # Since we need to manually manage the "this" stack, pop off that # context. traverser._debug("THIS_POP") traverser.this_stack.pop() # Call all of the function collection's members to traverse all of the # child functions. func_coll = traverser.function_collection.pop() for func in func_coll: func()
def wrap(traverser, node): me = JSObject() traverser.function_collection.append([]) # Replace the current context with a prototypeable JS object. traverser._pop_context() me.type_ = 'default' # Treat the function as a normal object. traverser._push_context(me) traverser._debug('THIS_PUSH') traverser.this_stack.append(me) # Allow references to "this" # Declare parameters in the local scope params = [] for param in node['params']: if param['type'] == 'Identifier': params.append(param['name']) elif param['type'] == 'ArrayPattern': for element in param['elements']: # Array destructuring in function prototypes? LOL! if element is None or element['type'] != 'Identifier': continue params.append(element['name']) local_context = traverser._peek_context(1) for param in params: var = JSWrapper(lazy=True, traverser=traverser) # We can assume that the params are static because we don't care # about what calls the function. We want to know whether the # function solely returns static values. If so, it is a static # function. local_context.set(param, var) traverser._traverse_node(node['body']) # Since we need to manually manage the "this" stack, pop off that # context. traverser._debug('THIS_POP') traverser.this_stack.pop() # Call all of the function collection's members to traverse all of the # child functions. func_coll = traverser.function_collection.pop() for func in func_coll: func()
def Literal(traverser, node): """ Convert a literal node in the parse tree to its corresponding interpreted value. """ value = node["value"] if isinstance(value, dict): return JSObject(traverser=traverser) return JSLiteral(value, traverser=traverser)
def _ident(traverser, node): 'Initiates an object lookup on the traverser based on an identifier token' name = node['name'] # Ban bits like "newThread" test_identifier(traverser, name) if traverser._is_defined(name): return traverser._seek_variable(name) return JSWrapper(JSObject(), traverser=traverser, dirty=True)
def nsIJSON_deprec(wrapper, arguments, traverser): """Throw a compatibility error about removed XPCOM methods.""" traverser.notice( err_id=('testcases_javascript_calldefinitions', 'nsIJSON', 'deprec'), notice='Deprecated nsIJSON methods in use.', description=('The `encode` and `decode` methods in nsIJSON have been ' 'deprecated in Gecko 7. You can use the methods in the ' 'global JSON object instead. See %s for more ' 'information.') % 'https://developer.mozilla.org/En/Using_native_JSON') return JSWrapper(JSObject(), traverser=traverser, dirty=True)
def ObjectExpression(traverser, node): var = JSObject(traverser=traverser) for prop in node["properties"]: if prop["type"] == "PrototypeMutation": var_name = "prototype" else: key = prop["key"] if key["type"] == "Literal": var_name = key["value"] elif isinstance(key["name"], basestring): var_name = key["name"] else: if "property" in key["name"]: name = key["name"] else: name = {"property": key["name"]} var_name = _get_member_exp_property(traverser, name) var_value = traverser.traverse_node(prop["value"]) var.set(var_name, var_value, traverser) return var
def _define_literal(traverser, node): """ Convert a literal node in the parse tree to its corresponding interpreted value. """ value = node['value'] if isinstance(value, dict): return JSWrapper(JSObject(), traverser=traverser, dirty=True) wrapper = JSWrapper(value if value is not None else JSLiteral(None), traverser=traverser) test_literal(traverser, wrapper) return wrapper
def js_unwrap(wrapper, arguments, traverser): """Return the unwrapped variant of an unwrapped JSObject.""" if not arguments: traverser._debug('UNWRAP:NO ARGS') return traverser._debug('UNWRAPPING OBJECT') obj = traverser._traverse_node(arguments[0]) if obj.value is None: traverser._debug('UNWRAPPING OBJECT>>NOTHING TO UNWRAP') return JSWrapper(JSObject(unwrapped=True), traverser=traverser) if obj.is_global: obj.value['is_unwrapped'] = True else: obj.value.is_unwrapped = True return obj
def UnaryExpression(traverser, node): operator = node["operator"] arg = traverser.traverse_node(node["argument"]) if (isinstance(arg, JSGlobal) and "literal" in arg.global_data and not (operator == "typeof" and "undefined" in arg.global_data)): arg = JSLiteral(utils.evaluate_lambdas(traverser, arg.global_data["literal"]), traverser=traverser) if operator in UNARY_OPERATORS: traverser._debug("Defined unary operator (%s)" % operator) return JSLiteral(UNARY_OPERATORS[node["operator"]](arg, traverser), traverser=traverser) elif operator == "void": traverser._debug("Void unary operator") from predefinedentities import resolve_entity return JSGlobal(resolve_entity(traverser, "undefined"), traverser=traverser) elif operator == "typeof": traverser._debug("Typeof unary operator") return JSLiteral(_expr_unary_typeof(arg)) else: traverser._debug("Undefined unary operator") return JSObject(traverser=traverser)
def js_wrap(wrapper, arguments, traverser): """Return the wrapped variant of an unwrapped JSObject.""" if not arguments: traverser._debug('WRAP:NO ARGS') return traverser._debug('WRAPPING OBJECT') obj = traverser._traverse_node(arguments[0]) if obj.value is None: traverser._debug('WRAPPING OBJECT>>NOTHING TO WRAP') return JSWrapper(JSObject(), traverser=traverser) if len(arguments) > 1: traverser.warning( err_id=('testcases_js_xpcom', 'xpcnativewrapper', 'shallow'), warning='Shallow XPCOM wrappers should not be used', description='Shallow XPCOM wrappers are seldom necessary and ' 'should not be used. Please use deep wrappers ' 'instead.', signing_help='Extensions making use of shallow wrappers will not ' 'be accepted for automated signing. Please remove ' 'the second and subsequent arguments of any calls ' 'to `XPCNativeWrapper`, as well as any code which ' 'applies `XPCNativeWrapper` to properties obtained ' 'from these shallowly wrapped objects.', signing_severity='high') # Do not mark shallow wrappers as not unwrapped. return obj if obj.is_global: # Why are we changing the original object? XPCNativeWrapper # does not alter its arguments. obj.value['is_unwrapped'] = False else: obj.value.is_unwrapped = False return obj
def _expr_assignment(traverser, node): """Evaluate an AssignmentExpression node.""" traverser._debug('ASSIGNMENT_EXPRESSION') traverser.debug_level += 1 traverser._debug('ASSIGNMENT>>PARSING RIGHT') right = traverser._traverse_node(node['right']) right = JSWrapper(right, traverser=traverser) # Treat direct assignment different than augmented assignment. if node['operator'] == '=': from predefinedentities import GLOBAL_ENTITIES, is_shared_scope global_overwrite = False readonly_value = is_shared_scope(traverser) node_left = node['left'] traverser._debug('ASSIGNMENT:DIRECT(%s)' % node_left['type']) if node_left['type'] == 'Identifier': # Identifiers just need the ID name and a value to push. # Raise a global overwrite issue if the identifier is global. global_overwrite = traverser._is_global(node_left['name']) # Get the readonly attribute and store its value if is_global if global_overwrite: global_dict = GLOBAL_ENTITIES[node_left['name']] if 'readonly' in global_dict: readonly_value = global_dict['readonly'] traverser._declare_variable(node_left['name'], right, type_='glob') elif node_left['type'] == 'MemberExpression': member_object = trace_member(traverser, node_left['object'], instantiate=True) global_overwrite = (member_object.is_global and not ('overwritable' in member_object.value and member_object.value['overwritable'])) member_property = _get_member_exp_property(traverser, node_left) traverser._debug('ASSIGNMENT:MEMBER_PROPERTY(%s)' % member_property) traverser._debug('ASSIGNMENT:GLOB_OV::%s' % global_overwrite) # Don't do the assignment if we're facing a global. if not member_object.is_global: if member_object.value is None: member_object.value = JSObject() if not member_object.is_global: member_object.value.set(member_property, right, traverser) else: # It's probably better to do nothing. pass elif 'value' in member_object.value: member_object_value = _expand_globals(traverser, member_object).value if member_property in member_object_value['value']: # If it's a global and the actual member exists, test # whether it can be safely overwritten. member = member_object_value['value'][member_property] if 'readonly' in member: global_overwrite = True readonly_value = member['readonly'] traverser._debug('ASSIGNMENT:DIRECT:GLOB_OVERWRITE %s' % global_overwrite) traverser._debug('ASSIGNMENT:DIRECT:READONLY %r' % readonly_value) if callable(readonly_value): readonly_value = readonly_value(traverser, right, node['right']) if readonly_value and global_overwrite: kwargs = dict( err_id=('testcases_javascript_actions', '_expr_assignment', 'global_overwrite'), warning='Global variable overwrite', description='An attempt was made to overwrite a global ' 'variable in some JavaScript code.') if isinstance(readonly_value, DESCRIPTION_TYPES): kwargs['description'] = readonly_value elif isinstance(readonly_value, dict): kwargs.update(readonly_value) traverser.warning(**kwargs) return right lit_right = right.get_literal_value() traverser._debug('ASSIGNMENT>>PARSING LEFT') left = traverser._traverse_node(node['left']) traverser._debug('ASSIGNMENT>>DONE PARSING LEFT') traverser.debug_level -= 1 if isinstance(left, JSWrapper): if left.dirty: return left lit_left = left.get_literal_value() token = node['operator'] # Don't perform an operation on None. Python freaks out if lit_left is None: lit_left = 0 if lit_right is None: lit_right = 0 # Give them default values so we have them in scope. gleft, gright = 0, 0 # All of the assignment operators operators = { '=': lambda: right, '+=': lambda: lit_left + lit_right, '-=': lambda: gleft - gright, '*=': lambda: gleft * gright, '/=': lambda: 0 if gright == 0 else (gleft / gright), '%=': lambda: 0 if gright == 0 else (gleft % gright), '<<=': lambda: int(gleft) << int(gright), '>>=': lambda: int(gleft) >> int(gright), '>>>=': lambda: float(abs(int(gleft)) >> gright), '|=': lambda: int(gleft) | int(gright), '^=': lambda: int(gleft) ^ int(gright), '&=': lambda: int(gleft) & int(gright) } # If we're modifying a non-numeric type with a numeric operator, return # NaN. if (not isinstance(lit_left, NUMERIC_TYPES) and token in NUMERIC_OPERATORS): left.set_value(get_NaN(traverser), traverser=traverser) return left # If either side of the assignment operator is a string, both sides # need to be casted to strings first. if (isinstance(lit_left, types.StringTypes) or isinstance(lit_right, types.StringTypes)): lit_left = _get_as_str(lit_left) lit_right = _get_as_str(lit_right) gleft, gright = _get_as_num(left), _get_as_num(right) traverser._debug('ASSIGNMENT>>OPERATION:%s' % token) if token not in operators: # We don't support that operator. (yet?) traverser._debug('ASSIGNMENT>>OPERATOR NOT FOUND', 1) return left elif token in ('<<=', '>>=', '>>>=') and gright < 0: # The user is doing weird bitshifting that will return 0 in JS but # not in Python. left.set_value(0, traverser=traverser) return left elif (token in ('<<=', '>>=', '>>>=', '|=', '^=', '&=') and (abs(gleft) == float('inf') or abs(gright) == float('inf'))): # Don't bother handling infinity for integer-converted operations. left.set_value(get_NaN(traverser), traverser=traverser) return left traverser._debug( 'ASSIGNMENT::L-value global? (%s)' % ('Y' if left.is_global else 'N'), 1) try: new_value = operators[token]() except Exception: traverser.system_error(exc_info=sys.exc_info()) new_value = None # Cap the length of analyzed strings. if (isinstance(new_value, types.StringTypes) and len(new_value) > MAX_STR_SIZE): new_value = new_value[:MAX_STR_SIZE] traverser._debug('ASSIGNMENT::New value >> %s' % new_value, 1) left.set_value(new_value, traverser=traverser) return left # Though it would otherwise be a syntax error, we say that 4=5 should # evaluate out to 5. return right
def trace_member(traverser, node, instantiate=False): 'Traces a MemberExpression and returns the appropriate object' traverser._debug('TESTING>>%s' % node['type']) if node['type'] == 'MemberExpression': # x.y or x[y] # x = base base = trace_member(traverser, node['object'], instantiate) base = _expand_globals(traverser, base) identifier = _get_member_exp_property(traverser, node) # Handle the various global entity properties. if base.is_global: # If we've got an XPCOM wildcard, return a copy of the entity. if 'xpcom_wildcard' in base.value: traverser._debug('MEMBER_EXP>>XPCOM_WILDCARD') from predefinedentities import CONTRACT_ENTITIES if identifier in CONTRACT_ENTITIES: kw = dict(err_id=('js', 'actions', 'dangerous_contract'), warning='Dangerous XPCOM contract ID') kw.update(CONTRACT_ENTITIES[identifier]) traverser.warning(**kw) base.value = base.value.copy() del base.value['xpcom_wildcard'] return base test_identifier(traverser, identifier) traverser._debug('MEMBER_EXP>>PROPERTY: %s' % identifier) output = base.get(traverser=traverser, instantiate=instantiate, name=identifier) output.context = base.context if base.is_global: # In the cases of XPCOM objects, methods generally # remain bound to their parent objects, even when called # indirectly. output.parent = base return output elif node['type'] == 'Identifier': traverser._debug('MEMBER_EXP>>ROOT:IDENTIFIER') test_identifier(traverser, node['name']) # If we're supposed to instantiate the object and it doesn't already # exist, instantitate the object. if instantiate and not traverser._is_defined(node['name']): output = JSWrapper(JSObject(), traverser=traverser) traverser.contexts[0].set(node['name'], output) else: output = traverser._seek_variable(node['name']) return _expand_globals(traverser, output) else: traverser._debug('MEMBER_EXP>>ROOT:EXPRESSION') # It's an expression, so just try your damndest. return traverser._traverse_node(node)
def VariableDeclaration(traverser, node): traverser._debug("VARIABLE_DECLARATION") traverser.debug_level += 1 for declaration in node["declarations"]: # It could be deconstruction of variables :( if declaration["id"]["type"] == "ArrayPattern": vars = [] for element in declaration["id"]["elements"]: # NOTE : Multi-level array destructuring sucks. Maybe implement # it someday if you're bored, but it's so rarely used and it's # so utterly complex, there's probably no need to ever code it # up. if element is None or element["type"] != "Identifier": vars.append(None) continue vars.append(element["name"]) # The variables are not initialized if declaration["init"] is None: # Simple instantiation; no initialization for var in filter(None, vars): traverser._declare_variable(var, JSObject(traverser=traverser)) # The variables are declared inline elif declaration["init"]["type"] == "ArrayPattern": # TODO : Test to make sure len(values) == len(vars) for value in declaration["init"]["elements"]: if vars[0]: traverser._declare_variable( vars[0], traverser.traverse_node(value)) vars = vars[1:] # Pop off the first value # It's being assigned by a JSArray (presumably) elif declaration["init"]["type"] == "ArrayExpression": assigner = traverser.traverse_node(declaration["init"]) for value, var in zip(assigner.elements, vars): traverser._declare_variable(var, value) elif declaration["id"]["type"] == "ObjectPattern": init = traverser.traverse_node(declaration["init"]) def _proc_objpattern(init_obj, properties): for prop in properties: # Get the name of the init obj's member if prop["key"]["type"] == "Literal": prop_name = prop["key"]["value"] elif prop["key"]["type"] == "Identifier": prop_name = prop["key"]["name"] else: continue if prop["value"]["type"] == "Identifier": traverser._declare_variable( prop["value"]["name"], init_obj.get(traverser, prop_name)) elif prop["value"]["type"] == "ObjectPattern": _proc_objpattern(init_obj.get(traverser, prop_name), prop["value"]["properties"]) if init is not None: _proc_objpattern(init_obj=init, properties=declaration["id"]["properties"]) else: var_name = declaration["id"]["name"] traverser._debug("NAME>>%s" % var_name) traverser._debug("TYPE>>%s" % node["kind"]) var = traverser.traverse_node(declaration["init"]) var.const = node["kind"] == "const" traverser._declare_variable(var_name, var, type_=node["kind"]) traverser.debug_level -= 1 # The "Declarations" branch contains custom elements. return True
def _call_expression(traverser, node): args = node['arguments'] for arg in args: traverser._traverse_node(arg, source='arguments') member = traverser._traverse_node(node['callee']) if (traverser.filename.startswith('defaults/preferences/') and ('name' not in node['callee'] or node['callee']['name'] not in (u'pref', u'user_pref'))): traverser.err.warning( err_id=('testcases_javascript_actions', '_call_expression', 'complex_prefs_defaults_code'), warning='Complex code should not appear in preference defaults ' 'files', description="Calls to functions other than 'pref' and 'user_pref' " 'should not appear in defaults/preferences/ files.', filename=traverser.filename, line=traverser.line, column=traverser.position, context=traverser.context) if member.is_global: if callable(member.value.get('dangerous', None)): result = member.value['dangerous'](a=args, t=traverser._traverse_node, e=traverser.err) name = member.value.get('name', '') if result and name: kwargs = { 'err_id': ('testcases_javascript_actions', '_call_expression', 'called_dangerous_global'), 'warning': '`%s` called in potentially dangerous manner' % member.value['name'], 'description': 'The global `%s` function was called using a set ' 'of dangerous parameters. Calls of this nature ' 'are deprecated.' % member.value['name'] } if isinstance(result, DESCRIPTION_TYPES): kwargs['description'] = result elif isinstance(result, dict): kwargs.update(result) traverser.warning(**kwargs) if callable(member.value.get('forbidden', None)): result = member.value['forbidden'](a=args, t=traverser._traverse_node, e=traverser.err) name = member.value.get('name', '') if result and name: kwargs = { 'err_id': ('testcases_javascript_actions', '_call_expression', 'called_forbidden_global'), 'warning': ' Forbidden `%s` called' % member.value['name'], 'description': 'The forbidden global `%s` function was called.' % member.value['name'] } if isinstance(result, DESCRIPTION_TYPES): kwargs['description'] = result elif isinstance(result, dict): kwargs.update(result) traverser.error(**kwargs) elif (node['callee']['type'] == 'MemberExpression' and node['callee']['property']['type'] == 'Identifier'): # If we can identify the function being called on any member of any # instance, we can use that to either generate an output value or test # for additional conditions. identifier_name = node['callee']['property']['name'] if identifier_name in instanceactions.INSTANCE_DEFINITIONS: result = instanceactions.INSTANCE_DEFINITIONS[identifier_name]( args, traverser, node, wrapper=member) return result if member.is_global and 'return' in member.value: if 'object' in node['callee']: member.parent = trace_member(traverser, node['callee']['object']) return member.value['return'](wrapper=member, arguments=args, traverser=traverser) return JSWrapper(JSObject(), dirty=True, traverser=traverser)
def AssignmentExpression(traverser, node): traverser._debug("ASSIGNMENT_EXPRESSION") traverser.debug_level += 1 traverser._debug("ASSIGNMENT>>PARSING RIGHT") right = traverser.traverse_node(node["right"]) traverser._debug("ASSIGNMENT>>PARSING LEFT") orig_left = left = traverser.traverse_node(node["left"]) operator = node["operator"] def set_lvalue(value): node_left = node["left"] traverser._debug("ASSIGNING:DIRECT(%s)" % node_left["type"]) global_overwrite = False readonly_value = True if node_left["type"] == "Identifier": # Identifiers just need the ID name and a value to push. # Raise a global overwrite issue if the identifier is global. global_overwrite = traverser._is_global(node_left["name"]) # Get the readonly attribute and store its value if is_global if global_overwrite: from predefinedentities import GLOBAL_ENTITIES global_dict = GLOBAL_ENTITIES[node_left["name"]] readonly_value = global_dict.get("readonly", False) traverser._declare_variable(node_left["name"], value, type_="glob") elif node_left["type"] == "MemberExpression": member_object = MemberExpression(traverser, node_left["object"], instantiate=True) member_property = _get_member_exp_property(traverser, node_left) traverser._debug("ASSIGNMENT:MEMBER_PROPERTY(%s)" % member_property) if member_object is None: member_object = JSObject() member_object.set(member_property, value, traverser) # Treat direct assignment different than augmented assignment. if operator == "=": set_lvalue(right) return right elif operator not in ASSIGNMENT_OPERATORS: # We don't support that operator. (yet?) traverser._debug("ASSIGNMENT>>OPERATOR NOT FOUND", 1) return left if left.const: traverser.err.warning( err_id=("js", "AssignmentExpression", "aug_const_overwrite"), warning="Overwritten constant value", description="A variable declared as constant has been " "overwritten in some JS code by an augmented " "assignment.", filename=traverser.filename, line=traverser.line, column=traverser.position, context=traverser.context) return JSObject(traverser=traverser) traverser._debug("ASSIGNMENT>>DONE PARSING LEFT") traverser.debug_level -= 1 # If we're modifying a non-numeric type with a numeric operator, return # NaN. if (operator in NUMERIC_OPERATORS and not isinstance( left.get_literal_value(traverser) or 0, NUMERIC_TYPES)): set_lvalue(utils.get_NaN(traverser)) return left gleft, gright = utils.get_as_num(left), utils.get_as_num(right) traverser._debug("ASSIGNMENT>>OPERATION:%s" % operator) if operator in ("<<=", ">>=", ">>>=") and gright < 0: # The user is doing weird bitshifting that will return 0 in JS but # not in Python. set_lvalue(JSLiteral(0, traverser=traverser)) return left elif (operator in ("<<=", ">>=", ">>>=", "|=", "^=", "&=") and (abs(gleft) == float('inf') or abs(gright) == float('inf'))): # Don't bother handling infinity for integer-converted operations. set_lvalue(utils.get_NaN(traverser)) return left if operator == '+=': lit_left = left.get_literal_value(traverser) lit_right = right.get_literal_value(traverser) # Don't perform an operation on None. Python freaks out. if lit_left is None: lit_left = 0 if lit_right is None: lit_right = 0 # If either side of the assignment operator is a string, both sides # need to be cast to strings first. if (isinstance(lit_left, types.StringTypes) or isinstance(lit_right, types.StringTypes)): left = utils.get_as_str(lit_left) right = utils.get_as_str(lit_right) else: left, right = lit_left, lit_right output = ASSIGNMENT_OPERATORS[operator](left, right, gleft, gright) # Cap the length of analyzed strings. if isinstance(output, types.StringTypes) and len(output) > MAX_STR_SIZE: output = output[:MAX_STR_SIZE] traverser._debug("ASSIGNMENT::New value >> %s" % output, 1) set_lvalue(JSLiteral(output, traverser=traverser)) return orig_left